From 9fc1ea65a5a40bb9a32c88e8a49306d7396f9842 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Domeradzki?= Date: Sat, 22 Aug 2020 21:41:01 +0200 Subject: [PATCH] Closes #1543 (#1944) * Start working on nullable checks help me * Update GlobalConfig.cs * Finish initial fixup round * nullability code review --- .../CatAPI.cs | 16 +- .../CatController.cs | 7 +- .../ExamplePlugin.cs | 17 +- .../PeriodicGCPlugin.cs | 2 +- .../GlobalCache.cs | 26 +- .../RequestData.cs | 6 +- .../ResponseData.cs | 2 +- .../SteamTokenDumperPlugin.cs | 36 +- ArchiSteamFarm/ASF.cs | 187 ++- ArchiSteamFarm/Actions.cs | 71 +- ArchiSteamFarm/ArchiCryptoHelper.cs | 98 +- ArchiSteamFarm/ArchiHandler.cs | 127 +- ArchiSteamFarm/ArchiWebHandler.cs | 570 ++++----- ArchiSteamFarm/Bot.cs | 482 +++----- ArchiSteamFarm/BotConfig.cs | 53 +- ArchiSteamFarm/BotDatabase.cs | 68 +- ArchiSteamFarm/CardsFarmer.cs | 116 +- .../Collections/ConcurrentEnumerator.cs | 5 +- .../Collections/ConcurrentHashSet.cs | 19 +- ArchiSteamFarm/Collections/ConcurrentList.cs | 7 +- ArchiSteamFarm/Commands.cs | 1089 +++++++---------- ArchiSteamFarm/Debugging.cs | 7 +- ArchiSteamFarm/Events.cs | 4 +- ArchiSteamFarm/GitHub.cs | 71 +- ArchiSteamFarm/GlobalConfig.cs | 75 +- ArchiSteamFarm/GlobalDatabase.cs | 37 +- ArchiSteamFarm/Helpers/ArchiCacheable.cs | 16 +- .../Helpers/CrossProcessFileBasedSemaphore.cs | 7 +- ArchiSteamFarm/Helpers/SemaphoreLock.cs | 3 +- ArchiSteamFarm/Helpers/SerializableFile.cs | 8 +- ArchiSteamFarm/IPC/ArchiKestrel.cs | 14 +- .../IPC/Controllers/Api/ASFController.cs | 20 +- .../IPC/Controllers/Api/BotController.cs | 126 +- .../IPC/Controllers/Api/CommandController.cs | 19 +- .../IPC/Controllers/Api/NLogController.cs | 21 +- .../Controllers/Api/StructureController.cs | 8 +- .../IPC/Controllers/Api/TypeController.cs | 44 +- .../IPC/Controllers/Api/WWWController.cs | 37 +- .../ApiAuthenticationMiddleware.cs | 22 +- .../IPC/Integration/EnumSchemaFilter.cs | 20 +- ArchiSteamFarm/IPC/Requests/ASFRequest.cs | 2 +- .../BotGamesToRedeemInBackgroundRequest.cs | 2 +- .../IPC/Requests/BotInputRequest.cs | 2 +- .../IPC/Requests/BotRedeemRequest.cs | 2 +- .../IPC/Requests/BotRenameRequest.cs | 2 +- ArchiSteamFarm/IPC/Requests/BotRequest.cs | 2 +- ArchiSteamFarm/IPC/Requests/CommandRequest.cs | 4 +- ...actorAuthenticationConfirmationsRequest.cs | 2 +- ArchiSteamFarm/IPC/Requests/WWWSendRequest.cs | 2 +- ArchiSteamFarm/IPC/Responses/ASFResponse.cs | 3 +- .../GamesToRedeemInBackgroundResponse.cs | 6 +- .../IPC/Responses/GenericResponse.cs | 18 +- .../IPC/Responses/GitHubReleaseResponse.cs | 7 +- ArchiSteamFarm/IPC/Responses/TypeResponse.cs | 11 +- ArchiSteamFarm/IPC/Startup.cs | 21 +- ArchiSteamFarm/IPC/WebUtilities.cs | 20 +- ArchiSteamFarm/Json/Steam.cs | 39 +- ArchiSteamFarm/MobileAuthenticator.cs | 101 +- ArchiSteamFarm/NLog/ArchiLogger.cs | 66 +- ArchiSteamFarm/NLog/HistoryTarget.cs | 4 +- ArchiSteamFarm/NLog/Logging.cs | 34 +- ArchiSteamFarm/NLog/SteamTarget.cs | 23 +- ArchiSteamFarm/OS.cs | 25 +- ArchiSteamFarm/Plugins/IASF.cs | 2 +- ArchiSteamFarm/Plugins/IBot.cs | 4 +- ArchiSteamFarm/Plugins/IBotCardsFarmerInfo.cs | 6 +- ArchiSteamFarm/Plugins/IBotCommand.cs | 4 +- ArchiSteamFarm/Plugins/IBotConnection.cs | 4 +- ArchiSteamFarm/Plugins/IBotFriendRequest.cs | 3 +- ArchiSteamFarm/Plugins/IBotMessage.cs | 4 +- ArchiSteamFarm/Plugins/IBotModules.cs | 2 +- ArchiSteamFarm/Plugins/IBotSteamClient.cs | 7 +- ArchiSteamFarm/Plugins/IBotTradeOffer.cs | 3 +- .../Plugins/IBotTradeOfferResults.cs | 2 +- .../Plugins/IBotUserNotifications.cs | 2 +- ArchiSteamFarm/Plugins/IBotsComparer.cs | 1 - ArchiSteamFarm/Plugins/IPlugin.cs | 2 - ArchiSteamFarm/Plugins/ISteamPICSChanges.cs | 3 +- ArchiSteamFarm/Plugins/PluginsCore.cs | 112 +- ArchiSteamFarm/Program.cs | 99 +- ArchiSteamFarm/RuntimeCompatibility.cs | 32 +- ArchiSteamFarm/SharedInfo.cs | 29 +- ArchiSteamFarm/Statistics.cs | 53 +- .../SteamKit2/InMemoryServerListProvider.cs | 7 +- .../SteamKit2/ServerRecordEndPoint.cs | 9 +- ArchiSteamFarm/SteamPICSChanges.cs | 8 +- ArchiSteamFarm/SteamSaleEvent.cs | 11 +- ArchiSteamFarm/Trading.cs | 98 +- ArchiSteamFarm/Utilities.cs | 95 +- ArchiSteamFarm/WebBrowser.cs | 240 ++-- Directory.Build.props | 1 + 91 files changed, 1996 insertions(+), 2808 deletions(-) diff --git a/ArchiSteamFarm.CustomPlugins.ExamplePlugin/CatAPI.cs b/ArchiSteamFarm.CustomPlugins.ExamplePlugin/CatAPI.cs index cba76a3f6..167a73fd6 100644 --- a/ArchiSteamFarm.CustomPlugins.ExamplePlugin/CatAPI.cs +++ b/ArchiSteamFarm.CustomPlugins.ExamplePlugin/CatAPI.cs @@ -22,34 +22,30 @@ using System; using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; -using JetBrains.Annotations; using Newtonsoft.Json; namespace ArchiSteamFarm.CustomPlugins.ExamplePlugin { // This is example class that shows how you can call third-party services within your plugin - // You always wanted from your ASF to post cats, right? Now is your chance! - // P.S. The code is almost 1:1 copy from the one I use in ArchiBoT, you can thank me later + // You've always wanted from your ASF to post cats, right? Now is your chance! + // P.S. The code is almost 1:1 copy from the one I use in ArchiBot, you can thank me later internal static class CatAPI { private const string URL = "https://aws.random.cat"; - [ItemCanBeNull] - internal static async Task GetRandomCatURL([JetBrains.Annotations.NotNull] WebBrowser webBrowser) { + internal static async Task GetRandomCatURL(WebBrowser webBrowser) { if (webBrowser == null) { throw new ArgumentNullException(nameof(webBrowser)); } const string request = URL + "/meow"; - WebBrowser.ObjectResponse response = await webBrowser.UrlGetToJsonObject(request).ConfigureAwait(false); + WebBrowser.ObjectResponse? response = await webBrowser.UrlGetToJsonObject(request).ConfigureAwait(false); if (response?.Content == null) { return null; } if (string.IsNullOrEmpty(response.Content.Link)) { - ASF.ArchiLogger.LogNullError(nameof(response.Content.Link)); - - return null; + throw new ArgumentNullException(nameof(response.Content.Link)); } return Uri.EscapeUriString(response.Content.Link); @@ -59,7 +55,7 @@ namespace ArchiSteamFarm.CustomPlugins.ExamplePlugin { private sealed class MeowResponse { #pragma warning disable 649 [JsonProperty(PropertyName = "file", Required = Required.Always)] - internal readonly string Link; + internal readonly string? Link; #pragma warning restore 649 [JsonConstructor] diff --git a/ArchiSteamFarm.CustomPlugins.ExamplePlugin/CatController.cs b/ArchiSteamFarm.CustomPlugins.ExamplePlugin/CatController.cs index 2f66fd2a1..da3c842ff 100644 --- a/ArchiSteamFarm.CustomPlugins.ExamplePlugin/CatController.cs +++ b/ArchiSteamFarm.CustomPlugins.ExamplePlugin/CatController.cs @@ -19,6 +19,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +using System; using System.Net; using System.Threading.Tasks; using ArchiSteamFarm.IPC.Controllers.Api; @@ -38,7 +39,11 @@ namespace ArchiSteamFarm.CustomPlugins.ExamplePlugin { [ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.OK)] [ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.ServiceUnavailable)] public async Task> CatGet() { - string link = await CatAPI.GetRandomCatURL(ASF.WebBrowser).ConfigureAwait(false); + if (ASF.WebBrowser == null) { + throw new ArgumentNullException(nameof(ASF.WebBrowser)); + } + + string? link = await CatAPI.GetRandomCatURL(ASF.WebBrowser).ConfigureAwait(false); return !string.IsNullOrEmpty(link) ? Ok(new GenericResponse(link)) : StatusCode((int) HttpStatusCode.ServiceUnavailable, new GenericResponse(false)); } diff --git a/ArchiSteamFarm.CustomPlugins.ExamplePlugin/ExamplePlugin.cs b/ArchiSteamFarm.CustomPlugins.ExamplePlugin/ExamplePlugin.cs index 79c03e48e..13d28fc8d 100644 --- a/ArchiSteamFarm.CustomPlugins.ExamplePlugin/ExamplePlugin.cs +++ b/ArchiSteamFarm.CustomPlugins.ExamplePlugin/ExamplePlugin.cs @@ -55,7 +55,7 @@ namespace ArchiSteamFarm.CustomPlugins.ExamplePlugin { // Thanks to that, you can extend default ASF config with your own stuff, then parse it here in order to customize your plugin during runtime // Keep in mind that, as noted in the interface, additionalConfigProperties can be null if no custom, unrecognized properties are found by ASF, you should handle that case appropriately // In addition to that, this method also guarantees that all plugins were already OnLoaded(), which allows cross-plugins-communication to be possible - public void OnASFInit(IReadOnlyDictionary additionalConfigProperties = null) { + public void OnASFInit(IReadOnlyDictionary? additionalConfigProperties = null) { if (additionalConfigProperties == null) { return; } @@ -80,14 +80,14 @@ namespace ArchiSteamFarm.CustomPlugins.ExamplePlugin { // Since ASF already had to do initial parsing in order to determine that the command is unknown, args[] are splitted using standard ASF delimiters // If by any chance you want to handle message in its raw format, you also have it available, although for usual ASF pattern you can most likely stick with args[] exclusively. The message has CommandPrefix already stripped for your convenience // If you do not recognize the command, just return null/empty and allow ASF to gracefully return "unknown command" to user on usual basis - public async Task OnBotCommand(Bot bot, ulong steamID, string message, string[] args) { + public async Task OnBotCommand(Bot bot, ulong steamID, string message, string[] args) { // 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): // 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); + string? randomCatURL = await CatAPI.GetRandomCatURL(bot.ArchiWebHandler.WebBrowser).ConfigureAwait(false); return !string.IsNullOrEmpty(randomCatURL) ? randomCatURL : "God damn it, we're out of cats, care to notify my master? Thanks!"; default: @@ -126,7 +126,7 @@ namespace ArchiSteamFarm.CustomPlugins.ExamplePlugin { // Keep in mind that, as noted in the interface, additionalConfigProperties can be null if no custom, unrecognized properties are found by ASF, you should handle that case appropriately // Also keep in mind that this function can be called multiple times, e.g. when user edits his bot configs during runtime // Take a look at OnASFInit() for example parsing code - public async void OnBotInitModules(Bot bot, IReadOnlyDictionary additionalConfigProperties = null) { + public async void OnBotInitModules(Bot bot, IReadOnlyDictionary? additionalConfigProperties = null) { // ASF marked this message as synchronous, in case we have async code to execute, we can just use async void return // For example, we'll ensure that every bot starts paused regardless of Paused property, in order to do this, we'll just call Pause here in InitModules() // Thanks to the fact that this method is called with each bot config reload, we'll ensure that our bot stays paused even if it'd get unpaused otherwise @@ -143,19 +143,22 @@ namespace ArchiSteamFarm.CustomPlugins.ExamplePlugin { // Keep in mind that there is no guarantee what is the actual access of steamID, so you should do the appropriate access checking yourself // You can use either ASF's default functions for that, or implement your own logic as you please // If you do not intend to return any response to user, just return null/empty and ASF will proceed with the silence as usual - public Task OnBotMessage(Bot bot, ulong steamID, string message) { + public Task OnBotMessage(Bot bot, ulong steamID, string message) { // 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)); + } // 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 if (Bot.BotsReadOnly.Values.Any(existingBot => existingBot.SteamID == steamID)) { - return Task.FromResult(null); + return Task.FromResult(null); } // If this message doesn't come from one of our bots, we can reply to the user in some pre-defined way bot.ArchiLogger.LogGenericTrace("Hey boss, we got some unknown message here!"); - return Task.FromResult("I didn't get that, did you mean to use a command?"); + return Task.FromResult((string?) "I didn't get that, did you mean to use a command?"); } // This method is called when bot receives a trade offer that ASF isn't willing to accept (ignored and rejected trades) diff --git a/ArchiSteamFarm.CustomPlugins.PeriodicGC/PeriodicGCPlugin.cs b/ArchiSteamFarm.CustomPlugins.PeriodicGC/PeriodicGCPlugin.cs index 23d067b36..75536ce7f 100644 --- a/ArchiSteamFarm.CustomPlugins.PeriodicGC/PeriodicGCPlugin.cs +++ b/ArchiSteamFarm.CustomPlugins.PeriodicGC/PeriodicGCPlugin.cs @@ -48,7 +48,7 @@ namespace ArchiSteamFarm.CustomPlugins.PeriodicGC { } } - private static void PerformGC(object state) { + private static void PerformGC(object? state) { ASF.ArchiLogger.LogGenericWarning("Performing GC, current memory: " + (GC.GetTotalMemory(false) / 1024) + " KB."); lock (PeriodicGCTimer) { diff --git a/ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/GlobalCache.cs b/ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/GlobalCache.cs index 13793da24..7351d7e7a 100644 --- a/ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/GlobalCache.cs +++ b/ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/GlobalCache.cs @@ -27,13 +27,11 @@ using System.Linq; using System.Threading.Tasks; using ArchiSteamFarm.Collections; using ArchiSteamFarm.Helpers; -using JetBrains.Annotations; using Newtonsoft.Json; using SteamKit2; namespace ArchiSteamFarm.OfficialPlugins.SteamTokenDumper { internal sealed class GlobalCache : SerializableFile { - [NotNull] private static string SharedFilePath => Path.Combine(ArchiSteamFarm.SharedInfo.ConfigDirectory, nameof(SteamTokenDumper) + ".cache"); [JsonProperty(Required = Required.DisallowNull)] @@ -64,22 +62,16 @@ namespace ArchiSteamFarm.OfficialPlugins.SteamTokenDumper { internal ulong GetAppToken(uint appID) => AppTokens[appID]; - [NotNull] internal Dictionary GetAppTokensForSubmission() => AppTokens.Where(appToken => !SubmittedAppIDs.Contains(appToken.Key)).ToDictionary(appToken => appToken.Key, appToken => appToken.Value); - - [NotNull] internal Dictionary GetDepotKeysForSubmission() => DepotKeys.Where(depotKey => !SubmittedDepotIDs.Contains(depotKey.Key)).ToDictionary(depotKey => depotKey.Key, depotKey => depotKey.Value); - - [NotNull] internal Dictionary GetPackageTokensForSubmission() => PackageTokens.Where(packageToken => !SubmittedPackageIDs.Contains(packageToken.Key)).ToDictionary(packageToken => packageToken.Key, packageToken => packageToken.Value); - [ItemNotNull] internal static async Task Load() { if (!File.Exists(SharedFilePath)) { return new GlobalCache(); } - GlobalCache globalCache = null; + GlobalCache? globalCache = null; try { string json = await RuntimeCompatibility.File.ReadAllTextAsync(SharedFilePath).ConfigureAwait(false); @@ -100,7 +92,7 @@ namespace ArchiSteamFarm.OfficialPlugins.SteamTokenDumper { return globalCache; } - internal async Task OnPICSChanges(uint currentChangeNumber, [NotNull] IReadOnlyCollection> appChanges) { + internal async Task OnPICSChanges(uint currentChangeNumber, IReadOnlyCollection> appChanges) { if ((currentChangeNumber == 0) || (appChanges == null)) { throw new ArgumentNullException(nameof(appChanges)); } @@ -127,7 +119,7 @@ namespace ArchiSteamFarm.OfficialPlugins.SteamTokenDumper { internal async Task OnPICSChangesRestart(uint currentChangeNumber) { if (currentChangeNumber == 0) { - throw new ArgumentNullException(); + throw new ArgumentNullException(nameof(currentChangeNumber)); } if (currentChangeNumber <= LastChangeNumber) { @@ -146,7 +138,7 @@ namespace ArchiSteamFarm.OfficialPlugins.SteamTokenDumper { internal bool ShouldRefreshAppInfo(uint appID) => !AppChangeNumbers.ContainsKey(appID); internal bool ShouldRefreshDepotKey(uint depotID) => !DepotKeys.ContainsKey(depotID); - internal async Task UpdateAppChangeNumbers([NotNull] IReadOnlyCollection> appChangeNumbers) { + internal async Task UpdateAppChangeNumbers(IReadOnlyCollection> appChangeNumbers) { if (appChangeNumbers == null) { throw new ArgumentNullException(nameof(appChangeNumbers)); } @@ -167,7 +159,7 @@ namespace ArchiSteamFarm.OfficialPlugins.SteamTokenDumper { } } - internal async Task UpdateAppTokens([NotNull] IReadOnlyCollection> appTokens, [NotNull] IReadOnlyCollection publicAppIDs) { + internal async Task UpdateAppTokens(IReadOnlyCollection> appTokens, IReadOnlyCollection publicAppIDs) { if ((appTokens == null) || (publicAppIDs == null)) { throw new ArgumentNullException(nameof(appTokens) + " || " + nameof(publicAppIDs)); } @@ -207,7 +199,7 @@ namespace ArchiSteamFarm.OfficialPlugins.SteamTokenDumper { } } - internal async Task UpdateDepotKeys([NotNull] ICollection depotKeyResults) { + internal async Task UpdateDepotKeys(ICollection depotKeyResults) { if (depotKeyResults == null) { throw new ArgumentNullException(nameof(depotKeyResults)); } @@ -221,7 +213,7 @@ namespace ArchiSteamFarm.OfficialPlugins.SteamTokenDumper { string depotKey = BitConverter.ToString(depotKeyResult.DepotKey).Replace("-", ""); - if (DepotKeys.TryGetValue(depotKeyResult.DepotID, out string previousDepotKey) && (previousDepotKey == depotKey)) { + if (DepotKeys.TryGetValue(depotKeyResult.DepotID, out string? previousDepotKey) && (previousDepotKey == depotKey)) { continue; } @@ -240,7 +232,7 @@ namespace ArchiSteamFarm.OfficialPlugins.SteamTokenDumper { } } - internal async Task UpdatePackageTokens([NotNull] IReadOnlyCollection> packageTokens) { + internal async Task UpdatePackageTokens(IReadOnlyCollection> packageTokens) { if (packageTokens == null) { throw new ArgumentNullException(nameof(packageTokens)); } @@ -267,7 +259,7 @@ namespace ArchiSteamFarm.OfficialPlugins.SteamTokenDumper { } } - internal async Task UpdateSubmittedData([NotNull] IReadOnlyCollection appIDs, [NotNull] IReadOnlyCollection packageIDs, [NotNull] IReadOnlyCollection depotIDs) { + 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)); } diff --git a/ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/RequestData.cs b/ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/RequestData.cs index a005aa7c5..84c96038f 100644 --- a/ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/RequestData.cs +++ b/ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/RequestData.cs @@ -22,7 +22,6 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; -using JetBrains.Annotations; using Newtonsoft.Json; using SteamKit2; @@ -40,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"); + private readonly string Guid = ASF.GlobalDatabase?.Guid.ToString("N") ?? throw new ArgumentNullException(nameof(ASF.GlobalDatabase.Guid)); #pragma warning restore IDE0052 private readonly ulong SteamID; @@ -62,11 +61,10 @@ namespace ArchiSteamFarm.OfficialPlugins.SteamTokenDumper { #pragma warning disable IDE0051 [JsonProperty(PropertyName = "steamid", Required = Required.Always)] - [NotNull] private string SteamIDText => new SteamID(SteamID).Render(); #pragma warning restore IDE0051 - internal RequestData(ulong steamID, [NotNull] IEnumerable> apps, [NotNull] IEnumerable> accessTokens, [NotNull] IEnumerable> depots) { + 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)); } diff --git a/ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/ResponseData.cs b/ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/ResponseData.cs index fc642d51c..011512277 100644 --- a/ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/ResponseData.cs +++ b/ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/ResponseData.cs @@ -27,7 +27,7 @@ namespace ArchiSteamFarm.OfficialPlugins.SteamTokenDumper { internal sealed class ResponseData { #pragma warning disable 649 [JsonProperty(PropertyName = "data", Required = Required.Always)] - internal readonly InternalData Data; + internal readonly InternalData? Data; #pragma warning restore 649 #pragma warning disable 649 diff --git a/ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/SteamTokenDumperPlugin.cs b/ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/SteamTokenDumperPlugin.cs index b46bc7ebc..a23062323 100644 --- a/ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/SteamTokenDumperPlugin.cs +++ b/ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/SteamTokenDumperPlugin.cs @@ -29,21 +29,19 @@ using System.Threading; using System.Threading.Tasks; using ArchiSteamFarm.Localization; using ArchiSteamFarm.Plugins; -using JetBrains.Annotations; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using SteamKit2; namespace ArchiSteamFarm.OfficialPlugins.SteamTokenDumper { [Export(typeof(IPlugin))] - [UsedImplicitly] 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 GlobalCache GlobalCache; + private static GlobalCache? GlobalCache; [JsonProperty] private static bool IsEnabled; @@ -56,7 +54,7 @@ namespace ArchiSteamFarm.OfficialPlugins.SteamTokenDumper { public Task GetPreferredChangeNumberToStartFrom() => Task.FromResult(IsEnabled ? GlobalCache?.LastChangeNumber ?? 0 : 0); - public void OnASFInit(IReadOnlyDictionary additionalConfigProperties = null) { + public void OnASFInit(IReadOnlyDictionary? additionalConfigProperties = null) { if (!SharedInfo.HasValidToken) { ASF.ArchiLogger.LogGenericError($"{Name} has been disabled due to missing build token."); @@ -103,7 +101,7 @@ namespace ArchiSteamFarm.OfficialPlugins.SteamTokenDumper { throw new ArgumentNullException(nameof(bot)); } - if (BotSubscriptions.TryRemove(bot, out IDisposable subscription)) { + if (BotSubscriptions.TryRemove(bot, out IDisposable? subscription)) { subscription.Dispose(); } @@ -138,7 +136,7 @@ namespace ArchiSteamFarm.OfficialPlugins.SteamTokenDumper { throw new ArgumentNullException(nameof(bot) + " || " + nameof(callbackManager)); } - if (BotSubscriptions.TryRemove(bot, out IDisposable subscription)) { + if (BotSubscriptions.TryRemove(bot, out IDisposable? subscription)) { subscription.Dispose(); } @@ -153,7 +151,7 @@ namespace ArchiSteamFarm.OfficialPlugins.SteamTokenDumper { } } - public IReadOnlyCollection OnBotSteamHandlersInit(Bot bot) => null; + public IReadOnlyCollection? OnBotSteamHandlersInit(Bot bot) => null; public override void OnLoaded() { } @@ -189,7 +187,7 @@ namespace ArchiSteamFarm.OfficialPlugins.SteamTokenDumper { await GlobalCache.OnPICSChangesRestart(currentChangeNumber).ConfigureAwait(false); } - private static async void OnLicenseList([NotNull] Bot bot, [NotNull] SteamApps.LicenseListCallback callback) { + private static async void OnLicenseList(Bot bot, SteamApps.LicenseListCallback callback) { if ((bot == null) || (callback == null)) { throw new ArgumentNullException(nameof(callback)); } @@ -208,7 +206,7 @@ namespace ArchiSteamFarm.OfficialPlugins.SteamTokenDumper { await Refresh(bot, packageTokens.Keys).ConfigureAwait(false); } - private static async Task Refresh([NotNull] Bot bot, IReadOnlyCollection packageIDs = null) { + private static async Task Refresh(Bot bot, IReadOnlyCollection? packageIDs = null) { if (bot == null) { throw new ArgumentNullException(nameof(bot)); } @@ -217,8 +215,8 @@ namespace ArchiSteamFarm.OfficialPlugins.SteamTokenDumper { return; } - if (GlobalCache == null) { - throw new ArgumentNullException(nameof(GlobalCache)); + if ((GlobalCache == null) || (ASF.GlobalDatabase == null)) { + throw new ArgumentNullException(nameof(GlobalCache) + " || " + nameof(ASF.GlobalDatabase)); } if (!BotSynchronizations.TryGetValue(bot, out (SemaphoreSlim RefreshSemaphore, Timer RefreshTimer) synchronization)) { @@ -239,7 +237,7 @@ namespace ArchiSteamFarm.OfficialPlugins.SteamTokenDumper { HashSet appIDsToRefresh = new HashSet(); foreach (uint packageID in packageIDs) { - if (!ASF.GlobalDatabase.PackagesDataReadOnly.TryGetValue(packageID, out (uint ChangeNumber, HashSet AppIDs) packageData) || (packageData.AppIDs == null)) { + if (!ASF.GlobalDatabase.PackagesDataReadOnly.TryGetValue(packageID, out (uint ChangeNumber, HashSet? AppIDs) packageData) || (packageData.AppIDs == null)) { // ASF might not have the package info for us at the moment, we'll retry later continue; } @@ -388,14 +386,18 @@ namespace ArchiSteamFarm.OfficialPlugins.SteamTokenDumper { } private static async Task SubmitData() { + if (Bot.Bots == null) { + throw new ArgumentNullException(nameof(Bot.Bots)); + } + const string request = SharedInfo.ServerURL + "/submit"; if (!IsEnabled) { return; } - if (GlobalCache == null) { - throw new ArgumentNullException(nameof(GlobalCache)); + if ((ASF.GlobalConfig == null) || (ASF.WebBrowser == null) || (GlobalCache == null)) { + throw new ArgumentNullException(nameof(ASF.GlobalConfig) + " || " + nameof(ASF.WebBrowser) + " || " + nameof(GlobalCache)); } if (!await SubmissionSemaphore.WaitAsync(0).ConfigureAwait(false)) { @@ -415,7 +417,7 @@ namespace ArchiSteamFarm.OfficialPlugins.SteamTokenDumper { return; } - ulong contributorSteamID = (ASF.GlobalConfig.SteamOwnerID > 0) && new SteamID(ASF.GlobalConfig.SteamOwnerID).IsIndividualAccount ? ASF.GlobalConfig.SteamOwnerID : Bot.BotsReadOnly.Values.Where(bot => bot.SteamID > 0).OrderByDescending(bot => bot.OwnedPackageIDsReadOnly.Count).FirstOrDefault()?.SteamID ?? 0; + ulong contributorSteamID = (ASF.GlobalConfig.SteamOwnerID > 0) && new SteamID(ASF.GlobalConfig.SteamOwnerID).IsIndividualAccount ? ASF.GlobalConfig.SteamOwnerID : Bot.Bots.Values.Where(bot => bot.SteamID > 0).OrderByDescending(bot => bot.OwnedPackageIDsReadOnly.Count).FirstOrDefault()?.SteamID ?? 0; if (contributorSteamID == 0) { ASF.ArchiLogger.LogGenericError($"Skipped {nameof(SubmitData)} trigger because there is no valid steamID we could classify as a contributor. Consider setting up {nameof(ASF.GlobalConfig.SteamOwnerID)} property."); @@ -427,9 +429,9 @@ namespace ArchiSteamFarm.OfficialPlugins.SteamTokenDumper { ASF.ArchiLogger.LogGenericInfo($"Submitting registered apps/subs/depots: {appTokens.Count}/{packageTokens.Count}/{depotKeys.Count}..."); - WebBrowser.ObjectResponse response = await ASF.WebBrowser.UrlPostToJsonObject(request, requestData, requestOptions: WebBrowser.ERequestOptions.ReturnClientErrors).ConfigureAwait(false); + WebBrowser.ObjectResponse? response = await ASF.WebBrowser.UrlPostToJsonObject(request, requestData, requestOptions: WebBrowser.ERequestOptions.ReturnClientErrors).ConfigureAwait(false); - if ((response?.Content == null) || response.StatusCode.IsClientErrorCode()) { + if ((response?.Content?.Data == null) || response.StatusCode.IsClientErrorCode()) { ASF.ArchiLogger.LogGenericWarning(Strings.WarningFailed); #if NETFRAMEWORK diff --git a/ArchiSteamFarm/ASF.cs b/ArchiSteamFarm/ASF.cs index b7dceb7df..2ea857632 100644 --- a/ArchiSteamFarm/ASF.cs +++ b/ArchiSteamFarm/ASF.cs @@ -51,58 +51,53 @@ namespace ArchiSteamFarm { public static byte LoadBalancingDelay => Math.Max(GlobalConfig?.LoginLimiterDelay ?? 0, GlobalConfig.DefaultLoginLimiterDelay); [PublicAPI] - public static GlobalConfig GlobalConfig { get; private set; } + public static GlobalConfig? GlobalConfig { get; private set; } [PublicAPI] - public static GlobalDatabase GlobalDatabase { get; private set; } + public static GlobalDatabase? GlobalDatabase { get; private set; } [PublicAPI] - public static WebBrowser WebBrowser { get; internal set; } + public static WebBrowser? WebBrowser { get; internal set; } - internal static ICrossProcessSemaphore ConfirmationsSemaphore { get; private set; } - internal static ICrossProcessSemaphore GiftsSemaphore { get; private set; } - internal static ICrossProcessSemaphore InventorySemaphore { get; private set; } - internal static ICrossProcessSemaphore LoginRateLimitingSemaphore { get; private set; } - internal static ICrossProcessSemaphore LoginSemaphore { get; private set; } - internal static ImmutableDictionary WebLimitingSemaphores { get; private set; } + internal static ICrossProcessSemaphore? ConfirmationsSemaphore { get; private set; } + internal static ICrossProcessSemaphore? GiftsSemaphore { get; private set; } + internal static ICrossProcessSemaphore? InventorySemaphore { get; private set; } + internal static ICrossProcessSemaphore? LoginRateLimitingSemaphore { get; private set; } + 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 Timer AutoUpdatesTimer; - private static FileSystemWatcher FileSystemWatcher; - private static ConcurrentDictionary LastWriteEvents; + private static Timer? AutoUpdatesTimer; + private static FileSystemWatcher? FileSystemWatcher; + private static ConcurrentDictionary? LastWriteEvents; [PublicAPI] public static bool IsOwner(ulong steamID) { if (steamID == 0) { - ArchiLogger.LogNullError(nameof(steamID)); - - return false; + throw new ArgumentNullException(nameof(steamID)); } - return (steamID == GlobalConfig.SteamOwnerID) || (Debugging.IsDebugBuild && (steamID == SharedInfo.ArchiSteamID)); + return (steamID == GlobalConfig?.SteamOwnerID) || (Debugging.IsDebugBuild && (steamID == SharedInfo.ArchiSteamID)); } internal static string GetFilePath(EFileType fileType) { if (!Enum.IsDefined(typeof(EFileType), fileType)) { - ArchiLogger.LogNullError(nameof(fileType)); - - return null; + throw new ArgumentNullException(nameof(fileType)); } - switch (fileType) { - case EFileType.Config: - return Path.Combine(SharedInfo.ConfigDirectory, SharedInfo.GlobalConfigFileName); - case EFileType.Database: - return Path.Combine(SharedInfo.ConfigDirectory, SharedInfo.GlobalDatabaseFileName); - default: - ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(fileType), fileType)); - - return null; - } + return fileType switch { + EFileType.Config => Path.Combine(SharedInfo.ConfigDirectory, SharedInfo.GlobalConfigFileName), + EFileType.Database => Path.Combine(SharedInfo.ConfigDirectory, SharedInfo.GlobalDatabaseFileName), + _ => throw new ArgumentOutOfRangeException(nameof(fileType)) + }; } internal static async Task Init() { + if (GlobalConfig == null) { + throw new ArgumentNullException(nameof(GlobalConfig)); + } + if (!PluginsCore.InitPlugins()) { await Task.Delay(10000).ConfigureAwait(false); } @@ -192,6 +187,10 @@ namespace ArchiSteamFarm { } internal static async Task RestartOrExit() { + if (GlobalConfig == null) { + throw new ArgumentNullException(nameof(GlobalConfig)); + } + if (Program.RestartAllowed && GlobalConfig.AutoRestart) { ArchiLogger.LogGenericInfo(Strings.Restarting); await Task.Delay(5000).ConfigureAwait(false); @@ -203,8 +202,11 @@ namespace ArchiSteamFarm { } } - [ItemCanBeNull] - internal static async Task Update(bool updateOverride = false) { + internal static async Task Update(bool updateOverride = false) { + if ((GlobalConfig == null) || (WebBrowser == null)) { + throw new ArgumentNullException(nameof(GlobalConfig) + " || " + nameof(WebBrowser)); + } + if (!SharedInfo.BuildInfo.CanUpdate || (GlobalConfig.UpdateChannel == GlobalConfig.EUpdateChannel.None)) { return null; } @@ -234,7 +236,7 @@ namespace ArchiSteamFarm { ArchiLogger.LogGenericInfo(Strings.UpdateCheckingNewVersion); - GitHub.ReleaseResponse releaseResponse = await GitHub.GetLatestRelease(GlobalConfig.UpdateChannel == GlobalConfig.EUpdateChannel.Stable).ConfigureAwait(false); + GitHub.ReleaseResponse? releaseResponse = await GitHub.GetLatestRelease(GlobalConfig.UpdateChannel == GlobalConfig.EUpdateChannel.Stable).ConfigureAwait(false); if (releaseResponse == null) { ArchiLogger.LogGenericWarning(Strings.ErrorUpdateCheckFailed); @@ -276,7 +278,7 @@ namespace ArchiSteamFarm { } string targetFile = SharedInfo.ASF + "-" + SharedInfo.BuildInfo.Variant + ".zip"; - GitHub.ReleaseResponse.Asset binaryAsset = releaseResponse.Assets.FirstOrDefault(asset => asset.Name.Equals(targetFile, StringComparison.OrdinalIgnoreCase)); + GitHub.ReleaseResponse.Asset? binaryAsset = releaseResponse.Assets.FirstOrDefault(asset => !string.IsNullOrEmpty(asset.Name) && asset.Name!.Equals(targetFile, StringComparison.OrdinalIgnoreCase)); if (binaryAsset == null) { ArchiLogger.LogGenericWarning(Strings.ErrorUpdateNoAssetForThisVersion); @@ -291,12 +293,12 @@ namespace ArchiSteamFarm { } if (!string.IsNullOrEmpty(releaseResponse.ChangelogPlainText)) { - ArchiLogger.LogGenericInfo(releaseResponse.ChangelogPlainText); + ArchiLogger.LogGenericInfo(releaseResponse.ChangelogPlainText!); } ArchiLogger.LogGenericInfo(string.Format(Strings.UpdateDownloadingNewVersion, newVersion, binaryAsset.Size / 1024 / 1024)); - WebBrowser.BinaryResponse response = await WebBrowser.UrlGetToBinaryWithProgress(binaryAsset.DownloadURL).ConfigureAwait(false); + WebBrowser.BinaryResponse? response = await WebBrowser.UrlGetToBinaryWithProgress(binaryAsset.DownloadURL!).ConfigureAwait(false); if (response?.Content == null) { return null; @@ -311,10 +313,11 @@ namespace ArchiSteamFarm { } try { -#if !NETFRAMEWORK - await +#if NETFRAMEWORK + using MemoryStream memoryStream = new MemoryStream(response.Content); +#else + await using MemoryStream memoryStream = new MemoryStream(response.Content); #endif - using MemoryStream memoryStream = new MemoryStream(response.Content); using ZipArchive zipArchive = new ZipArchive(memoryStream); @@ -344,10 +347,8 @@ namespace ArchiSteamFarm { } private static async Task CanHandleWriteEvent(string filePath) { - if (string.IsNullOrEmpty(filePath)) { - ArchiLogger.LogNullError(nameof(filePath)); - - return false; + if (string.IsNullOrEmpty(filePath) || (LastWriteEvents == null)) { + throw new ArgumentNullException(nameof(filePath) + " || " + nameof(LastWriteEvents)); } // Save our event in dictionary @@ -358,14 +359,12 @@ namespace ArchiSteamFarm { await Task.Delay(1000).ConfigureAwait(false); // We're allowed to handle this event if the one that is saved after full second is our event and we succeed in clearing it (we don't care what we're clearing anymore, it doesn't have to be atomic operation) - return LastWriteEvents.TryGetValue(filePath, out object savedWriteEvent) && (currentWriteEvent == savedWriteEvent) && LastWriteEvents.TryRemove(filePath, out _); + return LastWriteEvents.TryGetValue(filePath, out object? savedWriteEvent) && (currentWriteEvent == savedWriteEvent) && LastWriteEvents.TryRemove(filePath, out _); } private static void InitBotsComparer(StringComparer botsComparer) { if (botsComparer == null) { - ArchiLogger.LogNullError(nameof(botsComparer)); - - return; + throw new ArgumentNullException(nameof(botsComparer)); } if (Bot.Bots != null) { @@ -394,9 +393,7 @@ namespace ArchiSteamFarm { private static bool IsValidBotName(string botName) { if (string.IsNullOrEmpty(botName)) { - ArchiLogger.LogNullError(nameof(botName)); - - return false; + throw new ArgumentNullException(nameof(botName)); } if (botName[0] == '.') { @@ -408,9 +405,7 @@ namespace ArchiSteamFarm { private static async void OnChanged(object sender, FileSystemEventArgs e) { if ((sender == null) || (e == null)) { - ArchiLogger.LogNullError(nameof(sender) + " || " + nameof(e)); - - return; + throw new ArgumentNullException(nameof(sender) + " || " + nameof(e)); } await OnChangedFile(e.Name, e.FullPath).ConfigureAwait(false); @@ -418,9 +413,7 @@ namespace ArchiSteamFarm { private static async Task OnChangedConfigFile(string name, string fullPath) { if (string.IsNullOrEmpty(name) || string.IsNullOrEmpty(fullPath)) { - ArchiLogger.LogNullError(nameof(name) + " || " + nameof(fullPath)); - - return; + throw new ArgumentNullException(nameof(name) + " || " + nameof(fullPath)); } await OnCreatedConfigFile(name, fullPath).ConfigureAwait(false); @@ -428,9 +421,7 @@ namespace ArchiSteamFarm { private static async Task OnChangedConfigFile(string name) { if (string.IsNullOrEmpty(name)) { - ArchiLogger.LogNullError(nameof(name)); - - return; + throw new ArgumentNullException(nameof(name)); } if (!name.Equals(SharedInfo.IPCConfigFile) || (GlobalConfig?.IPC != true)) { @@ -447,6 +438,10 @@ namespace ArchiSteamFarm { } private static async Task OnChangedFile(string name, string fullPath) { + if (string.IsNullOrEmpty(name) || string.IsNullOrEmpty(fullPath)) { + throw new ArgumentNullException(nameof(name) + " || " + nameof(fullPath)); + } + string extension = Path.GetExtension(name); switch (extension) { @@ -464,9 +459,7 @@ namespace ArchiSteamFarm { private static async Task OnChangedKeysFile(string name, string fullPath) { if (string.IsNullOrEmpty(name) || string.IsNullOrEmpty(fullPath)) { - ArchiLogger.LogNullError(nameof(name) + " || " + nameof(fullPath)); - - return; + throw new ArgumentNullException(nameof(name) + " || " + nameof(fullPath)); } await OnCreatedKeysFile(name, fullPath).ConfigureAwait(false); @@ -474,9 +467,7 @@ namespace ArchiSteamFarm { private static async void OnCreated(object sender, FileSystemEventArgs e) { if ((sender == null) || (e == null)) { - ArchiLogger.LogNullError(nameof(sender) + " || " + nameof(e)); - - return; + throw new ArgumentNullException(nameof(sender) + " || " + nameof(e)); } await OnCreatedFile(e.Name, e.FullPath).ConfigureAwait(false); @@ -484,9 +475,7 @@ namespace ArchiSteamFarm { private static async Task OnCreatedConfigFile(string name, string fullPath) { if (string.IsNullOrEmpty(name) || string.IsNullOrEmpty(fullPath)) { - ArchiLogger.LogNullError(nameof(name) + " || " + nameof(fullPath)); - - return; + throw new ArgumentNullException(nameof(name) + " || " + nameof(fullPath)); } string extension = Path.GetExtension(name); @@ -505,9 +494,7 @@ namespace ArchiSteamFarm { private static async Task OnCreatedFile(string name, string fullPath) { if (string.IsNullOrEmpty(name) || string.IsNullOrEmpty(fullPath)) { - ArchiLogger.LogNullError(nameof(name) + " || " + nameof(fullPath)); - - return; + throw new ArgumentNullException(nameof(name) + " || " + nameof(fullPath)); } string extension = Path.GetExtension(name); @@ -526,10 +513,8 @@ namespace ArchiSteamFarm { } private static async Task OnCreatedJsonFile(string name, string fullPath) { - if (string.IsNullOrEmpty(name) || string.IsNullOrEmpty(fullPath)) { - ArchiLogger.LogNullError(nameof(name) + " || " + nameof(fullPath)); - - return; + if (string.IsNullOrEmpty(name) || string.IsNullOrEmpty(fullPath) || (Bot.Bots == null)) { + throw new ArgumentNullException(nameof(name) + " || " + nameof(fullPath) + " || " + nameof(Bot.Bots)); } string botName = Path.GetFileNameWithoutExtension(name); @@ -553,7 +538,7 @@ namespace ArchiSteamFarm { return; } - if (Bot.Bots.TryGetValue(botName, out Bot bot)) { + if (Bot.Bots.TryGetValue(botName, out Bot? bot)) { await bot.OnConfigChanged(false).ConfigureAwait(false); } else { await Bot.RegisterBot(botName).ConfigureAwait(false); @@ -565,10 +550,8 @@ namespace ArchiSteamFarm { } private static async Task OnCreatedKeysFile(string name, string fullPath) { - if (string.IsNullOrEmpty(name) || string.IsNullOrEmpty(fullPath)) { - ArchiLogger.LogNullError(nameof(name) + " || " + nameof(fullPath)); - - return; + if (string.IsNullOrEmpty(name) || string.IsNullOrEmpty(fullPath) || (Bot.Bots == null)) { + throw new ArgumentNullException(nameof(name) + " || " + nameof(fullPath) + " || " + nameof(Bot.Bots)); } string botName = Path.GetFileNameWithoutExtension(name); @@ -581,7 +564,7 @@ namespace ArchiSteamFarm { return; } - if (!Bot.Bots.TryGetValue(botName, out Bot bot)) { + if (!Bot.Bots.TryGetValue(botName, out Bot? bot)) { return; } @@ -590,9 +573,7 @@ namespace ArchiSteamFarm { private static async void OnDeleted(object sender, FileSystemEventArgs e) { if ((sender == null) || (e == null)) { - ArchiLogger.LogNullError(nameof(sender) + " || " + nameof(e)); - - return; + throw new ArgumentNullException(nameof(sender) + " || " + nameof(e)); } await OnDeletedFile(e.Name, e.FullPath).ConfigureAwait(false); @@ -600,9 +581,7 @@ namespace ArchiSteamFarm { private static async Task OnDeletedConfigFile(string name, string fullPath) { if (string.IsNullOrEmpty(name) || string.IsNullOrEmpty(fullPath)) { - ArchiLogger.LogNullError(nameof(name) + " || " + nameof(fullPath)); - - return; + throw new ArgumentNullException(nameof(name) + " || " + nameof(fullPath)); } string extension = Path.GetExtension(name); @@ -621,9 +600,7 @@ namespace ArchiSteamFarm { private static async Task OnDeletedFile(string name, string fullPath) { if (string.IsNullOrEmpty(name) || string.IsNullOrEmpty(fullPath)) { - ArchiLogger.LogNullError(nameof(name) + " || " + nameof(fullPath)); - - return; + throw new ArgumentNullException(nameof(name) + " || " + nameof(fullPath)); } string extension = Path.GetExtension(name); @@ -638,10 +615,8 @@ namespace ArchiSteamFarm { } private static async Task OnDeletedJsonConfigFile(string name, string fullPath) { - if (string.IsNullOrEmpty(name) || string.IsNullOrEmpty(fullPath)) { - ArchiLogger.LogNullError(nameof(name) + " || " + nameof(fullPath)); - - return; + if (string.IsNullOrEmpty(name) || string.IsNullOrEmpty(fullPath) || (Bot.Bots == null)) { + throw new ArgumentNullException(nameof(name) + " || " + nameof(fullPath) + " || " + nameof(Bot.Bots)); } string botName = Path.GetFileNameWithoutExtension(name); @@ -677,16 +652,14 @@ namespace ArchiSteamFarm { return; } - if (Bot.Bots.TryGetValue(botName, out Bot bot)) { + if (Bot.Bots.TryGetValue(botName, out Bot? bot)) { await bot.OnConfigChanged(true).ConfigureAwait(false); } } private static async void OnRenamed(object sender, RenamedEventArgs e) { if ((sender == null) || (e == null)) { - ArchiLogger.LogNullError(nameof(sender) + " || " + nameof(e)); - - return; + throw new ArgumentNullException(nameof(sender) + " || " + nameof(e)); } await OnDeletedFile(e.OldName, e.OldFullPath).ConfigureAwait(false); @@ -694,8 +667,8 @@ namespace ArchiSteamFarm { } private static async Task RegisterBots() { - if (Bot.Bots.Count > 0) { - return; + if ((GlobalConfig == null) || (GlobalDatabase == null) || (WebBrowser == null)) { + throw new ArgumentNullException(nameof(GlobalConfig) + " || " + nameof(GlobalDatabase) + " || " + nameof(WebBrowser)); } // Ensure that we ask for a list of servers if we don't have any saved servers available @@ -720,7 +693,7 @@ namespace ArchiSteamFarm { HashSet botNames; try { - botNames = Directory.Exists(SharedInfo.ConfigDirectory) ? Directory.EnumerateFiles(SharedInfo.ConfigDirectory, "*" + SharedInfo.JsonConfigExtension).Select(Path.GetFileNameWithoutExtension).Where(botName => !string.IsNullOrEmpty(botName) && IsValidBotName(botName)).ToHashSet(Bot.BotsComparer) : new HashSet(0); + botNames = Directory.Exists(SharedInfo.ConfigDirectory) ? Directory.EnumerateFiles(SharedInfo.ConfigDirectory, "*" + SharedInfo.JsonConfigExtension).Select(Path.GetFileNameWithoutExtension).Where(botName => !string.IsNullOrEmpty(botName) && IsValidBotName(botName)).ToHashSet(Bot.BotsComparer)! : new HashSet(0); } catch (Exception e) { ArchiLogger.LogGenericException(e); @@ -742,6 +715,10 @@ namespace ArchiSteamFarm { } private static async Task UpdateAndRestart() { + if (GlobalConfig == null) { + throw new ArgumentNullException(nameof(GlobalConfig)); + } + if (!SharedInfo.BuildInfo.CanUpdate || (GlobalConfig.UpdateChannel == GlobalConfig.EUpdateChannel.None)) { return; } @@ -759,7 +736,7 @@ namespace ArchiSteamFarm { ArchiLogger.LogGenericInfo(string.Format(Strings.AutoUpdateCheckInfo, autoUpdatePeriod.ToHumanReadable())); } - Version newVersion = await Update().ConfigureAwait(false); + Version? newVersion = await Update().ConfigureAwait(false); if ((newVersion == null) || (newVersion <= SharedInfo.Version)) { return; @@ -770,9 +747,7 @@ namespace ArchiSteamFarm { private static bool UpdateFromArchive(ZipArchive archive, string targetDirectory) { if ((archive == null) || string.IsNullOrEmpty(targetDirectory)) { - ArchiLogger.LogNullError(nameof(archive) + " || " + nameof(targetDirectory)); - - return false; + throw new ArgumentNullException(nameof(archive) + " || " + nameof(targetDirectory)); } // Firstly we'll move all our existing files to a backup directory @@ -795,7 +770,7 @@ namespace ArchiSteamFarm { return false; } - string relativeDirectoryName = Path.GetDirectoryName(relativeFilePath); + string? relativeDirectoryName = Path.GetDirectoryName(relativeFilePath); switch (relativeDirectoryName) { case null: @@ -854,7 +829,7 @@ namespace ArchiSteamFarm { // Check if this file requires its own folder if (zipFile.Name != zipFile.FullName) { - string directory = Path.GetDirectoryName(file); + string? directory = Path.GetDirectoryName(file); if (string.IsNullOrEmpty(directory)) { ArchiLogger.LogNullError(nameof(directory)); diff --git a/ArchiSteamFarm/Actions.cs b/ArchiSteamFarm/Actions.cs index 231db57db..b839e1db4 100644 --- a/ArchiSteamFarm/Actions.cs +++ b/ArchiSteamFarm/Actions.cs @@ -40,11 +40,11 @@ namespace ArchiSteamFarm { private readonly ConcurrentHashSet HandledGifts = new ConcurrentHashSet(); private readonly SemaphoreSlim TradingSemaphore = new SemaphoreSlim(1, 1); - private Timer CardsFarmerResumeTimer; + private Timer? CardsFarmerResumeTimer; private bool ProcessingGiftsScheduled; private bool TradingScheduled; - internal Actions([NotNull] Bot bot) => Bot = bot ?? throw new ArgumentNullException(nameof(bot)); + internal Actions(Bot bot) => Bot = bot ?? throw new ArgumentNullException(nameof(bot)); public async ValueTask DisposeAsync() { // Those are objects that are always being created if constructor doesn't throw exception @@ -70,17 +70,16 @@ namespace ArchiSteamFarm { } [PublicAPI] - public async Task<(bool Success, string Token, string Message)> GenerateTwoFactorAuthenticationToken() { - if (!Bot.HasMobileAuthenticator) { + public async Task<(bool Success, string? Token, string Message)> GenerateTwoFactorAuthenticationToken() { + if (Bot.BotDatabase.MobileAuthenticator == null) { return (false, null, Strings.BotNoASFAuthenticator); } - string token = await Bot.BotDatabase.MobileAuthenticator.GenerateToken().ConfigureAwait(false); + string? token = await Bot.BotDatabase.MobileAuthenticator.GenerateToken().ConfigureAwait(false); return (true, token, Strings.Success); } - [ItemNotNull] [PublicAPI] public async Task GetTradingLock() { await TradingSemaphore.WaitAsync().ConfigureAwait(false); @@ -89,8 +88,8 @@ namespace ArchiSteamFarm { } [PublicAPI] - public async Task<(bool Success, string Message)> HandleTwoFactorAuthenticationConfirmations(bool accept, MobileAuthenticator.Confirmation.EType? acceptedType = null, IReadOnlyCollection acceptedCreatorIDs = null, bool waitIfNeeded = false) { - if (!Bot.HasMobileAuthenticator) { + public async Task<(bool Success, string Message)> HandleTwoFactorAuthenticationConfirmations(bool accept, MobileAuthenticator.Confirmation.EType? acceptedType = null, IReadOnlyCollection? acceptedCreatorIDs = null, bool waitIfNeeded = false) { + if (Bot.BotDatabase.MobileAuthenticator == null) { return (false, Strings.BotNoASFAuthenticator); } @@ -99,14 +98,14 @@ namespace ArchiSteamFarm { } ushort handledConfirmationsCount = 0; - HashSet handledCreatorIDs = null; + HashSet? handledCreatorIDs = null; for (byte i = 0; (i == 0) || ((i < WebBrowser.MaxTries) && waitIfNeeded); i++) { if (i > 0) { await Task.Delay(1000).ConfigureAwait(false); } - HashSet confirmations = await Bot.BotDatabase.MobileAuthenticator.GetConfirmations().ConfigureAwait(false); + HashSet? confirmations = await Bot.BotDatabase.MobileAuthenticator.GetConfirmations().ConfigureAwait(false); if ((confirmations == null) || (confirmations.Count == 0)) { continue; @@ -135,7 +134,7 @@ namespace ArchiSteamFarm { handledConfirmationsCount += (ushort) confirmations.Count; if ((acceptedCreatorIDs != null) && (acceptedCreatorIDs.Count > 0)) { - IEnumerable handledCreatorIDsThisRound = confirmations.Select(confirmation => confirmation.Creator).Where(acceptedCreatorIDs.Contains); + IEnumerable handledCreatorIDsThisRound = confirmations.Select(confirmation => confirmation.Creator).Where(acceptedCreatorIDs.Contains!); if (handledCreatorIDs != null) { handledCreatorIDs.UnionWith(handledCreatorIDsThisRound); @@ -186,11 +185,9 @@ namespace ArchiSteamFarm { } [PublicAPI] - public async Task<(bool Success, string Message)> Play(IEnumerable gameIDs, string gameName = null) { + public async Task<(bool Success, string Message)> Play(IEnumerable gameIDs, string? gameName = null) { if (gameIDs == null) { - Bot.ArchiLogger.LogNullError(nameof(gameIDs)); - - return (false, string.Format(Strings.ErrorObjectIsNull, nameof(gameIDs))); + throw new ArgumentNullException(nameof(gameIDs)); } if (!Bot.IsConnectedAndLoggedOn) { @@ -207,7 +204,7 @@ namespace ArchiSteamFarm { } [PublicAPI] - public async Task RedeemKey(string key) { + public async Task RedeemKey(string key) { await LimitGiftsRequestsAsync().ConfigureAwait(false); return await Bot.ArchiHandler.RedeemKey(key).ConfigureAwait(false); @@ -238,11 +235,9 @@ namespace ArchiSteamFarm { } [PublicAPI] - public async Task<(bool Success, string Message)> SendInventory(uint appID = Steam.Asset.SteamAppID, ulong contextID = Steam.Asset.SteamCommunityContextID, ulong targetSteamID = 0, string tradeToken = null, Func filterFunction = null) { + public async Task<(bool Success, string Message)> SendInventory(uint appID = Steam.Asset.SteamAppID, ulong contextID = Steam.Asset.SteamCommunityContextID, ulong targetSteamID = 0, string? tradeToken = null, Func? filterFunction = null) { if ((appID == 0) || (contextID == 0)) { - Bot.ArchiLogger.LogNullError(nameof(appID) + " || " + nameof(contextID)); - - return (false, string.Format(Strings.ErrorObjectIsNull, nameof(appID) + " || " + nameof(contextID))); + throw new ArgumentNullException(nameof(appID) + " || " + nameof(contextID)); } if (!Bot.IsConnectedAndLoggedOn) { @@ -304,14 +299,14 @@ namespace ArchiSteamFarm { } if (string.IsNullOrEmpty(tradeToken) && (Bot.SteamFriends.GetFriendRelationship(targetSteamID) != EFriendRelationship.Friend)) { - Bot targetBot = Bot.Bots.Values.FirstOrDefault(bot => bot.SteamID == targetSteamID); + Bot? targetBot = Bot.Bots?.Values.FirstOrDefault(bot => bot.SteamID == targetSteamID); if (targetBot?.IsConnectedAndLoggedOn == true) { tradeToken = await targetBot.ArchiHandler.GetTradeToken().ConfigureAwait(false); } } - (bool success, HashSet mobileTradeOfferIDs) = await Bot.ArchiWebHandler.SendTradeOffer(targetSteamID, inventory, token: tradeToken).ConfigureAwait(false); + (bool success, HashSet? mobileTradeOfferIDs) = await Bot.ArchiWebHandler.SendTradeOffer(targetSteamID, inventory, token: tradeToken).ConfigureAwait(false); if ((mobileTradeOfferIDs != null) && (mobileTradeOfferIDs.Count > 0) && Bot.HasMobileAuthenticator) { (bool twoFactorSuccess, _) = await HandleTwoFactorAuthenticationConfirmations(true, MobileAuthenticator.Confirmation.EType.Trade, mobileTradeOfferIDs, true).ConfigureAwait(false); @@ -354,8 +349,8 @@ namespace ArchiSteamFarm { } [PublicAPI] - public static async Task<(bool Success, string Message, Version Version)> Update() { - Version version = await ASF.Update(true).ConfigureAwait(false); + public static async Task<(bool Success, string? Message, Version? Version)> Update() { + Version? version = await ASF.Update(true).ConfigureAwait(false); if (version == null) { return (false, null, null); @@ -386,7 +381,7 @@ namespace ArchiSteamFarm { ProcessingGiftsScheduled = false; } - HashSet giftCardIDs = await Bot.ArchiWebHandler.GetDigitalGiftCards().ConfigureAwait(false); + HashSet? giftCardIDs = await Bot.ArchiWebHandler.GetDigitalGiftCards().ConfigureAwait(false); if ((giftCardIDs == null) || (giftCardIDs.Count == 0)) { return; @@ -413,9 +408,7 @@ namespace ArchiSteamFarm { internal async Task AcceptGuestPasses(IReadOnlyCollection guestPassIDs) { if ((guestPassIDs == null) || (guestPassIDs.Count == 0)) { - Bot.ArchiLogger.LogNullError(nameof(guestPassIDs)); - - return; + throw new ArgumentNullException(nameof(guestPassIDs)); } foreach (ulong guestPassID in guestPassIDs.Where(guestPassID => !HandledGifts.Contains(guestPassID))) { @@ -424,7 +417,7 @@ namespace ArchiSteamFarm { Bot.ArchiLogger.LogGenericInfo(string.Format(Strings.BotAcceptingGift, guestPassID)); await LimitGiftsRequestsAsync().ConfigureAwait(false); - ArchiHandler.RedeemGuestPassResponseCallback response = await Bot.ArchiHandler.RedeemGuestPass(guestPassID).ConfigureAwait(false); + ArchiHandler.RedeemGuestPassResponseCallback? response = await Bot.ArchiHandler.RedeemGuestPass(guestPassID).ConfigureAwait(false); if (response != null) { if (response.Result == EResult.OK) { @@ -441,19 +434,25 @@ 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.EPermission.Master)).Select(kv => kv.Key).OrderBy(steamID => steamID).FirstOrDefault(); - return steamMasterID > 0 ? steamMasterID : (ASF.GlobalConfig.SteamOwnerID != 0) && new SteamID(ASF.GlobalConfig.SteamOwnerID).IsIndividualAccount ? ASF.GlobalConfig.SteamOwnerID : 0; + if (steamMasterID > 0) { + return steamMasterID; + } + + ulong steamOwnerID = ASF.GlobalConfig?.SteamOwnerID ?? GlobalConfig.DefaultSteamOwnerID; + + return (steamOwnerID > 0) && new SteamID(steamOwnerID).IsIndividualAccount ? steamOwnerID : 0; } private static async Task LimitGiftsRequestsAsync() { if (ASF.GiftsSemaphore == null) { - ASF.ArchiLogger.LogNullError(nameof(ASF.GiftsSemaphore)); - - return; + throw new ArgumentNullException(nameof(ASF.GiftsSemaphore)); } - if (ASF.GlobalConfig.GiftsLimiterDelay == 0) { + byte giftsLimiterDelay = ASF.GlobalConfig?.GiftsLimiterDelay ?? GlobalConfig.DefaultGiftsLimiterDelay; + + if (giftsLimiterDelay == 0) { return; } @@ -461,7 +460,7 @@ namespace ArchiSteamFarm { Utilities.InBackground( async () => { - await Task.Delay(ASF.GlobalConfig.GiftsLimiterDelay * 1000).ConfigureAwait(false); + await Task.Delay(giftsLimiterDelay * 1000).ConfigureAwait(false); ASF.GiftsSemaphore.Release(); } ); diff --git a/ArchiSteamFarm/ArchiCryptoHelper.cs b/ArchiSteamFarm/ArchiCryptoHelper.cs index 8524ded86..e712dfebb 100644 --- a/ArchiSteamFarm/ArchiCryptoHelper.cs +++ b/ArchiSteamFarm/ArchiCryptoHelper.cs @@ -24,9 +24,7 @@ using System.Collections.Generic; using System.Linq; using System.Security.Cryptography; using System.Text; -using ArchiSteamFarm.Localization; using CryptSharp.Utility; -using JetBrains.Annotations; using SteamKit2; namespace ArchiSteamFarm { @@ -35,10 +33,8 @@ namespace ArchiSteamFarm { private const byte SteamParentalSCryptBlocksCount = 8; private const ushort SteamParentalSCryptIterations = 8192; - [NotNull] private static IEnumerable SteamParentalCharacters => Enumerable.Range('0', 10).Select(character => (byte) character); - [NotNull] private static IEnumerable SteamParentalCodes { get { HashSet steamParentalCharacters = SteamParentalCharacters.ToHashSet(); @@ -49,53 +45,35 @@ namespace ArchiSteamFarm { private static byte[] EncryptionKey = Encoding.UTF8.GetBytes(nameof(ArchiSteamFarm)); - internal static string Decrypt(ECryptoMethod cryptoMethod, string encrypted) { + internal static string? Decrypt(ECryptoMethod cryptoMethod, string encrypted) { if (!Enum.IsDefined(typeof(ECryptoMethod), cryptoMethod) || string.IsNullOrEmpty(encrypted)) { - ASF.ArchiLogger.LogNullError(nameof(cryptoMethod) + " || " + nameof(encrypted)); - - return null; + throw new ArgumentNullException(nameof(cryptoMethod) + " || " + nameof(encrypted)); } - switch (cryptoMethod) { - case ECryptoMethod.PlainText: - return encrypted; - case ECryptoMethod.AES: - return DecryptAES(encrypted); - case ECryptoMethod.ProtectedDataForCurrentUser: - return DecryptProtectedDataForCurrentUser(encrypted); - default: - ASF.ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(cryptoMethod), cryptoMethod)); - - return null; - } + return cryptoMethod switch { + ECryptoMethod.PlainText => encrypted, + ECryptoMethod.AES => DecryptAES(encrypted), + ECryptoMethod.ProtectedDataForCurrentUser => DecryptProtectedDataForCurrentUser(encrypted), + _ => throw new ArgumentOutOfRangeException(nameof(cryptoMethod)) + }; } - internal static string Encrypt(ECryptoMethod cryptoMethod, string decrypted) { + internal static string? Encrypt(ECryptoMethod cryptoMethod, string decrypted) { if (!Enum.IsDefined(typeof(ECryptoMethod), cryptoMethod) || string.IsNullOrEmpty(decrypted)) { - ASF.ArchiLogger.LogNullError(nameof(cryptoMethod) + " || " + nameof(decrypted)); - - return null; + throw new ArgumentNullException(nameof(cryptoMethod) + " || " + nameof(decrypted)); } - switch (cryptoMethod) { - case ECryptoMethod.PlainText: - return decrypted; - case ECryptoMethod.AES: - return EncryptAES(decrypted); - case ECryptoMethod.ProtectedDataForCurrentUser: - return EncryptProtectedDataForCurrentUser(decrypted); - default: - ASF.ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(cryptoMethod), cryptoMethod)); - - return null; - } + return cryptoMethod switch { + ECryptoMethod.PlainText => decrypted, + ECryptoMethod.AES => EncryptAES(decrypted), + ECryptoMethod.ProtectedDataForCurrentUser => EncryptProtectedDataForCurrentUser(decrypted), + _ => throw new ArgumentOutOfRangeException(nameof(cryptoMethod)) + }; } - internal static IEnumerable GenerateSteamParentalHash(byte[] password, byte[] salt, byte hashLength, ESteamParentalAlgorithm steamParentalAlgorithm) { + internal static IEnumerable? GenerateSteamParentalHash(byte[] password, byte[] salt, byte hashLength, ESteamParentalAlgorithm steamParentalAlgorithm) { if ((password == null) || (salt == null) || (hashLength == 0) || !Enum.IsDefined(typeof(ESteamParentalAlgorithm), steamParentalAlgorithm)) { - ASF.ArchiLogger.LogNullError(nameof(password) + " || " + nameof(salt) + " || " + nameof(hashLength) + " || " + nameof(steamParentalAlgorithm)); - - return null; + throw new ArgumentNullException(nameof(password) + " || " + nameof(salt) + " || " + nameof(hashLength) + " || " + nameof(steamParentalAlgorithm)); } switch (steamParentalAlgorithm) { @@ -106,39 +84,31 @@ namespace ArchiSteamFarm { case ESteamParentalAlgorithm.SCrypt: return SCrypt.ComputeDerivedKey(password, salt, SteamParentalSCryptIterations, SteamParentalSCryptBlocksCount, 1, null, hashLength); default: - ASF.ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(steamParentalAlgorithm), steamParentalAlgorithm)); - - return null; + throw new ArgumentOutOfRangeException(nameof(steamParentalAlgorithm)); } } - internal static string RecoverSteamParentalCode(byte[] passwordHash, byte[] salt, ESteamParentalAlgorithm steamParentalAlgorithm) { + internal static string? RecoverSteamParentalCode(byte[] passwordHash, byte[] salt, ESteamParentalAlgorithm steamParentalAlgorithm) { if ((passwordHash == null) || (salt == null) || !Enum.IsDefined(typeof(ESteamParentalAlgorithm), steamParentalAlgorithm)) { - ASF.ArchiLogger.LogNullError(nameof(passwordHash) + " || " + nameof(salt) + " || " + nameof(steamParentalAlgorithm)); - - return null; + throw new ArgumentNullException(nameof(passwordHash) + " || " + nameof(salt) + " || " + nameof(steamParentalAlgorithm)); } - byte[] password = SteamParentalCodes.AsParallel().FirstOrDefault(passwordToTry => GenerateSteamParentalHash(passwordToTry, salt, (byte) passwordHash.Length, steamParentalAlgorithm)?.SequenceEqual(passwordHash) == true); + byte[]? password = SteamParentalCodes.AsParallel().FirstOrDefault(passwordToTry => GenerateSteamParentalHash(passwordToTry, salt, (byte) passwordHash.Length, steamParentalAlgorithm)?.SequenceEqual(passwordHash) == true); return password != null ? Encoding.UTF8.GetString(password) : null; } internal static void SetEncryptionKey(string key) { if (string.IsNullOrEmpty(key)) { - ASF.ArchiLogger.LogNullError(nameof(key)); - - return; + throw new ArgumentNullException(nameof(key)); } EncryptionKey = Encoding.UTF8.GetBytes(key); } - private static string DecryptAES(string encrypted) { + private static string? DecryptAES(string encrypted) { if (string.IsNullOrEmpty(encrypted)) { - ASF.ArchiLogger.LogNullError(nameof(encrypted)); - - return null; + throw new ArgumentNullException(nameof(encrypted)); } try { @@ -159,11 +129,9 @@ namespace ArchiSteamFarm { } } - private static string DecryptProtectedDataForCurrentUser(string encrypted) { + private static string? DecryptProtectedDataForCurrentUser(string encrypted) { if (string.IsNullOrEmpty(encrypted)) { - ASF.ArchiLogger.LogNullError(nameof(encrypted)); - - return null; + throw new ArgumentNullException(nameof(encrypted)); } try { @@ -185,11 +153,9 @@ namespace ArchiSteamFarm { } } - private static string EncryptAES(string decrypted) { + private static string? EncryptAES(string decrypted) { if (string.IsNullOrEmpty(decrypted)) { - ASF.ArchiLogger.LogNullError(nameof(decrypted)); - - return null; + throw new ArgumentNullException(nameof(decrypted)); } try { @@ -210,11 +176,9 @@ namespace ArchiSteamFarm { } } - private static string EncryptProtectedDataForCurrentUser(string decrypted) { + private static string? EncryptProtectedDataForCurrentUser(string decrypted) { if (string.IsNullOrEmpty(decrypted)) { - ASF.ArchiLogger.LogNullError(nameof(decrypted)); - - return null; + throw new ArgumentNullException(nameof(decrypted)); } try { diff --git a/ArchiSteamFarm/ArchiHandler.cs b/ArchiSteamFarm/ArchiHandler.cs index 8b3663381..283f47eac 100644 --- a/ArchiSteamFarm/ArchiHandler.cs +++ b/ArchiSteamFarm/ArchiHandler.cs @@ -47,7 +47,7 @@ namespace ArchiSteamFarm { internal DateTime LastPacketReceived { get; private set; } - internal ArchiHandler([JetBrains.Annotations.NotNull] ArchiLogger archiLogger, [JetBrains.Annotations.NotNull] SteamUnifiedMessages steamUnifiedMessages) { + internal ArchiHandler(ArchiLogger archiLogger, SteamUnifiedMessages steamUnifiedMessages) { if ((archiLogger == null) || (steamUnifiedMessages == null)) { throw new ArgumentNullException(nameof(archiLogger) + " || " + nameof(steamUnifiedMessages)); } @@ -62,14 +62,8 @@ namespace ArchiSteamFarm { } public override void HandleMsg(IPacketMsg packetMsg) { - if (packetMsg == null) { - ArchiLogger.LogNullError(nameof(packetMsg)); - - return; - } - - if (Client == null) { - ArchiLogger.LogNullError(nameof(Client)); + if ((packetMsg == null) || (Client == null)) { + ArchiLogger.LogNullError(nameof(packetMsg) + " || " + nameof(Client)); return; } @@ -122,9 +116,7 @@ namespace ArchiSteamFarm { internal void AckChatMessage(ulong chatGroupID, ulong chatID, uint timestamp) { if ((chatGroupID == 0) || (chatID == 0) || (timestamp == 0)) { - ArchiLogger.LogNullError(nameof(chatGroupID) + " || " + nameof(chatID) + " || " + nameof(timestamp)); - - return; + throw new ArgumentNullException(nameof(chatGroupID) + " || " + nameof(chatID) + " || " + nameof(timestamp)); } if (Client == null) { @@ -148,9 +140,7 @@ namespace ArchiSteamFarm { internal void AckMessage(ulong steamID, uint timestamp) { if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || (timestamp == 0)) { - ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(timestamp)); - - return; + throw new ArgumentNullException(nameof(steamID) + " || " + nameof(timestamp)); } if (Client == null) { @@ -173,9 +163,7 @@ namespace ArchiSteamFarm { internal void AcknowledgeClanInvite(ulong steamID, bool acceptInvite) { if ((steamID == 0) || !new SteamID(steamID).IsClanAccount) { - ArchiLogger.LogNullError(nameof(steamID)); - - return; + throw new ArgumentNullException(nameof(steamID)); } if (Client == null) { @@ -200,9 +188,7 @@ namespace ArchiSteamFarm { internal async Task AddFriend(ulong steamID) { if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { - ArchiLogger.LogNullError(nameof(steamID)); - - return false; + throw new ArgumentNullException(nameof(steamID)); } if (Client == null) { @@ -232,9 +218,7 @@ namespace ArchiSteamFarm { internal async Task GetClanChatGroupID(ulong steamID) { if ((steamID == 0) || !new SteamID(steamID).IsClanAccount) { - ArchiLogger.LogNullError(nameof(steamID)); - - return 0; + throw new ArgumentNullException(nameof(steamID)); } if (Client == null) { @@ -302,7 +286,7 @@ namespace ArchiSteamFarm { return body.player_level; } - internal async Task> GetMyChatGroupIDs() { + internal async Task?> GetMyChatGroupIDs() { if (Client == null) { ArchiLogger.LogNullError(nameof(Client)); @@ -334,7 +318,7 @@ namespace ArchiSteamFarm { return body.chat_room_groups.Select(chatRoom => chatRoom.group_summary.chat_group_id).ToHashSet(); } - internal async Task GetPrivacySettings() { + internal async Task GetPrivacySettings() { if (Client == null) { ArchiLogger.LogNullError(nameof(Client)); @@ -366,7 +350,7 @@ namespace ArchiSteamFarm { return body.privacy_settings; } - internal async Task GetTradeToken() { + internal async Task GetTradeToken() { if (Client == null) { ArchiLogger.LogNullError(nameof(Client)); @@ -398,11 +382,9 @@ namespace ArchiSteamFarm { return body.trade_offer_access_token; } - internal async Task GetTwoFactorDeviceIdentifier(ulong steamID) { + internal async Task GetTwoFactorDeviceIdentifier(ulong steamID) { if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { - ArchiLogger.LogNullError(nameof(steamID)); - - return null; + throw new ArgumentNullException(nameof(steamID)); } if (Client == null) { @@ -440,9 +422,7 @@ namespace ArchiSteamFarm { internal async Task JoinChatRoomGroup(ulong chatGroupID) { if (chatGroupID == 0) { - ArchiLogger.LogNullError(nameof(chatGroupID)); - - return false; + throw new ArgumentNullException(nameof(chatGroupID)); } if (Client == null) { @@ -470,11 +450,9 @@ namespace ArchiSteamFarm { return response.Result == EResult.OK; } - internal async Task PlayGames(IEnumerable gameIDs, string gameName = null) { + internal async Task PlayGames(IEnumerable gameIDs, string? gameName = null) { if (gameIDs == null) { - ArchiLogger.LogNullError(nameof(gameIDs)); - - return; + throw new ArgumentNullException(nameof(gameIDs)); } if (Client == null) { @@ -526,11 +504,9 @@ namespace ArchiSteamFarm { Client.Send(request); } - internal async Task RedeemGuestPass(ulong guestPassID) { + internal async Task RedeemGuestPass(ulong guestPassID) { if (guestPassID == 0) { - ArchiLogger.LogNullError(nameof(guestPassID)); - - return null; + throw new ArgumentNullException(nameof(guestPassID)); } if (Client == null) { @@ -559,11 +535,9 @@ namespace ArchiSteamFarm { } } - internal async Task RedeemKey(string key) { + internal async Task RedeemKey(string key) { if (string.IsNullOrEmpty(key)) { - ArchiLogger.LogNullError(nameof(key)); - - return null; + throw new ArgumentNullException(nameof(key)); } if (Client == null) { @@ -594,9 +568,7 @@ namespace ArchiSteamFarm { internal async Task RemoveFriend(ulong steamID) { if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { - ArchiLogger.LogNullError(nameof(steamID)); - - return false; + throw new ArgumentNullException(nameof(steamID)); } if (Client == null) { @@ -641,9 +613,7 @@ namespace ArchiSteamFarm { internal async Task SendMessage(ulong steamID, string message) { if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || string.IsNullOrEmpty(message)) { - ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(message)); - - return EResult.Invalid; + throw new ArgumentNullException(nameof(steamID) + " || " + nameof(message)); } if (Client == null) { @@ -678,9 +648,7 @@ namespace ArchiSteamFarm { internal async Task SendMessage(ulong chatGroupID, ulong chatID, string message) { if ((chatGroupID == 0) || (chatID == 0) || string.IsNullOrEmpty(message)) { - ArchiLogger.LogNullError(nameof(chatGroupID) + " || " + nameof(chatID) + " || " + nameof(message)); - - return EResult.Invalid; + throw new ArgumentNullException(nameof(chatGroupID) + " || " + nameof(chatID) + " || " + nameof(message)); } if (Client == null) { @@ -714,9 +682,7 @@ namespace ArchiSteamFarm { internal async Task SendTypingStatus(ulong steamID) { if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { - ArchiLogger.LogNullError(nameof(steamID)); - - return EResult.Invalid; + throw new ArgumentNullException(nameof(steamID)); } if (Client == null) { @@ -749,9 +715,7 @@ namespace ArchiSteamFarm { internal void SetCurrentMode(uint chatMode) { if (chatMode == 0) { - ArchiLogger.LogNullError(nameof(chatMode)); - - return; + throw new ArgumentNullException(nameof(chatMode)); } if (Client == null) { @@ -770,7 +734,7 @@ namespace ArchiSteamFarm { [SuppressMessage("ReSharper", "MemberCanBeInternal")] public sealed class PurchaseResponseCallback : CallbackMsg { - public readonly Dictionary Items; + public readonly Dictionary? Items; public EPurchaseResultDetail PurchaseResultDetail { get; internal set; } public EResult Result { get; internal set; } @@ -784,7 +748,7 @@ namespace ArchiSteamFarm { PurchaseResultDetail = purchaseResult; } - internal PurchaseResponseCallback([JetBrains.Annotations.NotNull] JobID jobID, [JetBrains.Annotations.NotNull] CMsgClientPurchaseResponse msg) { + internal PurchaseResponseCallback(JobID jobID, CMsgClientPurchaseResponse msg) { if ((jobID == null) || (msg == null)) { throw new ArgumentNullException(nameof(jobID) + " || " + nameof(msg)); } @@ -832,7 +796,7 @@ namespace ArchiSteamFarm { } } - string gameName = lineItem["ItemDescription"].Value; + string? gameName = lineItem["ItemDescription"].AsString(); if (string.IsNullOrEmpty(gameName)) { ASF.ArchiLogger.LogNullError(nameof(gameName)); @@ -850,7 +814,7 @@ namespace ArchiSteamFarm { public sealed class UserNotificationsCallback : CallbackMsg { internal readonly Dictionary Notifications; - internal UserNotificationsCallback([JetBrains.Annotations.NotNull] JobID jobID, [JetBrains.Annotations.NotNull] CMsgClientUserNotifications msg) { + internal UserNotificationsCallback(JobID jobID, CMsgClientUserNotifications msg) { if ((jobID == null) || (msg == null)) { throw new ArgumentNullException(nameof(jobID) + " || " + nameof(msg)); } @@ -890,7 +854,7 @@ namespace ArchiSteamFarm { } } - internal UserNotificationsCallback([JetBrains.Annotations.NotNull] JobID jobID, [JetBrains.Annotations.NotNull] CMsgClientItemAnnouncements msg) { + internal UserNotificationsCallback(JobID jobID, CMsgClientItemAnnouncements msg) { if ((jobID == null) || (msg == null)) { throw new ArgumentNullException(nameof(jobID) + " || " + nameof(msg)); } @@ -899,7 +863,7 @@ namespace ArchiSteamFarm { Notifications = new Dictionary(1) { { EUserNotification.Items, msg.count_new_items } }; } - internal UserNotificationsCallback([JetBrains.Annotations.NotNull] JobID jobID, [JetBrains.Annotations.NotNull] CMsgClientCommentNotifications msg) { + internal UserNotificationsCallback(JobID jobID, CMsgClientCommentNotifications msg) { if ((jobID == null) || (msg == null)) { throw new ArgumentNullException(nameof(jobID) + " || " + nameof(msg)); } @@ -911,24 +875,23 @@ namespace ArchiSteamFarm { [PublicAPI] public enum EUserNotification : byte { Unknown, - Trading, - GameTurns, - ModeratorMessages, - Comments, - Items, - Invites, - Unknown7, // No clue what 7 stands for, and I doubt we can find out - Gifts, - Chat, - HelpRequestReplies, - AccountAlerts + Trading = 1, + GameTurns = 2, + ModeratorMessages = 3, + Comments = 4, + Items = 5, + Invites = 6, + Gifts = 8, + Chat = 9, + HelpRequestReplies = 10, + AccountAlerts = 11 } } internal sealed class PlayingSessionStateCallback : CallbackMsg { internal readonly bool PlayingBlocked; - internal PlayingSessionStateCallback([JetBrains.Annotations.NotNull] JobID jobID, [JetBrains.Annotations.NotNull] CMsgClientPlayingSessionState msg) { + internal PlayingSessionStateCallback(JobID jobID, CMsgClientPlayingSessionState msg) { if ((jobID == null) || (msg == null)) { throw new ArgumentNullException(nameof(jobID) + " || " + nameof(msg)); } @@ -941,7 +904,7 @@ namespace ArchiSteamFarm { internal sealed class RedeemGuestPassResponseCallback : CallbackMsg { internal readonly EResult Result; - internal RedeemGuestPassResponseCallback([JetBrains.Annotations.NotNull] JobID jobID, [JetBrains.Annotations.NotNull] CMsgClientRedeemGuestPassResponse msg) { + internal RedeemGuestPassResponseCallback(JobID jobID, CMsgClientRedeemGuestPassResponse msg) { if ((jobID == null) || (msg == null)) { throw new ArgumentNullException(nameof(jobID) + " || " + nameof(msg)); } @@ -954,7 +917,7 @@ namespace ArchiSteamFarm { internal sealed class SharedLibraryLockStatusCallback : CallbackMsg { internal readonly ulong LibraryLockedBySteamID; - internal SharedLibraryLockStatusCallback([JetBrains.Annotations.NotNull] JobID jobID, [JetBrains.Annotations.NotNull] CMsgClientSharedLibraryLockStatus msg) { + internal SharedLibraryLockStatusCallback(JobID jobID, CMsgClientSharedLibraryLockStatus msg) { if ((jobID == null) || (msg == null)) { throw new ArgumentNullException(nameof(jobID) + " || " + nameof(msg)); } @@ -972,7 +935,7 @@ namespace ArchiSteamFarm { internal sealed class VanityURLChangedCallback : CallbackMsg { internal readonly string VanityURL; - internal VanityURLChangedCallback([JetBrains.Annotations.NotNull] JobID jobID, [JetBrains.Annotations.NotNull] CMsgClientVanityURLChangedNotification msg) { + internal VanityURLChangedCallback(JobID jobID, CMsgClientVanityURLChangedNotification msg) { if ((jobID == null) || (msg == null)) { throw new ArgumentNullException(nameof(jobID) + " || " + nameof(msg)); } diff --git a/ArchiSteamFarm/ArchiWebHandler.cs b/ArchiSteamFarm/ArchiWebHandler.cs index eb831107c..2f3de377f 100644 --- a/ArchiSteamFarm/ArchiWebHandler.cs +++ b/ArchiSteamFarm/ArchiWebHandler.cs @@ -74,13 +74,13 @@ namespace ArchiSteamFarm { private DateTime LastSessionCheck; private DateTime LastSessionRefresh; private bool MarkingInventoryScheduled; - private string VanityURL; + private string? VanityURL; - internal ArchiWebHandler([NotNull] Bot bot) { + internal ArchiWebHandler(Bot bot) { Bot = bot ?? throw new ArgumentNullException(nameof(bot)); CachedApiKey = new ArchiCacheable(ResolveApiKey); - WebBrowser = new WebBrowser(bot.ArchiLogger, ASF.GlobalConfig.WebProxy); + WebBrowser = new WebBrowser(bot.ArchiLogger, ASF.GlobalConfig?.WebProxy); } public void Dispose() { @@ -89,11 +89,12 @@ namespace ArchiSteamFarm { WebBrowser.Dispose(); } - [ItemCanBeNull] [PublicAPI] - public async Task GetAbsoluteProfileURL(bool waitForInitialization = true) { + public async Task GetAbsoluteProfileURL(bool waitForInitialization = true) { if (waitForInitialization && !Initialized) { - for (byte i = 0; (i < ASF.GlobalConfig.ConnectionTimeout) && !Initialized && Bot.IsConnectedAndLoggedOn; i++) { + byte connectionTimeout = ASF.GlobalConfig?.ConnectionTimeout ?? GlobalConfig.DefaultConnectionTimeout; + + for (byte i = 0; (i < connectionTimeout) && !Initialized && Bot.IsConnectedAndLoggedOn; i++) { await Task.Delay(1000).ConfigureAwait(false); } @@ -107,20 +108,17 @@ namespace ArchiSteamFarm { return string.IsNullOrEmpty(VanityURL) ? "/profiles/" + Bot.SteamID : "/id/" + VanityURL; } - [NotNull] [PublicAPI] public async IAsyncEnumerable GetInventoryAsync(ulong steamID = 0, uint appID = Steam.Asset.SteamAppID, ulong contextID = Steam.Asset.SteamCommunityContextID) { - if ((appID == 0) || (contextID == 0)) { - throw new ArgumentException(string.Format(Strings.ErrorObjectIsNull, nameof(appID) + " || " + nameof(contextID))); - } - - if (ASF.InventorySemaphore == null) { - throw new ArgumentNullException(nameof(ASF.InventorySemaphore)); + if ((appID == 0) || (contextID == 0) || (ASF.InventorySemaphore == null)) { + throw new ArgumentNullException(nameof(appID) + " || " + nameof(contextID) + " || " + nameof(ASF.InventorySemaphore)); } if (steamID == 0) { if (!Initialized) { - for (byte i = 0; (i < ASF.GlobalConfig.ConnectionTimeout) && !Initialized && Bot.IsConnectedAndLoggedOn; i++) { + byte connectionTimeout = ASF.GlobalConfig?.ConnectionTimeout ?? GlobalConfig.DefaultConnectionTimeout; + + for (byte i = 0; (i < connectionTimeout) && !Initialized && Bot.IsConnectedAndLoggedOn; i++) { await Task.Delay(1000).ConfigureAwait(false); } @@ -144,7 +142,7 @@ namespace ArchiSteamFarm { await ASF.InventorySemaphore.WaitAsync().ConfigureAwait(false); try { - Steam.InventoryResponse response = await UrlGetToJsonObjectWithSession(SteamCommunityURL, request + (startAssetID > 0 ? "&start_assetid=" + startAssetID : "")).ConfigureAwait(false); + Steam.InventoryResponse? response = await UrlGetToJsonObjectWithSession(SteamCommunityURL, request + (startAssetID > 0 ? "&start_assetid=" + startAssetID : "")).ConfigureAwait(false); if (response == null) { throw new HttpRequestException(string.Format(Strings.ErrorObjectIsNull, nameof(response))); @@ -180,7 +178,7 @@ namespace ArchiSteamFarm { } foreach (Steam.Asset asset in response.Assets.Where(asset => asset != null)) { - if (!descriptions.TryGetValue((asset.ClassID, asset.InstanceID), out Steam.InventoryResponse.Description description) || assetIDs.Contains(asset.AssetID)) { + if (!descriptions.TryGetValue((asset.ClassID, asset.InstanceID), out Steam.InventoryResponse.Description? description) || assetIDs.Contains(asset.AssetID)) { continue; } @@ -210,12 +208,14 @@ namespace ArchiSteamFarm { startAssetID = response.LastAssetID; } finally { - if (ASF.GlobalConfig.InventoryLimiterDelay == 0) { + byte inventoryLimiterDelay = ASF.GlobalConfig?.InventoryLimiterDelay ?? GlobalConfig.DefaultInventoryLimiterDelay; + + if (inventoryLimiterDelay == 0) { ASF.InventorySemaphore.Release(); } else { Utilities.InBackground( async () => { - await Task.Delay(ASF.GlobalConfig.InventoryLimiterDelay * 1000).ConfigureAwait(false); + await Task.Delay(inventoryLimiterDelay * 1000).ConfigureAwait(false); ASF.InventorySemaphore.Release(); } ); @@ -224,14 +224,13 @@ namespace ArchiSteamFarm { } } - [ItemCanBeNull] [PublicAPI] - public async Task> GetMyOwnedGames() { + public async Task?> GetMyOwnedGames() { const string request = "/my/games?l=english&xml=1"; - XmlDocument response = await UrlGetToXmlDocumentWithSession(SteamCommunityURL, request, false).ConfigureAwait(false); + XmlDocument? response = await UrlGetToXmlDocumentWithSession(SteamCommunityURL, request, false).ConfigureAwait(false); - using XmlNodeList xmlNodeList = response?.SelectNodes("gamesList/games/game"); + using XmlNodeList? xmlNodeList = response?.SelectNodes("gamesList/games/game"); if ((xmlNodeList == null) || (xmlNodeList.Count == 0)) { return null; @@ -239,7 +238,13 @@ namespace ArchiSteamFarm { Dictionary result = new Dictionary(xmlNodeList.Count); - foreach (XmlNode xmlNode in xmlNodeList) { + foreach (XmlNode? xmlNode in xmlNodeList) { + if (xmlNode == null) { + ASF.ArchiLogger.LogNullError(nameof(xmlNode)); + + return null; + } + XmlNode appNode = xmlNode.SelectSingleNode("appID"); if (appNode == null) { @@ -268,22 +273,19 @@ namespace ArchiSteamFarm { return result; } - [ItemCanBeNull] [PublicAPI] - public async Task> GetOwnedGames(ulong steamID) { + public async Task?> GetOwnedGames(ulong steamID) { if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { - Bot.ArchiLogger.LogNullError(nameof(steamID)); - - return null; + throw new ArgumentNullException(nameof(steamID)); } - (bool success, string steamApiKey) = await CachedApiKey.GetValue().ConfigureAwait(false); + (bool success, string? steamApiKey) = await CachedApiKey.GetValue().ConfigureAwait(false); if (!success || string.IsNullOrEmpty(steamApiKey)) { return null; } - KeyValue response = null; + KeyValue? response = null; for (byte i = 0; (i < WebBrowser.MaxTries) && (response == null); i++) { using WebAPI.AsyncInterface iPlayerService = Bot.SteamConfiguration.GetAsyncWebAPIInterface(IPlayerService); @@ -298,7 +300,7 @@ namespace ArchiSteamFarm { async () => await iPlayerService.CallAsync( HttpMethod.Get, "GetOwnedGames", args: new Dictionary(3, StringComparer.Ordinal) { { "include_appinfo", 1 }, - { "key", steamApiKey }, + { "key", steamApiKey! }, { "steamid", steamID } } ).ConfigureAwait(false) @@ -329,7 +331,15 @@ namespace ArchiSteamFarm { return null; } - result[appID] = game["name"].AsString(); + string? gameName = game["name"].AsString(); + + if (string.IsNullOrEmpty(gameName)) { + Bot.ArchiLogger.LogNullError(nameof(gameName)); + + return null; + } + + result[appID] = gameName!; } return result; @@ -337,17 +347,15 @@ namespace ArchiSteamFarm { [PublicAPI] public async Task HasValidApiKey() { - (bool success, string steamApiKey) = await CachedApiKey.GetValue().ConfigureAwait(false); + (bool success, string? steamApiKey) = await CachedApiKey.GetValue().ConfigureAwait(false); return success ? !string.IsNullOrEmpty(steamApiKey) : (bool?) null; } [PublicAPI] - public async Task<(bool Success, HashSet MobileTradeOfferIDs)> SendTradeOffer(ulong steamID, IReadOnlyCollection itemsToGive = null, IReadOnlyCollection itemsToReceive = null, string token = null, bool forcedSingleOffer = false) { + public async Task<(bool Success, HashSet? MobileTradeOfferIDs)> SendTradeOffer(ulong steamID, IReadOnlyCollection? itemsToGive = null, IReadOnlyCollection? itemsToReceive = null, string? token = null, bool forcedSingleOffer = false) { if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || (((itemsToGive == null) || (itemsToGive.Count == 0)) && ((itemsToReceive == null) || (itemsToReceive.Count == 0)))) { - Bot.ArchiLogger.LogNullError(nameof(steamID) + " || (" + nameof(itemsToGive) + " && " + nameof(itemsToReceive) + ")"); - - return (false, null); + throw new ArgumentNullException(nameof(steamID) + " || (" + nameof(itemsToGive) + " && " + nameof(itemsToReceive) + ")"); } Steam.TradeOfferSendRequest singleTrade = new Steam.TradeOfferSendRequest(); @@ -399,7 +407,7 @@ namespace ArchiSteamFarm { foreach (Steam.TradeOfferSendRequest trade in trades) { data["json_tradeoffer"] = JsonConvert.SerializeObject(trade); - Steam.TradeOfferSendResponse response = await UrlPostToJsonObjectWithSession(SteamCommunityURL, request, data, referer).ConfigureAwait(false); + Steam.TradeOfferSendResponse? response = await UrlPostToJsonObjectWithSession(SteamCommunityURL, request, data, referer).ConfigureAwait(false); if (response == null) { return (false, mobileTradeOfferIDs); @@ -414,11 +422,9 @@ namespace ArchiSteamFarm { } [PublicAPI] - public async Task UrlGetToHtmlDocumentWithSession(string host, string request, bool checkSessionPreemptively = true, byte maxTries = WebBrowser.MaxTries) { + public async Task UrlGetToHtmlDocumentWithSession(string host, string request, bool checkSessionPreemptively = true, byte maxTries = WebBrowser.MaxTries) { if (string.IsNullOrEmpty(host) || string.IsNullOrEmpty(request)) { - Bot.ArchiLogger.LogNullError(nameof(host) + " || " + nameof(request)); - - return null; + throw new ArgumentNullException(nameof(host) + " || " + nameof(request)); } if (maxTries == 0) { @@ -449,7 +455,9 @@ namespace ArchiSteamFarm { } if (!Initialized) { - for (byte i = 0; (i < ASF.GlobalConfig.ConnectionTimeout) && !Initialized && Bot.IsConnectedAndLoggedOn; i++) { + byte connectionTimeout = ASF.GlobalConfig?.ConnectionTimeout ?? GlobalConfig.DefaultConnectionTimeout; + + for (byte i = 0; (i < connectionTimeout) && !Initialized && Bot.IsConnectedAndLoggedOn; i++) { await Task.Delay(1000).ConfigureAwait(false); } @@ -461,7 +469,7 @@ namespace ArchiSteamFarm { } } - WebBrowser.HtmlDocumentResponse response = await WebLimitRequest(host, async () => await WebBrowser.UrlGetToHtmlDocument(host + request).ConfigureAwait(false)).ConfigureAwait(false); + WebBrowser.HtmlDocumentResponse? response = await WebLimitRequest(host, async () => await WebBrowser.UrlGetToHtmlDocument(host + request).ConfigureAwait(false)).ConfigureAwait(false); if (response == null) { return null; @@ -489,11 +497,9 @@ namespace ArchiSteamFarm { } [PublicAPI] - public async Task UrlGetToJsonObjectWithSession(string host, string request, bool checkSessionPreemptively = true, byte maxTries = WebBrowser.MaxTries) where T : class { + public async Task UrlGetToJsonObjectWithSession(string host, string request, bool checkSessionPreemptively = true, byte maxTries = WebBrowser.MaxTries) where T : class { if (string.IsNullOrEmpty(host) || string.IsNullOrEmpty(request)) { - Bot.ArchiLogger.LogNullError(nameof(host) + " || " + nameof(request)); - - return default; + throw new ArgumentNullException(nameof(host) + " || " + nameof(request)); } if (maxTries == 0) { @@ -524,7 +530,9 @@ namespace ArchiSteamFarm { } if (!Initialized) { - for (byte i = 0; (i < ASF.GlobalConfig.ConnectionTimeout) && !Initialized && Bot.IsConnectedAndLoggedOn; i++) { + byte connectionTimeout = ASF.GlobalConfig?.ConnectionTimeout ?? GlobalConfig.DefaultConnectionTimeout; + + for (byte i = 0; (i < connectionTimeout) && !Initialized && Bot.IsConnectedAndLoggedOn; i++) { await Task.Delay(1000).ConfigureAwait(false); } @@ -536,7 +544,7 @@ namespace ArchiSteamFarm { } } - WebBrowser.ObjectResponse response = await WebLimitRequest(host, async () => await WebBrowser.UrlGetToJsonObject(host + request).ConfigureAwait(false)).ConfigureAwait(false); + WebBrowser.ObjectResponse? response = await WebLimitRequest(host, async () => await WebBrowser.UrlGetToJsonObject(host + request).ConfigureAwait(false)).ConfigureAwait(false); if (response == null) { return default; @@ -564,11 +572,9 @@ namespace ArchiSteamFarm { } [PublicAPI] - public async Task UrlGetToXmlDocumentWithSession(string host, string request, bool checkSessionPreemptively = true, byte maxTries = WebBrowser.MaxTries) { + public async Task UrlGetToXmlDocumentWithSession(string host, string request, bool checkSessionPreemptively = true, byte maxTries = WebBrowser.MaxTries) { if (string.IsNullOrEmpty(host) || string.IsNullOrEmpty(request)) { - Bot.ArchiLogger.LogNullError(nameof(host) + " || " + nameof(request)); - - return null; + throw new ArgumentNullException(nameof(host) + " || " + nameof(request)); } if (maxTries == 0) { @@ -599,7 +605,9 @@ namespace ArchiSteamFarm { } if (!Initialized) { - for (byte i = 0; (i < ASF.GlobalConfig.ConnectionTimeout) && !Initialized && Bot.IsConnectedAndLoggedOn; i++) { + byte connectionTimeout = ASF.GlobalConfig?.ConnectionTimeout ?? GlobalConfig.DefaultConnectionTimeout; + + for (byte i = 0; (i < connectionTimeout) && !Initialized && Bot.IsConnectedAndLoggedOn; i++) { await Task.Delay(1000).ConfigureAwait(false); } @@ -611,7 +619,7 @@ namespace ArchiSteamFarm { } } - WebBrowser.XmlDocumentResponse response = await WebLimitRequest(host, async () => await WebBrowser.UrlGetToXmlDocument(host + request).ConfigureAwait(false)).ConfigureAwait(false); + WebBrowser.XmlDocumentResponse? response = await WebLimitRequest(host, async () => await WebBrowser.UrlGetToXmlDocument(host + request).ConfigureAwait(false)).ConfigureAwait(false); if (response == null) { return null; @@ -641,9 +649,7 @@ namespace ArchiSteamFarm { [PublicAPI] public async Task UrlHeadWithSession(string host, string request, bool checkSessionPreemptively = true, byte maxTries = WebBrowser.MaxTries) { if (string.IsNullOrEmpty(host) || string.IsNullOrEmpty(request)) { - Bot.ArchiLogger.LogNullError(nameof(host) + " || " + nameof(request)); - - return false; + throw new ArgumentNullException(nameof(host) + " || " + nameof(request)); } if (maxTries == 0) { @@ -674,7 +680,9 @@ namespace ArchiSteamFarm { } if (!Initialized) { - for (byte i = 0; (i < ASF.GlobalConfig.ConnectionTimeout) && !Initialized && Bot.IsConnectedAndLoggedOn; i++) { + byte connectionTimeout = ASF.GlobalConfig?.ConnectionTimeout ?? GlobalConfig.DefaultConnectionTimeout; + + for (byte i = 0; (i < connectionTimeout) && !Initialized && Bot.IsConnectedAndLoggedOn; i++) { await Task.Delay(1000).ConfigureAwait(false); } @@ -686,7 +694,7 @@ namespace ArchiSteamFarm { } } - WebBrowser.BasicResponse response = await WebLimitRequest(host, async () => await WebBrowser.UrlHead(host + request).ConfigureAwait(false)).ConfigureAwait(false); + WebBrowser.BasicResponse? response = await WebLimitRequest(host, async () => await WebBrowser.UrlHead(host + request).ConfigureAwait(false)).ConfigureAwait(false); if (response == null) { return false; @@ -714,11 +722,9 @@ namespace ArchiSteamFarm { } [PublicAPI] - public async Task UrlPostToHtmlDocumentWithSession(string host, string request, Dictionary data = null, string referer = null, ESession session = ESession.Lowercase, bool checkSessionPreemptively = true, byte maxTries = WebBrowser.MaxTries) { + public async Task UrlPostToHtmlDocumentWithSession(string host, string request, IDictionary? data = null, string? referer = null, ESession session = ESession.Lowercase, bool checkSessionPreemptively = true, byte maxTries = WebBrowser.MaxTries) { if (string.IsNullOrEmpty(host) || string.IsNullOrEmpty(request) || !Enum.IsDefined(typeof(ESession), session)) { - Bot.ArchiLogger.LogNullError(nameof(host) + " || " + nameof(request) + " || " + nameof(session)); - - return null; + throw new ArgumentNullException(nameof(host) + " || " + nameof(request) + " || " + nameof(session)); } if (maxTries == 0) { @@ -749,7 +755,9 @@ namespace ArchiSteamFarm { } if (!Initialized) { - for (byte i = 0; (i < ASF.GlobalConfig.ConnectionTimeout) && !Initialized && Bot.IsConnectedAndLoggedOn; i++) { + byte connectionTimeout = ASF.GlobalConfig?.ConnectionTimeout ?? GlobalConfig.DefaultConnectionTimeout; + + for (byte i = 0; (i < connectionTimeout) && !Initialized && Bot.IsConnectedAndLoggedOn; i++) { await Task.Delay(1000).ConfigureAwait(false); } @@ -762,7 +770,7 @@ namespace ArchiSteamFarm { } if (session != ESession.None) { - string sessionID = WebBrowser.CookieContainer.GetCookieValue(host, "sessionid"); + string? sessionID = WebBrowser.CookieContainer.GetCookieValue(host, "sessionid"); if (string.IsNullOrEmpty(sessionID)) { Bot.ArchiLogger.LogNullError(nameof(sessionID)); @@ -770,35 +778,21 @@ namespace ArchiSteamFarm { return null; } - string sessionName; - - switch (session) { - case ESession.CamelCase: - sessionName = "sessionID"; - - break; - case ESession.Lowercase: - sessionName = "sessionid"; - - break; - case ESession.PascalCase: - sessionName = "SessionID"; - - break; - default: - Bot.ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(session), session)); - - return null; - } + string sessionName = session switch { + ESession.CamelCase => "sessionID", + ESession.Lowercase => "sessionid", + ESession.PascalCase => "SessionID", + _ => throw new ArgumentOutOfRangeException(nameof(session)) + }; if (data != null) { - data[sessionName] = sessionID; + data[sessionName] = sessionID!; } else { - data = new Dictionary(1, StringComparer.Ordinal) { { sessionName, sessionID } }; + data = new Dictionary(1, StringComparer.Ordinal) { { sessionName, sessionID! } }; } } - WebBrowser.HtmlDocumentResponse response = await WebLimitRequest(host, async () => await WebBrowser.UrlPostToHtmlDocument(host + request, data, referer).ConfigureAwait(false)).ConfigureAwait(false); + WebBrowser.HtmlDocumentResponse? response = await WebLimitRequest(host, async () => await WebBrowser.UrlPostToHtmlDocument(host + request, data, referer).ConfigureAwait(false)).ConfigureAwait(false); if (response == null) { return null; @@ -826,11 +820,9 @@ namespace ArchiSteamFarm { } [PublicAPI] - public async Task UrlPostToJsonObjectWithSession(string host, string request, Dictionary data = null, string referer = null, ESession session = ESession.Lowercase, bool checkSessionPreemptively = true, byte maxTries = WebBrowser.MaxTries) where T : class { + public async Task UrlPostToJsonObjectWithSession(string host, string request, IDictionary? data = null, string? referer = null, ESession session = ESession.Lowercase, bool checkSessionPreemptively = true, byte maxTries = WebBrowser.MaxTries) where T : class { if (string.IsNullOrEmpty(host) || string.IsNullOrEmpty(request) || !Enum.IsDefined(typeof(ESession), session)) { - Bot.ArchiLogger.LogNullError(nameof(host) + " || " + nameof(request) + " || " + nameof(session)); - - return null; + throw new ArgumentNullException(nameof(host) + " || " + nameof(request) + " || " + nameof(session)); } if (maxTries == 0) { @@ -861,7 +853,9 @@ namespace ArchiSteamFarm { } if (!Initialized) { - for (byte i = 0; (i < ASF.GlobalConfig.ConnectionTimeout) && !Initialized && Bot.IsConnectedAndLoggedOn; i++) { + byte connectionTimeout = ASF.GlobalConfig?.ConnectionTimeout ?? GlobalConfig.DefaultConnectionTimeout; + + for (byte i = 0; (i < connectionTimeout) && !Initialized && Bot.IsConnectedAndLoggedOn; i++) { await Task.Delay(1000).ConfigureAwait(false); } @@ -874,7 +868,7 @@ namespace ArchiSteamFarm { } if (session != ESession.None) { - string sessionID = WebBrowser.CookieContainer.GetCookieValue(host, "sessionid"); + string? sessionID = WebBrowser.CookieContainer.GetCookieValue(host, "sessionid"); if (string.IsNullOrEmpty(sessionID)) { Bot.ArchiLogger.LogNullError(nameof(sessionID)); @@ -882,35 +876,21 @@ namespace ArchiSteamFarm { return null; } - string sessionName; - - switch (session) { - case ESession.CamelCase: - sessionName = "sessionID"; - - break; - case ESession.Lowercase: - sessionName = "sessionid"; - - break; - case ESession.PascalCase: - sessionName = "SessionID"; - - break; - default: - Bot.ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(session), session)); - - return null; - } + string sessionName = session switch { + ESession.CamelCase => "sessionID", + ESession.Lowercase => "sessionid", + ESession.PascalCase => "SessionID", + _ => throw new ArgumentOutOfRangeException(nameof(session)) + }; if (data != null) { - data[sessionName] = sessionID; + data[sessionName] = sessionID!; } else { - data = new Dictionary(1, StringComparer.Ordinal) { { sessionName, sessionID } }; + data = new Dictionary(1, StringComparer.Ordinal) { { sessionName, sessionID! } }; } } - WebBrowser.ObjectResponse response = await WebLimitRequest(host, async () => await WebBrowser.UrlPostToJsonObject>(host + request, data, referer).ConfigureAwait(false)).ConfigureAwait(false); + WebBrowser.ObjectResponse? response = await WebLimitRequest(host, async () => await WebBrowser.UrlPostToJsonObject>(host + request, data, referer).ConfigureAwait(false)).ConfigureAwait(false); if (response == null) { return null; @@ -938,11 +918,9 @@ namespace ArchiSteamFarm { } [PublicAPI] - public async Task UrlPostToJsonObjectWithSession(string host, string request, List> data = null, string referer = null, ESession session = ESession.Lowercase, bool checkSessionPreemptively = true, byte maxTries = WebBrowser.MaxTries) where T : class { + public async Task UrlPostToJsonObjectWithSession(string host, string request, ICollection>? data = null, string? referer = null, ESession session = ESession.Lowercase, bool checkSessionPreemptively = true, byte maxTries = WebBrowser.MaxTries) where T : class { if (string.IsNullOrEmpty(host) || string.IsNullOrEmpty(request) || !Enum.IsDefined(typeof(ESession), session)) { - Bot.ArchiLogger.LogNullError(nameof(host) + " || " + nameof(request) + " || " + nameof(session)); - - return null; + throw new ArgumentNullException(nameof(host) + " || " + nameof(request) + " || " + nameof(session)); } if (maxTries == 0) { @@ -973,7 +951,9 @@ namespace ArchiSteamFarm { } if (!Initialized) { - for (byte i = 0; (i < ASF.GlobalConfig.ConnectionTimeout) && !Initialized && Bot.IsConnectedAndLoggedOn; i++) { + byte connectionTimeout = ASF.GlobalConfig?.ConnectionTimeout ?? GlobalConfig.DefaultConnectionTimeout; + + for (byte i = 0; (i < connectionTimeout) && !Initialized && Bot.IsConnectedAndLoggedOn; i++) { await Task.Delay(1000).ConfigureAwait(false); } @@ -986,7 +966,7 @@ namespace ArchiSteamFarm { } if (session != ESession.None) { - string sessionID = WebBrowser.CookieContainer.GetCookieValue(host, "sessionid"); + string? sessionID = WebBrowser.CookieContainer.GetCookieValue(host, "sessionid"); if (string.IsNullOrEmpty(sessionID)) { Bot.ArchiLogger.LogNullError(nameof(sessionID)); @@ -994,28 +974,14 @@ namespace ArchiSteamFarm { return null; } - string sessionName; + string sessionName = session switch { + ESession.CamelCase => "sessionID", + ESession.Lowercase => "sessionid", + ESession.PascalCase => "SessionID", + _ => throw new ArgumentOutOfRangeException(nameof(session)) + }; - switch (session) { - case ESession.CamelCase: - sessionName = "sessionID"; - - break; - case ESession.Lowercase: - sessionName = "sessionid"; - - break; - case ESession.PascalCase: - sessionName = "SessionID"; - - break; - default: - Bot.ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(session), session)); - - return null; - } - - KeyValuePair sessionValue = new KeyValuePair(sessionName, sessionID); + KeyValuePair sessionValue = new KeyValuePair(sessionName, sessionID!); if (data != null) { data.Remove(sessionValue); @@ -1025,7 +991,7 @@ namespace ArchiSteamFarm { } } - WebBrowser.ObjectResponse response = await WebLimitRequest(host, async () => await WebBrowser.UrlPostToJsonObject>>(host + request, data, referer).ConfigureAwait(false)).ConfigureAwait(false); + WebBrowser.ObjectResponse? response = await WebLimitRequest(host, async () => await WebBrowser.UrlPostToJsonObject>>(host + request, data, referer).ConfigureAwait(false)).ConfigureAwait(false); if (response == null) { return null; @@ -1053,11 +1019,9 @@ namespace ArchiSteamFarm { } [PublicAPI] - public async Task UrlPostWithSession(string host, string request, Dictionary data = null, string referer = null, ESession session = ESession.Lowercase, bool checkSessionPreemptively = true, byte maxTries = WebBrowser.MaxTries) { + public async Task UrlPostWithSession(string host, string request, IDictionary? data = null, string? referer = null, ESession session = ESession.Lowercase, bool checkSessionPreemptively = true, byte maxTries = WebBrowser.MaxTries) { if (string.IsNullOrEmpty(host) || string.IsNullOrEmpty(request) || !Enum.IsDefined(typeof(ESession), session)) { - Bot.ArchiLogger.LogNullError(nameof(host) + " || " + nameof(request) + " || " + nameof(session)); - - return false; + throw new ArgumentNullException(nameof(host) + " || " + nameof(request) + " || " + nameof(session)); } if (maxTries == 0) { @@ -1088,7 +1052,9 @@ namespace ArchiSteamFarm { } if (!Initialized) { - for (byte i = 0; (i < ASF.GlobalConfig.ConnectionTimeout) && !Initialized && Bot.IsConnectedAndLoggedOn; i++) { + byte connectionTimeout = ASF.GlobalConfig?.ConnectionTimeout ?? GlobalConfig.DefaultConnectionTimeout; + + for (byte i = 0; (i < connectionTimeout) && !Initialized && Bot.IsConnectedAndLoggedOn; i++) { await Task.Delay(1000).ConfigureAwait(false); } @@ -1101,7 +1067,7 @@ namespace ArchiSteamFarm { } if (session != ESession.None) { - string sessionID = WebBrowser.CookieContainer.GetCookieValue(host, "sessionid"); + string? sessionID = WebBrowser.CookieContainer.GetCookieValue(host, "sessionid"); if (string.IsNullOrEmpty(sessionID)) { Bot.ArchiLogger.LogNullError(nameof(sessionID)); @@ -1109,35 +1075,21 @@ namespace ArchiSteamFarm { return false; } - string sessionName; - - switch (session) { - case ESession.CamelCase: - sessionName = "sessionID"; - - break; - case ESession.Lowercase: - sessionName = "sessionid"; - - break; - case ESession.PascalCase: - sessionName = "SessionID"; - - break; - default: - Bot.ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(session), session)); - - return false; - } + string sessionName = session switch { + ESession.CamelCase => "sessionID", + ESession.Lowercase => "sessionid", + ESession.PascalCase => "SessionID", + _ => throw new ArgumentOutOfRangeException(nameof(session)) + }; if (data != null) { - data[sessionName] = sessionID; + data[sessionName] = sessionID!; } else { - data = new Dictionary(1, StringComparer.Ordinal) { { sessionName, sessionID } }; + data = new Dictionary(1, StringComparer.Ordinal) { { sessionName, sessionID! } }; } } - WebBrowser.BasicResponse response = await WebLimitRequest(host, async () => await WebBrowser.UrlPost(host + request, data, referer).ConfigureAwait(false)).ConfigureAwait(false); + WebBrowser.BasicResponse? response = await WebLimitRequest(host, async () => await WebBrowser.UrlPost(host + request, data, referer).ConfigureAwait(false)).ConfigureAwait(false); if (response == null) { return false; @@ -1165,20 +1117,14 @@ namespace ArchiSteamFarm { } [PublicAPI] - public static async Task WebLimitRequest(string service, Func> function) { - if (string.IsNullOrEmpty(service) || (function == null)) { - ASF.ArchiLogger.LogNullError(nameof(service) + " || " + nameof(function)); - - return default; + public static async Task WebLimitRequest(string service, Func> function) where T : class { + if (string.IsNullOrEmpty(service) || (function == null) || (ASF.WebLimitingSemaphores == null)) { + throw new ArgumentNullException(nameof(service) + " || " + nameof(function) + " || " + nameof(ASF.WebLimitingSemaphores)); } - if (ASF.WebLimitingSemaphores == null) { - ASF.ArchiLogger.LogNullError(nameof(ASF.WebLimitingSemaphores)); + ushort webLimiterDelay = ASF.GlobalConfig?.WebLimiterDelay ?? GlobalConfig.DefaultWebLimiterDelay; - return default; - } - - if (ASF.GlobalConfig.WebLimiterDelay == 0) { + if (webLimiterDelay == 0) { return await function().ConfigureAwait(false); } @@ -1202,7 +1148,7 @@ namespace ArchiSteamFarm { // We release rate-limiter semaphore regardless of our task completion, since we use that one only to guarantee rate-limiting of their creation Utilities.InBackground( async () => { - await Task.Delay(ASF.GlobalConfig.WebLimiterDelay).ConfigureAwait(false); + await Task.Delay(webLimiterDelay).ConfigureAwait(false); limiters.RateLimitingSemaphore.Release(); } ); @@ -1216,9 +1162,7 @@ namespace ArchiSteamFarm { internal async Task AcceptDigitalGiftCard(ulong giftCardID) { if (giftCardID == 0) { - Bot.ArchiLogger.LogNullError(nameof(giftCardID)); - - return false; + throw new ArgumentNullException(nameof(giftCardID)); } const string request = "/gifts/0/resolvegiftcard"; @@ -1229,7 +1173,7 @@ namespace ArchiSteamFarm { { "giftcardid", giftCardID.ToString() } }; - Steam.EResultResponse response = await UrlPostToJsonObjectWithSession(SteamStoreURL, request, data).ConfigureAwait(false); + Steam.EResultResponse? response = await UrlPostToJsonObjectWithSession(SteamStoreURL, request, data).ConfigureAwait(false); if (response == null) { return false; @@ -1246,9 +1190,7 @@ namespace ArchiSteamFarm { internal async Task<(bool Success, bool RequiresMobileConfirmation)> AcceptTradeOffer(ulong tradeID) { if (tradeID == 0) { - Bot.ArchiLogger.LogNullError(nameof(tradeID)); - - return (false, false); + throw new ArgumentNullException(nameof(tradeID)); } string request = "/tradeoffer/" + tradeID + "/accept"; @@ -1260,16 +1202,14 @@ namespace ArchiSteamFarm { { "tradeofferid", tradeID.ToString() } }; - Steam.TradeOfferAcceptResponse response = await UrlPostToJsonObjectWithSession(SteamCommunityURL, request, data, referer).ConfigureAwait(false); + Steam.TradeOfferAcceptResponse? response = await UrlPostToJsonObjectWithSession(SteamCommunityURL, request, data, referer).ConfigureAwait(false); return response != null ? (true, response.RequiresMobileConfirmation) : (false, false); } internal async Task AddFreeLicense(uint subID) { if (subID == 0) { - Bot.ArchiLogger.LogNullError(nameof(subID)); - - return false; + throw new ArgumentNullException(nameof(subID)); } const string request = "/checkout/addfreelicense"; @@ -1280,19 +1220,17 @@ namespace ArchiSteamFarm { { "subid", subID.ToString() } }; - using IDocument htmlDocument = await UrlPostToHtmlDocumentWithSession(SteamStoreURL, request, data).ConfigureAwait(false); + using IDocument? htmlDocument = await UrlPostToHtmlDocumentWithSession(SteamStoreURL, request, data).ConfigureAwait(false); return htmlDocument?.SelectSingleNode("//div[@class='add_free_content_success_area']") != null; } internal async Task ChangePrivacySettings(Steam.UserPrivacy userPrivacy) { if (userPrivacy == null) { - Bot.ArchiLogger.LogNullError(nameof(userPrivacy)); - - return false; + throw new ArgumentNullException(nameof(userPrivacy)); } - string profileURL = await GetAbsoluteProfileURL().ConfigureAwait(false); + string? profileURL = await GetAbsoluteProfileURL().ConfigureAwait(false); if (string.IsNullOrEmpty(profileURL)) { Bot.ArchiLogger.LogGenericWarning(Strings.WarningFailed); @@ -1308,7 +1246,7 @@ namespace ArchiSteamFarm { { "Privacy", JsonConvert.SerializeObject(userPrivacy.Settings) } }; - Steam.EResultResponse response = await UrlPostToJsonObjectWithSession(SteamCommunityURL, request, data).ConfigureAwait(false); + Steam.EResultResponse? response = await UrlPostToJsonObjectWithSession(SteamCommunityURL, request, data).ConfigureAwait(false); if (response == null) { return false; @@ -1325,9 +1263,7 @@ namespace ArchiSteamFarm { internal async Task ClearFromDiscoveryQueue(uint appID) { if (appID == 0) { - Bot.ArchiLogger.LogNullError(nameof(appID)); - - return false; + throw new ArgumentNullException(nameof(appID)); } string request = "/app/" + appID; @@ -1340,18 +1276,16 @@ namespace ArchiSteamFarm { internal async Task DeclineTradeOffer(ulong tradeID) { if (tradeID == 0) { - Bot.ArchiLogger.LogNullError(nameof(tradeID)); - - return false; + throw new ArgumentNullException(nameof(tradeID)); } - (bool success, string steamApiKey) = await CachedApiKey.GetValue().ConfigureAwait(false); + (bool success, string? steamApiKey) = await CachedApiKey.GetValue().ConfigureAwait(false); if (!success || string.IsNullOrEmpty(steamApiKey)) { return false; } - KeyValue response = null; + KeyValue? response = null; for (byte i = 0; (i < WebBrowser.MaxTries) && (response == null); i++) { using WebAPI.AsyncInterface iEconService = Bot.SteamConfiguration.GetAsyncWebAPIInterface(IEconService); @@ -1365,7 +1299,7 @@ namespace ArchiSteamFarm { // ReSharper disable once AccessToDisposedClosure async () => await iEconService.CallAsync( HttpMethod.Post, "DeclineTradeOffer", args: new Dictionary(2, StringComparer.Ordinal) { - { "key", steamApiKey }, + { "key", steamApiKey! }, { "tradeofferid", tradeID } } ).ConfigureAwait(false) @@ -1386,30 +1320,27 @@ namespace ArchiSteamFarm { return true; } - [NotNull] internal HttpClient GenerateDisposableHttpClient() => WebBrowser.GenerateDisposableHttpClient(); - [ItemCanBeNull] - internal async Task> GenerateNewDiscoveryQueue() { + internal async Task?> GenerateNewDiscoveryQueue() { const string request = "/explore/generatenewdiscoveryqueue"; // Extra entry for sessionID Dictionary data = new Dictionary(2, StringComparer.Ordinal) { { "queuetype", "0" } }; - Steam.NewDiscoveryQueueResponse output = await UrlPostToJsonObjectWithSession(SteamStoreURL, request, data).ConfigureAwait(false); + Steam.NewDiscoveryQueueResponse? output = await UrlPostToJsonObjectWithSession(SteamStoreURL, request, data).ConfigureAwait(false); return output?.Queue; } - [ItemCanBeNull] - internal async Task> GetActiveTradeOffers() { - (bool success, string steamApiKey) = await CachedApiKey.GetValue().ConfigureAwait(false); + internal async Task?> GetActiveTradeOffers() { + (bool success, string? steamApiKey) = await CachedApiKey.GetValue().ConfigureAwait(false); if (!success || string.IsNullOrEmpty(steamApiKey)) { return null; } - KeyValue response = null; + KeyValue? response = null; for (byte i = 0; (i < WebBrowser.MaxTries) && (response == null); i++) { using WebAPI.AsyncInterface iEconService = Bot.SteamConfiguration.GetAsyncWebAPIInterface(IEconService); @@ -1426,7 +1357,7 @@ namespace ArchiSteamFarm { { "active_only", 1 }, { "get_descriptions", 1 }, { "get_received_offers", 1 }, - { "key", steamApiKey }, + { "key", steamApiKey! }, { "time_historical_cutoff", uint.MaxValue } } ).ConfigureAwait(false) @@ -1485,7 +1416,7 @@ namespace ArchiSteamFarm { HashSet parsedTags = new HashSet(tags.Count); foreach (KeyValue tag in tags) { - string identifier = tag["category"].AsString(); + string? identifier = tag["category"].AsString(); if (string.IsNullOrEmpty(identifier)) { Bot.ArchiLogger.LogNullError(nameof(identifier)); @@ -1493,7 +1424,7 @@ namespace ArchiSteamFarm { return null; } - string value = tag["internal_name"].AsString(); + string? value = tag["internal_name"].AsString(); if (string.IsNullOrEmpty(value)) { Bot.ArchiLogger.LogNullError(nameof(value)); @@ -1501,7 +1432,7 @@ namespace ArchiSteamFarm { return null; } - parsedTags.Add(new Steam.Asset.Tag(identifier, value)); + parsedTags.Add(new Steam.Asset.Tag(identifier!, value!)); } parsedDescription.Tags = parsedTags.ToImmutableHashSet(); @@ -1569,9 +1500,8 @@ namespace ArchiSteamFarm { return result; } - [ItemCanBeNull] - internal async Task> GetAppList() { - KeyValue response = null; + internal async Task?> GetAppList() { + KeyValue? response = null; for (byte i = 0; (i < WebBrowser.MaxTries) && (response == null); i++) { using WebAPI.AsyncInterface iSteamApps = Bot.SteamConfiguration.GetAsyncWebAPIInterface(ISteamApps); @@ -1621,11 +1551,9 @@ namespace ArchiSteamFarm { return result; } - internal async Task GetBadgePage(byte page) { + internal async Task GetBadgePage(byte page) { if (page == 0) { - Bot.ArchiLogger.LogNullError(nameof(page)); - - return null; + throw new ArgumentNullException(nameof(page)); } string request = "/my/badges?l=english&p=" + page; @@ -1633,15 +1561,15 @@ namespace ArchiSteamFarm { return await UrlGetToHtmlDocumentWithSession(SteamCommunityURL, request, false).ConfigureAwait(false); } - internal async Task GetConfirmations(string deviceID, string confirmationHash, uint time) { + internal async Task GetConfirmations(string deviceID, string confirmationHash, uint time) { if (string.IsNullOrEmpty(deviceID) || string.IsNullOrEmpty(confirmationHash) || (time == 0)) { - Bot.ArchiLogger.LogNullError(nameof(deviceID) + " || " + nameof(confirmationHash) + " || " + nameof(time)); - - return null; + throw new ArgumentNullException(nameof(deviceID) + " || " + nameof(confirmationHash) + " || " + nameof(time)); } if (!Initialized) { - for (byte i = 0; (i < ASF.GlobalConfig.ConnectionTimeout) && !Initialized && Bot.IsConnectedAndLoggedOn; i++) { + byte connectionTimeout = ASF.GlobalConfig?.ConnectionTimeout ?? GlobalConfig.DefaultConnectionTimeout; + + for (byte i = 0; (i < connectionTimeout) && !Initialized && Bot.IsConnectedAndLoggedOn; i++) { await Task.Delay(1000).ConfigureAwait(false); } @@ -1657,11 +1585,10 @@ namespace ArchiSteamFarm { return await UrlGetToHtmlDocumentWithSession(SteamCommunityURL, request).ConfigureAwait(false); } - [ItemCanBeNull] - internal async Task> GetDigitalGiftCards() { + internal async Task?> GetDigitalGiftCards() { const string request = "/gifts"; - using IDocument response = await UrlGetToHtmlDocumentWithSession(SteamStoreURL, request).ConfigureAwait(false); + using IDocument? response = await UrlGetToHtmlDocumentWithSession(SteamStoreURL, request).ConfigureAwait(false); if (response == null) { return null; @@ -1675,7 +1602,7 @@ namespace ArchiSteamFarm { HashSet results = new HashSet(htmlNodes.Count); - foreach (string giftCardIDText in htmlNodes.Select(node => node.GetAttributeValue("id"))) { + foreach (string? giftCardIDText in htmlNodes.Select(node => node.GetAttribute("id"))) { if (string.IsNullOrEmpty(giftCardIDText)) { Bot.ArchiLogger.LogNullError(nameof(giftCardIDText)); @@ -1700,17 +1627,16 @@ namespace ArchiSteamFarm { return results; } - internal async Task GetDiscoveryQueuePage() { + internal async Task GetDiscoveryQueuePage() { const string request = "/explore?l=english"; return await UrlGetToHtmlDocumentWithSession(SteamStoreURL, request).ConfigureAwait(false); } - [ItemCanBeNull] - internal async Task> GetFamilySharingSteamIDs() { + internal async Task?> GetFamilySharingSteamIDs() { const string request = "/account/managedevices?l=english"; - using IDocument htmlDocument = await UrlGetToHtmlDocumentWithSession(SteamStoreURL, request).ConfigureAwait(false); + using IDocument? htmlDocument = await UrlGetToHtmlDocumentWithSession(SteamStoreURL, request).ConfigureAwait(false); if (htmlDocument == null) { return null; @@ -1725,7 +1651,7 @@ namespace ArchiSteamFarm { HashSet result = new HashSet(htmlNodes.Count); - foreach (string miniProfile in htmlNodes.Select(htmlNode => htmlNode.GetAttributeValue("data-miniprofile"))) { + foreach (string? miniProfile in htmlNodes.Select(htmlNode => htmlNode.GetAttribute("data-miniprofile"))) { if (string.IsNullOrEmpty(miniProfile)) { Bot.ArchiLogger.LogNullError(nameof(miniProfile)); @@ -1745,11 +1671,9 @@ namespace ArchiSteamFarm { return result; } - internal async Task GetGameCardsPage(uint appID) { + internal async Task GetGameCardsPage(uint appID) { if (appID == 0) { - Bot.ArchiLogger.LogNullError(nameof(appID)); - - return null; + throw new ArgumentNullException(nameof(appID)); } string request = "/my/gamecards/" + appID + "?l=english"; @@ -1758,7 +1682,7 @@ namespace ArchiSteamFarm { } internal async Task GetServerTime() { - KeyValue response = null; + KeyValue? response = null; for (byte i = 0; (i < WebBrowser.MaxTries) && (response == null); i++) { using WebAPI.AsyncInterface iTwoFactorService = Bot.SteamConfiguration.GetAsyncWebAPIInterface(ITwoFactorService); @@ -1798,16 +1722,14 @@ namespace ArchiSteamFarm { internal async Task GetTradeHoldDurationForTrade(ulong tradeID) { if (tradeID == 0) { - Bot.ArchiLogger.LogNullError(nameof(tradeID)); - - return null; + throw new ArgumentNullException(nameof(tradeID)); } string request = "/tradeoffer/" + tradeID + "?l=english"; - using IDocument htmlDocument = await UrlGetToHtmlDocumentWithSession(SteamCommunityURL, request).ConfigureAwait(false); + using IDocument? htmlDocument = await UrlGetToHtmlDocumentWithSession(SteamCommunityURL, request).ConfigureAwait(false); - IElement htmlNode = htmlDocument?.SelectSingleNode("//div[@class='pagecontent']/script"); + IElement? htmlNode = htmlDocument?.SelectSingleNode("//div[@class='pagecontent']/script"); if (htmlNode == null) { // Trade can be no longer valid @@ -1853,31 +1775,27 @@ namespace ArchiSteamFarm { return result; } - internal async Task GetTradeHoldDurationForUser(ulong steamID, string tradeToken = null) { + internal async Task GetTradeHoldDurationForUser(ulong steamID, string? tradeToken = null) { if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { - Bot.ArchiLogger.LogNullError(nameof(steamID)); - - return null; + throw new ArgumentNullException(nameof(steamID)); } - (bool success, string steamApiKey) = await CachedApiKey.GetValue().ConfigureAwait(false); + (bool success, string? steamApiKey) = await CachedApiKey.GetValue().ConfigureAwait(false); if (!success || string.IsNullOrEmpty(steamApiKey)) { return null; } - bool hasTradeToken = !string.IsNullOrEmpty(tradeToken); - - Dictionary arguments = new Dictionary(hasTradeToken ? 3 : 2, StringComparer.Ordinal) { - { "key", steamApiKey }, + Dictionary arguments = new Dictionary(!string.IsNullOrEmpty(tradeToken) ? 3 : 2, StringComparer.Ordinal) { + { "key", steamApiKey! }, { "steamid_target", steamID } }; - if (hasTradeToken) { - arguments["trade_offer_access_token"] = tradeToken; + if (!string.IsNullOrEmpty(tradeToken)) { + arguments["trade_offer_access_token"] = tradeToken!; } - KeyValue response = null; + KeyValue? response = null; for (byte i = 0; (i < WebBrowser.MaxTries) && (response == null); i++) { using WebAPI.AsyncInterface iEconService = Bot.SteamConfiguration.GetAsyncWebAPIInterface(IEconService); @@ -1917,13 +1835,13 @@ namespace ArchiSteamFarm { internal async Task HandleConfirmation(string deviceID, string confirmationHash, uint time, ulong confirmationID, ulong confirmationKey, bool accept) { if (string.IsNullOrEmpty(deviceID) || string.IsNullOrEmpty(confirmationHash) || (time == 0) || (confirmationID == 0) || (confirmationKey == 0)) { - Bot.ArchiLogger.LogNullError(nameof(deviceID) + " || " + nameof(confirmationHash) + " || " + nameof(time) + " || " + nameof(confirmationID) + " || " + nameof(confirmationKey)); - - return null; + throw new ArgumentNullException(nameof(deviceID) + " || " + nameof(confirmationHash) + " || " + nameof(time) + " || " + nameof(confirmationID) + " || " + nameof(confirmationKey)); } if (!Initialized) { - for (byte i = 0; (i < ASF.GlobalConfig.ConnectionTimeout) && !Initialized && Bot.IsConnectedAndLoggedOn; i++) { + byte connectionTimeout = ASF.GlobalConfig?.ConnectionTimeout ?? GlobalConfig.DefaultConnectionTimeout; + + for (byte i = 0; (i < connectionTimeout) && !Initialized && Bot.IsConnectedAndLoggedOn; i++) { await Task.Delay(1000).ConfigureAwait(false); } @@ -1936,20 +1854,20 @@ namespace ArchiSteamFarm { string request = "/mobileconf/ajaxop?a=" + Bot.SteamID + "&cid=" + confirmationID + "&ck=" + confirmationKey + "&k=" + WebUtility.UrlEncode(confirmationHash) + "&l=english&m=android&op=" + (accept ? "allow" : "cancel") + "&p=" + WebUtility.UrlEncode(deviceID) + "&t=" + time + "&tag=conf"; - Steam.BooleanResponse response = await UrlGetToJsonObjectWithSession(SteamCommunityURL, request).ConfigureAwait(false); + Steam.BooleanResponse? response = await UrlGetToJsonObjectWithSession(SteamCommunityURL, request).ConfigureAwait(false); return response?.Success; } internal async Task HandleConfirmations(string deviceID, string confirmationHash, uint time, IReadOnlyCollection confirmations, bool accept) { if (string.IsNullOrEmpty(deviceID) || string.IsNullOrEmpty(confirmationHash) || (time == 0) || (confirmations == null) || (confirmations.Count == 0)) { - Bot.ArchiLogger.LogNullError(nameof(deviceID) + " || " + nameof(confirmationHash) + " || " + nameof(time) + " || " + nameof(confirmations)); - - return null; + throw new ArgumentNullException(nameof(deviceID) + " || " + nameof(confirmationHash) + " || " + nameof(time) + " || " + nameof(confirmations)); } if (!Initialized) { - for (byte i = 0; (i < ASF.GlobalConfig.ConnectionTimeout) && !Initialized && Bot.IsConnectedAndLoggedOn; i++) { + byte connectionTimeout = ASF.GlobalConfig?.ConnectionTimeout ?? GlobalConfig.DefaultConnectionTimeout; + + for (byte i = 0; (i < connectionTimeout) && !Initialized && Bot.IsConnectedAndLoggedOn; i++) { await Task.Delay(1000).ConfigureAwait(false); } @@ -1978,19 +1896,17 @@ namespace ArchiSteamFarm { data.Add(new KeyValuePair("ck[]", confirmation.Key.ToString())); } - Steam.BooleanResponse response = await UrlPostToJsonObjectWithSession(SteamCommunityURL, request, data).ConfigureAwait(false); + Steam.BooleanResponse? response = await UrlPostToJsonObjectWithSession(SteamCommunityURL, request, data).ConfigureAwait(false); return response?.Success; } - internal async Task Init(ulong steamID, EUniverse universe, string webAPIUserNonce, string parentalCode = null) { + internal async Task Init(ulong steamID, EUniverse universe, string webAPIUserNonce, string? parentalCode = null) { if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || (universe == EUniverse.Invalid) || !Enum.IsDefined(typeof(EUniverse), universe) || string.IsNullOrEmpty(webAPIUserNonce)) { - Bot.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(universe) + " || " + nameof(webAPIUserNonce)); - - return false; + throw new ArgumentNullException(nameof(steamID) + " || " + nameof(universe) + " || " + nameof(webAPIUserNonce)); } - byte[] publicKey = KeyDictionary.GetPublicKey(universe); + byte[]? publicKey = KeyDictionary.GetPublicKey(universe); if ((publicKey == null) || (publicKey.Length == 0)) { Bot.ArchiLogger.LogNullError(nameof(publicKey)); @@ -2017,7 +1933,7 @@ namespace ArchiSteamFarm { // We're now ready to send the data to Steam API Bot.ArchiLogger.LogGenericInfo(string.Format(Strings.LoggingIn, ISteamUserAuth)); - KeyValue response; + KeyValue? response; // We do not use usual retry pattern here as webAPIUserNonce is valid only for a single request // Even during timeout, webAPIUserNonce is most likely already invalid @@ -2053,7 +1969,7 @@ namespace ArchiSteamFarm { return false; } - string steamLogin = response["token"].AsString(); + string? steamLogin = response["token"].AsString(); if (string.IsNullOrEmpty(steamLogin)) { Bot.ArchiLogger.LogNullError(nameof(steamLogin)); @@ -2061,7 +1977,7 @@ namespace ArchiSteamFarm { return false; } - string steamLoginSecure = response["tokensecure"].AsString(); + string? steamLoginSecure = response["tokensecure"].AsString(); if (string.IsNullOrEmpty(steamLoginSecure)) { Bot.ArchiLogger.LogNullError(nameof(steamLoginSecure)); @@ -2107,9 +2023,7 @@ namespace ArchiSteamFarm { internal async Task JoinGroup(ulong groupID) { if ((groupID == 0) || !new SteamID(groupID).IsClanAccount) { - Bot.ArchiLogger.LogNullError(nameof(groupID)); - - return false; + throw new ArgumentNullException(nameof(groupID)); } string request = "/gid/" + groupID; @@ -2122,9 +2036,7 @@ namespace ArchiSteamFarm { internal async Task MarkInventory() { if (ASF.InventorySemaphore == null) { - Bot.ArchiLogger.LogNullError(nameof(ASF.InventorySemaphore)); - - return; + throw new ArgumentNullException(nameof(ASF.InventorySemaphore)); } // We aim to have a maximum of 2 tasks, one already working, and one waiting in the queue @@ -2147,12 +2059,14 @@ namespace ArchiSteamFarm { const string request = "/my/inventory"; await UrlHeadWithSession(SteamCommunityURL, request, false).ConfigureAwait(false); } finally { - if (ASF.GlobalConfig.InventoryLimiterDelay == 0) { + byte inventoryLimiterDelay = ASF.GlobalConfig?.InventoryLimiterDelay ?? GlobalConfig.DefaultInventoryLimiterDelay; + + if (inventoryLimiterDelay == 0) { ASF.InventorySemaphore.Release(); } else { Utilities.InBackground( async () => { - await Task.Delay(ASF.GlobalConfig.InventoryLimiterDelay * 1000).ConfigureAwait(false); + await Task.Delay(inventoryLimiterDelay * 1000).ConfigureAwait(false); ASF.InventorySemaphore.Release(); } ); @@ -2171,13 +2085,11 @@ namespace ArchiSteamFarm { Utilities.InBackground(CachedApiKey.Reset); } - internal void OnVanityURLChanged(string vanityURL = null) => VanityURL = !string.IsNullOrEmpty(vanityURL) ? vanityURL : null; + internal void OnVanityURLChanged(string? vanityURL = null) => VanityURL = !string.IsNullOrEmpty(vanityURL) ? vanityURL : null; internal async Task<(EResult Result, EPurchaseResultDetail? PurchaseResult)?> RedeemWalletKey(string key) { if (string.IsNullOrEmpty(key)) { - Bot.ArchiLogger.LogNullError(nameof(key)); - - return null; + throw new ArgumentNullException(nameof(key)); } // ASF should redeem wallet key only in case of existing wallet @@ -2192,7 +2104,7 @@ namespace ArchiSteamFarm { // Extra entry for sessionID Dictionary data = new Dictionary(2, StringComparer.Ordinal) { { "wallet_code", key } }; - Steam.RedeemWalletResponse responseValidateCode = await UrlPostToJsonObjectWithSession(SteamStoreURL, requestValidateCode, data).ConfigureAwait(false); + Steam.RedeemWalletResponse? responseValidateCode = await UrlPostToJsonObjectWithSession(SteamStoreURL, requestValidateCode, data).ConfigureAwait(false); if (responseValidateCode == null) { return null; @@ -2211,7 +2123,7 @@ namespace ArchiSteamFarm { if (responseValidateCode.WalletCurrencyCode != responseValidateCode.KeyDetails.CurrencyCode) { const string requestCheckFunds = "/account/createwalletandcheckfunds"; - Steam.EResultResponse responseCheckFunds = await UrlPostToJsonObjectWithSession(SteamStoreURL, requestCheckFunds, data).ConfigureAwait(false); + Steam.EResultResponse? responseCheckFunds = await UrlPostToJsonObjectWithSession(SteamStoreURL, requestCheckFunds, data).ConfigureAwait(false); if (responseCheckFunds == null) { return null; @@ -2223,7 +2135,7 @@ namespace ArchiSteamFarm { } const string requestConfirmRedeem = "/account/confirmredeemwalletcode"; - Steam.RedeemWalletResponse responseConfirmRedeem = await UrlPostToJsonObjectWithSession(SteamStoreURL, requestConfirmRedeem, data).ConfigureAwait(false); + Steam.RedeemWalletResponse? responseConfirmRedeem = await UrlPostToJsonObjectWithSession(SteamStoreURL, requestConfirmRedeem, data).ConfigureAwait(false); if (responseConfirmRedeem == null) { return null; @@ -2235,12 +2147,10 @@ namespace ArchiSteamFarm { internal async Task UnpackBooster(uint appID, ulong itemID) { if ((appID == 0) || (itemID == 0)) { - Bot.ArchiLogger.LogNullError(nameof(appID) + " || " + nameof(itemID)); - - return false; + throw new ArgumentNullException(nameof(appID) + " || " + nameof(itemID)); } - string profileURL = await GetAbsoluteProfileURL().ConfigureAwait(false); + string? profileURL = await GetAbsoluteProfileURL().ConfigureAwait(false); if (string.IsNullOrEmpty(profileURL)) { Bot.ArchiLogger.LogGenericWarning(Strings.WarningFailed); @@ -2256,16 +2166,16 @@ namespace ArchiSteamFarm { { "communityitemid", itemID.ToString() } }; - Steam.EResultResponse response = await UrlPostToJsonObjectWithSession(SteamCommunityURL, request, data).ConfigureAwait(false); + Steam.EResultResponse? response = await UrlPostToJsonObjectWithSession(SteamCommunityURL, request, data).ConfigureAwait(false); return response?.Result == EResult.OK; } - private async Task<(ESteamApiKeyState State, string Key)> GetApiKeyState() { + private async Task<(ESteamApiKeyState State, string? Key)> GetApiKeyState() { const string request = "/dev/apikey?l=english"; - using IDocument htmlDocument = await UrlGetToHtmlDocumentWithSession(SteamCommunityURL, request).ConfigureAwait(false); + using IDocument? htmlDocument = await UrlGetToHtmlDocumentWithSession(SteamCommunityURL, request).ConfigureAwait(false); - IElement titleNode = htmlDocument?.SelectSingleNode("//div[@id='mainContents']/h2"); + IElement? titleNode = htmlDocument?.SelectSingleNode("//div[@id='mainContents']/h2"); if (titleNode == null) { return (ESteamApiKeyState.Timeout, null); @@ -2283,7 +2193,7 @@ namespace ArchiSteamFarm { return (ESteamApiKeyState.AccessDenied, null); } - IElement htmlNode = htmlDocument.SelectSingleNode("//div[@id='bodyContents_ex']/p"); + IElement? htmlNode = htmlDocument!.SelectSingleNode("//div[@id='bodyContents_ex']/p"); if (htmlNode == null) { Bot.ArchiLogger.LogNullError(nameof(htmlNode)); @@ -2332,12 +2242,10 @@ namespace ArchiSteamFarm { private async Task IsProfileUri(Uri uri, bool waitForInitialization = true) { if (uri == null) { - ASF.ArchiLogger.LogNullError(nameof(uri)); - - return false; + throw new ArgumentNullException(nameof(uri)); } - string profileURL = await GetAbsoluteProfileURL(waitForInitialization).ConfigureAwait(false); + string? profileURL = await GetAbsoluteProfileURL(waitForInitialization).ConfigureAwait(false); if (string.IsNullOrEmpty(profileURL)) { Bot.ArchiLogger.LogGenericWarning(Strings.WarningFailed); @@ -2372,7 +2280,7 @@ namespace ArchiSteamFarm { const string host = SteamStoreURL; const string request = "/account"; - WebBrowser.BasicResponse response = await WebLimitRequest(host, async () => await WebBrowser.UrlHead(host + request).ConfigureAwait(false)).ConfigureAwait(false); + WebBrowser.BasicResponse? response = await WebLimitRequest(host, async () => await WebBrowser.UrlHead(host + request).ConfigureAwait(false)).ConfigureAwait(false); if (response?.FinalUri == null) { return null; @@ -2398,9 +2306,7 @@ namespace ArchiSteamFarm { private static bool IsSessionExpiredUri(Uri uri) { if (uri == null) { - ASF.ArchiLogger.LogNullError(nameof(uri)); - - return false; + throw new ArgumentNullException(nameof(uri)); } return uri.AbsolutePath.StartsWith("/login", StringComparison.Ordinal) || uri.Host.Equals("lostauth"); @@ -2408,9 +2314,7 @@ namespace ArchiSteamFarm { private static bool ParseItems(IReadOnlyDictionary<(uint AppID, ulong ClassID, ulong InstanceID), Steam.InventoryResponse.Description> descriptions, IReadOnlyCollection input, ICollection output) { if ((descriptions == null) || (input == null) || (input.Count == 0) || (output == null)) { - ASF.ArchiLogger.LogNullError(nameof(descriptions) + " || " + nameof(input) + " || " + nameof(output)); - - return false; + throw new ArgumentNullException(nameof(descriptions) + " || " + nameof(input) + " || " + nameof(output)); } foreach (KeyValue item in input) { @@ -2454,12 +2358,12 @@ namespace ArchiSteamFarm { bool marketable = true; bool tradable = true; - ImmutableHashSet tags = null; + ImmutableHashSet? tags = null; uint realAppID = 0; Steam.Asset.EType type = Steam.Asset.EType.Unknown; Steam.Asset.ERarity rarity = Steam.Asset.ERarity.Unknown; - if (descriptions.TryGetValue(key, out Steam.InventoryResponse.Description description)) { + if (descriptions.TryGetValue(key, out Steam.InventoryResponse.Description? description)) { marketable = description.Marketable; tradable = description.Tradable; tags = description.Tags; @@ -2529,13 +2433,13 @@ namespace ArchiSteamFarm { return await UrlPostWithSession(SteamCommunityURL, request, data).ConfigureAwait(false); } - private async Task<(bool Success, string Result)> ResolveApiKey() { + private async Task<(bool Success, string? Result)> ResolveApiKey() { if (Bot.IsAccountLimited) { // API key is permanently unavailable for limited accounts return (true, null); } - (ESteamApiKeyState State, string Key) result = await GetApiKeyState().ConfigureAwait(false); + (ESteamApiKeyState State, string? Key) result = await GetApiKeyState().ConfigureAwait(false); switch (result.State) { case ESteamApiKeyState.AccessDenied: @@ -2581,9 +2485,7 @@ namespace ArchiSteamFarm { private async Task UnlockParentalAccount(string parentalCode) { if (string.IsNullOrEmpty(parentalCode)) { - Bot.ArchiLogger.LogNullError(nameof(parentalCode)); - - return false; + throw new ArgumentNullException(nameof(parentalCode)); } Bot.ArchiLogger.LogGenericInfo(Strings.UnlockingParentalAccount); @@ -2603,9 +2505,7 @@ namespace ArchiSteamFarm { private async Task UnlockParentalAccountForService(string serviceURL, string parentalCode, byte maxTries = WebBrowser.MaxTries) { if (string.IsNullOrEmpty(serviceURL) || string.IsNullOrEmpty(parentalCode)) { - Bot.ArchiLogger.LogNullError(nameof(serviceURL) + " || " + nameof(parentalCode)); - - return false; + throw new ArgumentNullException(nameof(serviceURL) + " || " + nameof(parentalCode)); } const string request = "/parental/ajaxunlock"; @@ -2617,7 +2517,7 @@ namespace ArchiSteamFarm { return false; } - string sessionID = WebBrowser.CookieContainer.GetCookieValue(serviceURL, "sessionid"); + string? sessionID = WebBrowser.CookieContainer.GetCookieValue(serviceURL, "sessionid"); if (string.IsNullOrEmpty(sessionID)) { Bot.ArchiLogger.LogNullError(nameof(sessionID)); @@ -2627,11 +2527,11 @@ namespace ArchiSteamFarm { Dictionary data = new Dictionary(2, StringComparer.Ordinal) { { "pin", parentalCode }, - { "sessionid", sessionID } + { "sessionid", sessionID! } }; // This request doesn't go through UrlPostRetryWithSession as we have no access to session refresh capability (this is in fact session initialization) - WebBrowser.BasicResponse response = await WebLimitRequest(serviceURL, async () => await WebBrowser.UrlPost(serviceURL + request, data, serviceURL).ConfigureAwait(false)).ConfigureAwait(false); + WebBrowser.BasicResponse? response = await WebLimitRequest(serviceURL, async () => await WebBrowser.UrlPost(serviceURL + request, data, serviceURL).ConfigureAwait(false)).ConfigureAwait(false); if ((response == null) || IsSessionExpiredUri(response.FinalUri)) { // There is no session refresh capability at this stage diff --git a/ArchiSteamFarm/Bot.cs b/ArchiSteamFarm/Bot.cs index e1f7d1dab..7e440f187 100755 --- a/ArchiSteamFarm/Bot.cs +++ b/ArchiSteamFarm/Bot.cs @@ -56,10 +56,10 @@ namespace ArchiSteamFarm { private const byte ReservedMessageLength = 2; // 2 for 2x optional … [PublicAPI] - public static IReadOnlyDictionary BotsReadOnly => Bots; + public static IReadOnlyDictionary? BotsReadOnly => Bots; - internal static ConcurrentDictionary Bots { get; private set; } - internal static StringComparer BotsComparer { get; private set; } + internal static ConcurrentDictionary? Bots { get; private set; } + 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); @@ -116,7 +116,6 @@ namespace ArchiSteamFarm { [PublicAPI] public bool IsPlayingPossible => !PlayingBlocked && !LibraryLocked; - [NotNull] [JsonIgnore] [PublicAPI] public IReadOnlyCollection OwnedPackageIDsReadOnly => OwnedPackageIDs.Keys.ToHashSet(); @@ -135,7 +134,7 @@ namespace ArchiSteamFarm { private readonly SemaphoreSlim InitializationSemaphore = new SemaphoreSlim(1, 1); private readonly SemaphoreSlim MessagingSemaphore = new SemaphoreSlim(1, 1); private readonly ConcurrentDictionary PastNotifications = new ConcurrentDictionary(); - private readonly Statistics Statistics; + private readonly Statistics? Statistics; private readonly SteamClient SteamClient; private readonly ConcurrentHashSet SteamFamilySharingIDs = new ConcurrentHashSet(); private readonly SteamUser SteamUser; @@ -143,8 +142,12 @@ namespace ArchiSteamFarm { private IEnumerable<(string FilePath, EFileType FileType)> RelatedFiles { get { - foreach (EFileType fileType in Enum.GetValues(typeof(EFileType))) { - string filePath = GetFilePath(fileType); + foreach (EFileType? fileType in Enum.GetValues(typeof(EFileType))) { + if (fileType == null) { + continue; + } + + string filePath = GetFilePath(fileType.Value); if (string.IsNullOrEmpty(filePath)) { ArchiLogger.LogNullError(nameof(filePath)); @@ -152,14 +155,14 @@ namespace ArchiSteamFarm { yield break; } - yield return (filePath, fileType); + yield return (filePath, fileType.Value); } } } #pragma warning disable IDE0051 [JsonProperty(PropertyName = SharedInfo.UlongCompatibilityStringPrefix + nameof(SteamID))] - [NotNull] + private string SSteamID => SteamID.ToString(); #pragma warning restore IDE0051 @@ -177,7 +180,7 @@ namespace ArchiSteamFarm { [JsonProperty] [PublicAPI] - public string Nickname { get; private set; } + public string? Nickname { get; private set; } [JsonProperty] [PublicAPI] @@ -199,33 +202,33 @@ namespace ArchiSteamFarm { internal bool PlayingBlocked { get; private set; } internal bool PlayingWasBlocked { get; private set; } - private string AuthCode; + private string? AuthCode; #pragma warning disable IDE0052 [JsonProperty] - private string AvatarHash; + private string? AvatarHash; #pragma warning restore IDE0052 - private Timer ConnectionFailureTimer; + private Timer? ConnectionFailureTimer; private bool FirstTradeSent; - private Timer GamesRedeemerInBackgroundTimer; + private Timer? GamesRedeemerInBackgroundTimer; private byte HeartBeatFailures; private byte InvalidPasswordFailures; private EResult LastLogOnResult; private DateTime LastLogonSessionReplaced; private bool LibraryLocked; private ulong MasterChatGroupID; - private Timer PlayingWasBlockedTimer; + private Timer? PlayingWasBlockedTimer; private bool ReconnectOnUserInitiated; - private Timer SendItemsTimer; + private Timer? SendItemsTimer; private bool SteamParentalActive = true; - private SteamSaleEvent SteamSaleEvent; - private string TwoFactorCode; + private SteamSaleEvent? SteamSaleEvent; + private string? TwoFactorCode; private byte TwoFactorCodeFailures; - private Bot([NotNull] string botName, [NotNull] BotConfig botConfig, [NotNull] BotDatabase botDatabase) { - if (string.IsNullOrEmpty(botName) || (botConfig == null) || (botDatabase == null)) { - throw new ArgumentNullException(nameof(botName) + " || " + nameof(botConfig) + " || " + nameof(botDatabase)); + 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 = botName; @@ -234,9 +237,7 @@ namespace ArchiSteamFarm { ArchiLogger = new ArchiLogger(botName); - if (HasMobileAuthenticator) { - BotDatabase.MobileAuthenticator.Init(this); - } + BotDatabase.MobileAuthenticator?.Init(this); ArchiWebHandler = new ArchiWebHandler(this); @@ -256,7 +257,7 @@ namespace ArchiSteamFarm { } } - SteamUnifiedMessages steamUnifiedMessages = SteamClient.GetHandler(); + SteamUnifiedMessages? steamUnifiedMessages = SteamClient.GetHandler(); ArchiHandler = new ArchiHandler(ArchiLogger, steamUnifiedMessages ?? throw new ArgumentNullException(nameof(steamUnifiedMessages))); SteamClient.AddHandler(ArchiHandler); @@ -265,17 +266,17 @@ namespace ArchiSteamFarm { CallbackManager.Subscribe(OnConnected); CallbackManager.Subscribe(OnDisconnected); - SteamApps = SteamClient.GetHandler(); + SteamApps = SteamClient.GetHandler() ?? throw new ArgumentNullException(nameof(SteamApps)); CallbackManager.Subscribe(OnGuestPassList); CallbackManager.Subscribe(OnLicenseList); - SteamFriends = SteamClient.GetHandler(); + SteamFriends = SteamClient.GetHandler() ?? throw new ArgumentNullException(nameof(SteamFriends)); CallbackManager.Subscribe(OnFriendsList); CallbackManager.Subscribe(OnPersonaState); CallbackManager.Subscribe(OnServiceMethod); - SteamUser = SteamClient.GetHandler(); + SteamUser = SteamClient.GetHandler() ?? throw new ArgumentNullException(nameof(SteamUser)); CallbackManager.Subscribe(OnLoggedOff); CallbackManager.Subscribe(OnLoggedOn); CallbackManager.Subscribe(OnLoginKey); @@ -307,6 +308,7 @@ namespace ArchiSteamFarm { public async ValueTask DisposeAsync() { // Those are objects that are always being created if constructor doesn't throw exception ArchiWebHandler.Dispose(); + BotDatabase.Dispose(); CallbackSemaphore.Dispose(); GamesRedeemerInBackgroundSemaphore.Dispose(); InitializationSemaphore.Dispose(); @@ -318,8 +320,6 @@ namespace ArchiSteamFarm { await HeartBeatTimer.DisposeAsync().ConfigureAwait(false); // Those are objects that might be null and the check should be in-place - BotDatabase?.Dispose(); - if (ConnectionFailureTimer != null) { await ConnectionFailureTimer.DisposeAsync().ConfigureAwait(false); } @@ -346,14 +346,12 @@ namespace ArchiSteamFarm { } [PublicAPI] - public static Bot GetBot(string botName) { - if (string.IsNullOrEmpty(botName)) { - ASF.ArchiLogger.LogNullError(nameof(botName)); - - return null; + public static Bot? GetBot(string botName) { + if (string.IsNullOrEmpty(botName) || (Bots == null)) { + throw new ArgumentNullException(nameof(botName) + " || " + nameof(Bots)); } - if (Bots.TryGetValue(botName, out Bot targetBot)) { + if (Bots.TryGetValue(botName, out Bot? targetBot)) { return targetBot; } @@ -365,11 +363,9 @@ namespace ArchiSteamFarm { } [PublicAPI] - public static HashSet GetBots(string args) { + public static HashSet? GetBots(string args) { if (string.IsNullOrEmpty(args)) { - ASF.ArchiLogger.LogNullError(nameof(args)); - - return null; + throw new ArgumentNullException(nameof(args)); } string[] botNames = args.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); @@ -388,10 +384,10 @@ namespace ArchiSteamFarm { string[] botRange = botName.Split(new[] { ".." }, StringSplitOptions.RemoveEmptyEntries); if (botRange.Length == 2) { - Bot firstBot = GetBot(botRange[0]); + Bot? firstBot = GetBot(botRange[0]); if (firstBot != null) { - Bot lastBot = GetBot(botRange[1]); + Bot? lastBot = GetBot(botRange[1]); if (lastBot != null) { foreach (Bot bot in Bots.OrderBy(bot => bot.Key, BotsComparer).Select(bot => bot.Value).SkipWhile(bot => bot != firstBot)) { @@ -435,7 +431,7 @@ namespace ArchiSteamFarm { continue; } - Bot singleBot = GetBot(botName); + Bot? singleBot = GetBot(botName); if (singleBot == null) { continue; @@ -449,10 +445,8 @@ namespace ArchiSteamFarm { [PublicAPI] public async Task GetTradeHoldDuration(ulong steamID, ulong tradeID) { - if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || (tradeID == 0)) { - ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(tradeID)); - - return null; + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || (tradeID == 0) || (Bots == null)) { + throw new ArgumentNullException(nameof(steamID) + " || " + nameof(tradeID) + " || " + nameof(Bots)); } if (SteamFriends.GetFriendRelationship(steamID) == EFriendRelationship.Friend) { @@ -463,10 +457,10 @@ namespace ArchiSteamFarm { } } - Bot targetBot = Bots.Values.FirstOrDefault(bot => bot.SteamID == steamID); + Bot? targetBot = Bots.Values.FirstOrDefault(bot => bot.SteamID == steamID); if (targetBot?.IsConnectedAndLoggedOn == true) { - string targetTradeToken = await targetBot.ArchiHandler.GetTradeToken().ConfigureAwait(false); + string? targetTradeToken = await targetBot.ArchiHandler.GetTradeToken().ConfigureAwait(false); if (!string.IsNullOrEmpty(targetTradeToken)) { byte? tradeHoldDurationForUser = await ArchiWebHandler.GetTradeHoldDurationForUser(steamID, targetTradeToken).ConfigureAwait(false); @@ -482,10 +476,8 @@ namespace ArchiSteamFarm { [PublicAPI] public bool HasPermission(ulong steamID, BotConfig.EPermission permission) { - if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || (permission == BotConfig.EPermission.None)) { - ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(permission)); - - return false; + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || (permission == BotConfig.EPermission.None) || !Enum.IsDefined(typeof(BotConfig.EPermission), permission)) { + throw new ArgumentNullException(nameof(steamID) + " || " + nameof(permission)); } if (ASF.IsOwner(steamID)) { @@ -501,26 +493,16 @@ namespace ArchiSteamFarm { [PublicAPI] public bool SetUserInput(ASF.EUserInputType inputType, string inputValue) { if ((inputType == ASF.EUserInputType.None) || !Enum.IsDefined(typeof(ASF.EUserInputType), inputType) || string.IsNullOrEmpty(inputValue)) { - ArchiLogger.LogNullError(nameof(inputType) + " || " + nameof(inputValue)); - - return false; + throw new ArgumentNullException(nameof(inputType) + " || " + nameof(inputValue)); } // This switch should cover ONLY bot properties switch (inputType) { case ASF.EUserInputType.Login: - if (BotConfig == null) { - return false; - } - BotConfig.SteamLogin = inputValue; break; case ASF.EUserInputType.Password: - if (BotConfig == null) { - return false; - } - BotConfig.DecryptedSteamPassword = inputValue; break; @@ -533,7 +515,7 @@ namespace ArchiSteamFarm { break; case ASF.EUserInputType.SteamParentalCode: - if ((BotConfig == null) || (inputValue.Length != BotConfig.SteamParentalCodeLength)) { + if (inputValue.Length != BotConfig.SteamParentalCodeLength) { return false; } @@ -549,9 +531,7 @@ namespace ArchiSteamFarm { break; default: - ASF.ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(inputType), inputType)); - - break; + throw new ArgumentOutOfRangeException(nameof(inputType)); } if (RequiredInput == inputType) { @@ -563,9 +543,7 @@ namespace ArchiSteamFarm { internal void AddGamesToRedeemInBackground(IOrderedDictionary gamesToRedeemInBackground) { if ((gamesToRedeemInBackground == null) || (gamesToRedeemInBackground.Count == 0)) { - ArchiLogger.LogNullError(nameof(gamesToRedeemInBackground)); - - return; + throw new ArgumentNullException(nameof(gamesToRedeemInBackground)); } BotDatabase.AddGamesToRedeemInBackground(gamesToRedeemInBackground); @@ -633,9 +611,7 @@ namespace ArchiSteamFarm { internal static string FormatBotResponse(string response, string botName) { if (string.IsNullOrEmpty(response) || string.IsNullOrEmpty(botName)) { - ASF.ArchiLogger.LogNullError(nameof(response) + " || " + nameof(botName)); - - return null; + throw new ArgumentNullException(nameof(response) + " || " + nameof(botName)); } return Environment.NewLine + "<" + botName + "> " + response; @@ -648,7 +624,7 @@ namespace ArchiSteamFarm { return (0, DateTime.MaxValue, true); } - HashSet packageIDs = ASF.GlobalDatabase.GetPackageIDs(appID, OwnedPackageIDs.Keys); + HashSet? packageIDs = ASF.GlobalDatabase?.GetPackageIDs(appID, OwnedPackageIDs.Keys); if ((packageIDs == null) || (packageIDs.Count == 0)) { return (0, DateTime.MaxValue, true); @@ -662,7 +638,7 @@ namespace ArchiSteamFarm { continue; } - if (IsRefundable(packageData.PaymentMethod) && (packageData.TimeCreated > mostRecent)) { + if ((packageData.PaymentMethod > EPaymentMethod.None) && IsRefundable(packageData.PaymentMethod) && (packageData.TimeCreated > mostRecent)) { mostRecent = packageData.TimeCreated; } } @@ -676,7 +652,7 @@ namespace ArchiSteamFarm { } } - SteamApps.PICSTokensCallback tokenCallback = null; + SteamApps.PICSTokensCallback? tokenCallback = null; for (byte i = 0; (i < WebBrowser.MaxTries) && (tokenCallback == null) && IsConnectedAndLoggedOn; i++) { try { @@ -692,7 +668,7 @@ namespace ArchiSteamFarm { SteamApps.PICSRequest request = new SteamApps.PICSRequest(appID, tokenCallback.AppTokens.TryGetValue(appID, out ulong accessToken) ? accessToken : 0, false); - AsyncJobMultiple.ResultSet productInfoResultSet = null; + AsyncJobMultiple.ResultSet? productInfoResultSet = null; for (byte i = 0; (i < WebBrowser.MaxTries) && (productInfoResultSet == null) && IsConnectedAndLoggedOn; i++) { try { @@ -707,7 +683,7 @@ namespace ArchiSteamFarm { } foreach (Dictionary productInfoApps in productInfoResultSet.Results.Select(result => result.Apps)) { - if (!productInfoApps.TryGetValue(appID, out SteamApps.PICSProductInfoCallback.PICSProductInfo productInfoApp)) { + if (!productInfoApps.TryGetValue(appID, out SteamApps.PICSProductInfoCallback.PICSProductInfo? productInfoApp)) { continue; } @@ -725,11 +701,11 @@ namespace ArchiSteamFarm { continue; } - string releaseState = commonProductInfo["ReleaseState"].Value; + string? releaseState = commonProductInfo["ReleaseState"].AsString(); if (!string.IsNullOrEmpty(releaseState)) { // We must convert this to uppercase, since Valve doesn't stick to any convention and we can have a case mismatch - switch (releaseState.ToUpperInvariant()) { + switch (releaseState!.ToUpperInvariant()) { case "RELEASED": break; case "PRELOADONLY": @@ -742,14 +718,14 @@ namespace ArchiSteamFarm { } } - string type = commonProductInfo["type"].Value; + string? type = commonProductInfo["type"].AsString(); if (string.IsNullOrEmpty(type)) { return (appID, DateTime.MinValue, true); } // We must convert this to uppercase, since Valve doesn't stick to any convention and we can have a case mismatch - switch (type.ToUpperInvariant()) { + switch (type!.ToUpperInvariant()) { case "APPLICATION": case "EPISODE": case "GAME": @@ -778,13 +754,13 @@ namespace ArchiSteamFarm { return (0, DateTime.MinValue, true); } - string listOfDlc = productInfo["extended"]["listofdlc"].Value; + string? listOfDlc = productInfo["extended"]["listofdlc"].AsString(); if (string.IsNullOrEmpty(listOfDlc)) { return (appID, DateTime.MinValue, true); } - string[] dlcAppIDsTexts = listOfDlc.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + string[] dlcAppIDsTexts = listOfDlc!.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); foreach (string dlcAppIDsText in dlcAppIDsTexts) { if (!uint.TryParse(dlcAppIDsText, out uint dlcAppID) || (dlcAppID == 0)) { @@ -808,44 +784,28 @@ namespace ArchiSteamFarm { internal static string GetFilePath(string botName, EFileType fileType) { if (string.IsNullOrEmpty(botName) || !Enum.IsDefined(typeof(EFileType), fileType)) { - ASF.ArchiLogger.LogNullError(nameof(botName) + " || " + nameof(fileType)); - - return null; + throw new ArgumentNullException(nameof(botName) + " || " + nameof(fileType)); } string botPath = Path.Combine(SharedInfo.ConfigDirectory, botName); - switch (fileType) { - case EFileType.Config: - return botPath + SharedInfo.JsonConfigExtension; - case EFileType.Database: - return botPath + SharedInfo.DatabaseExtension; - case EFileType.KeysToRedeem: - return botPath + SharedInfo.KeysExtension; - case EFileType.KeysToRedeemUnused: - return botPath + SharedInfo.KeysExtension + SharedInfo.KeysUnusedExtension; - case EFileType.KeysToRedeemUsed: - return botPath + SharedInfo.KeysExtension + SharedInfo.KeysUsedExtension; - case EFileType.MobileAuthenticator: - return botPath + SharedInfo.MobileAuthenticatorExtension; - case EFileType.SentryFile: - return botPath + SharedInfo.SentryHashExtension; - default: - ASF.ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(fileType), fileType)); - - return null; - } + return fileType switch { + EFileType.Config => botPath + SharedInfo.JsonConfigExtension, + EFileType.Database => botPath + SharedInfo.DatabaseExtension, + EFileType.KeysToRedeem => botPath + SharedInfo.KeysExtension, + EFileType.KeysToRedeemUnused => botPath + SharedInfo.KeysExtension + SharedInfo.KeysUnusedExtension, + EFileType.KeysToRedeemUsed => botPath + SharedInfo.KeysExtension + SharedInfo.KeysUsedExtension, + EFileType.MobileAuthenticator => botPath + SharedInfo.MobileAuthenticatorExtension, + EFileType.SentryFile => botPath + SharedInfo.SentryHashExtension, + _ => throw new ArgumentOutOfRangeException(nameof(fileType)) + }; } - [ItemCanBeNull] - internal async Task> GetMarketableAppIDs() => await ArchiWebHandler.GetAppList().ConfigureAwait(false); + internal async Task?> GetMarketableAppIDs() => await ArchiWebHandler.GetAppList().ConfigureAwait(false); - [ItemCanBeNull] - internal async Task AppIDs)>> GetPackagesData(IReadOnlyCollection packageIDs) { - if ((packageIDs == null) || (packageIDs.Count == 0)) { - ArchiLogger.LogNullError(nameof(packageIDs)); - - return null; + internal async Task? AppIDs)>?> GetPackagesData(IReadOnlyCollection packageIDs) { + if ((packageIDs == null) || (packageIDs.Count == 0) || (ASF.GlobalDatabase == null)) { + throw new ArgumentNullException(nameof(packageIDs) + " || " + nameof(ASF.GlobalDatabase)); } HashSet packageRequests = new HashSet(); @@ -859,10 +819,10 @@ namespace ArchiSteamFarm { } if (packageRequests.Count == 0) { - return new Dictionary AppIDs)>(0); + return new Dictionary? AppIDs)>(0); } - AsyncJobMultiple.ResultSet productInfoResultSet = null; + AsyncJobMultiple.ResultSet? productInfoResultSet = null; for (byte i = 0; (i < WebBrowser.MaxTries) && (productInfoResultSet == null) && IsConnectedAndLoggedOn; i++) { try { @@ -876,7 +836,7 @@ namespace ArchiSteamFarm { return null; } - Dictionary AppIDs)> result = new Dictionary AppIDs)>(); + Dictionary? AppIDs)> result = new Dictionary? AppIDs)>(); 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) { @@ -885,7 +845,7 @@ namespace ArchiSteamFarm { return null; } - (uint ChangeNumber, HashSet AppIDs) value = (productInfo.ChangeNumber, null); + (uint ChangeNumber, HashSet? AppIDs) value = (productInfo.ChangeNumber, null); try { KeyValue appIDs = productInfo.KeyValues["appids"]; @@ -896,7 +856,7 @@ namespace ArchiSteamFarm { value.AppIDs = new HashSet(appIDs.Children.Count); - foreach (string appIDText in appIDs.Children.Select(app => app.Value)) { + foreach (string? appIDText in appIDs.Children.Select(app => app.Value)) { if (!uint.TryParse(appIDText, out uint appID) || (appID == 0)) { ArchiLogger.LogNullError(nameof(appID)); @@ -913,26 +873,10 @@ namespace ArchiSteamFarm { return result; } - internal async Task<(Dictionary UnusedKeys, Dictionary UsedKeys)> GetUsedAndUnusedKeys() { - string unusedKeysFilePath = GetFilePath(EFileType.KeysToRedeemUnused); + internal async Task<(Dictionary? UnusedKeys, Dictionary? UsedKeys)> GetUsedAndUnusedKeys() { + string[] files = { GetFilePath(EFileType.KeysToRedeemUnused), GetFilePath(EFileType.KeysToRedeemUsed) }; - if (string.IsNullOrEmpty(unusedKeysFilePath)) { - ASF.ArchiLogger.LogNullError(nameof(unusedKeysFilePath)); - - return (null, null); - } - - string usedKeysFilePath = GetFilePath(EFileType.KeysToRedeemUsed); - - if (string.IsNullOrEmpty(usedKeysFilePath)) { - ASF.ArchiLogger.LogNullError(nameof(usedKeysFilePath)); - - return (null, null); - } - - string[] files = { unusedKeysFilePath, usedKeysFilePath }; - - IList> results = await Utilities.InParallel(files.Select(GetKeysFromFile)).ConfigureAwait(false); + IList?> results = await Utilities.InParallel(files.Select(GetKeysFromFile)).ConfigureAwait(false); return (results[0], results[1]); } @@ -942,7 +886,7 @@ namespace ArchiSteamFarm { return null; } - CPrivacySettings privacySettings = await ArchiHandler.GetPrivacySettings().ConfigureAwait(false); + CPrivacySettings? privacySettings = await ArchiHandler.GetPrivacySettings().ConfigureAwait(false); if (privacySettings == null) { ArchiLogger.LogGenericWarning(Strings.WarningFailed); @@ -955,12 +899,10 @@ namespace ArchiSteamFarm { internal async Task IdleGame(CardsFarmer.Game game) { if (game == null) { - ArchiLogger.LogNullError(nameof(game)); - - return; + throw new ArgumentNullException(nameof(game)); } - string gameName = null; + string? gameName = null; if (!string.IsNullOrEmpty(BotConfig.CustomGamePlayedWhileFarming)) { gameName = string.Format(BotConfig.CustomGamePlayedWhileFarming, game.AppID, game.GameName); @@ -971,12 +913,10 @@ namespace ArchiSteamFarm { internal async Task IdleGames(IReadOnlyCollection games) { if ((games == null) || (games.Count == 0)) { - ArchiLogger.LogNullError(nameof(games)); - - return; + throw new ArgumentNullException(nameof(games)); } - string gameName = null; + 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))); @@ -987,16 +927,14 @@ namespace ArchiSteamFarm { internal async Task ImportKeysToRedeem(string filePath) { if (string.IsNullOrEmpty(filePath) || !File.Exists(filePath)) { - ArchiLogger.LogNullError(nameof(filePath)); - - return; + throw new ArgumentNullException(nameof(filePath)); } try { OrderedDictionary gamesToRedeemInBackground = new OrderedDictionary(); using (StreamReader reader = new StreamReader(filePath)) { - string line; + string? line; while ((line = await reader.ReadLineAsync().ConfigureAwait(false)) != null) { if (line.Length == 0) { @@ -1037,27 +975,19 @@ namespace ArchiSteamFarm { } internal static void Init(StringComparer botsComparer) { - if (botsComparer == null) { - ASF.ArchiLogger.LogNullError(nameof(botsComparer)); - - return; - } - if (Bots != null) { ASF.ArchiLogger.LogGenericError(Strings.WarningFailed); return; } - BotsComparer = botsComparer; + BotsComparer = botsComparer ?? throw new ArgumentNullException(nameof(botsComparer)); Bots = new ConcurrentDictionary(botsComparer); } internal bool IsBlacklistedFromIdling(uint appID) { if (appID == 0) { - ArchiLogger.LogNullError(nameof(appID)); - - return false; + throw new ArgumentNullException(nameof(appID)); } return BotDatabase.IsBlacklistedFromIdling(appID); @@ -1065,9 +995,7 @@ namespace ArchiSteamFarm { internal bool IsBlacklistedFromTrades(ulong steamID) { if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { - ArchiLogger.LogNullError(nameof(steamID)); - - return false; + throw new ArgumentNullException(nameof(steamID)); } return BotDatabase.IsBlacklistedFromTrades(steamID); @@ -1075,9 +1003,7 @@ namespace ArchiSteamFarm { internal bool IsPriorityIdling(uint appID) { if (appID == 0) { - ArchiLogger.LogNullError(nameof(appID)); - - return false; + throw new ArgumentNullException(nameof(appID)); } return BotDatabase.IsPriorityIdling(appID); @@ -1098,7 +1024,7 @@ namespace ArchiSteamFarm { return; } - BotConfig botConfig = await BotConfig.Load(configFile).ConfigureAwait(false); + BotConfig? botConfig = await BotConfig.Load(configFile).ConfigureAwait(false); if (botConfig == null) { await Destroy().ConfigureAwait(false); @@ -1180,10 +1106,8 @@ namespace ArchiSteamFarm { } internal static async Task RegisterBot(string botName) { - if (string.IsNullOrEmpty(botName)) { - ASF.ArchiLogger.LogNullError(nameof(botName)); - - return; + if (string.IsNullOrEmpty(botName) || (Bots == null)) { + throw new ArgumentNullException(nameof(botName) + " || " + nameof(Bots)); } if (Bots.ContainsKey(botName)) { @@ -1198,7 +1122,7 @@ namespace ArchiSteamFarm { return; } - BotConfig botConfig = await BotConfig.Load(configFilePath).ConfigureAwait(false); + BotConfig? botConfig = await BotConfig.Load(configFilePath).ConfigureAwait(false); if (botConfig == null) { ASF.ArchiLogger.LogGenericError(string.Format(Strings.ErrorBotConfigInvalid, configFilePath)); @@ -1218,7 +1142,7 @@ namespace ArchiSteamFarm { return; } - BotDatabase botDatabase = await BotDatabase.CreateOrLoad(databaseFilePath).ConfigureAwait(false); + BotDatabase? botDatabase = await BotDatabase.CreateOrLoad(databaseFilePath).ConfigureAwait(false); if (botDatabase == null) { ASF.ArchiLogger.LogGenericError(string.Format(Strings.ErrorDatabaseInvalid, databaseFilePath)); @@ -1254,7 +1178,7 @@ namespace ArchiSteamFarm { await PluginsCore.OnBotInit(bot).ConfigureAwait(false); - HashSet customHandlers = await PluginsCore.OnBotSteamHandlersInit(bot).ConfigureAwait(false); + HashSet? customHandlers = await PluginsCore.OnBotSteamHandlersInit(bot).ConfigureAwait(false); if ((customHandlers != null) && (customHandlers.Count > 0)) { foreach (ClientMsgHandler customHandler in customHandlers) { @@ -1270,10 +1194,8 @@ namespace ArchiSteamFarm { } internal async Task Rename(string newBotName) { - if (string.IsNullOrEmpty(newBotName)) { - ArchiLogger.LogNullError(nameof(newBotName)); - - return false; + if (string.IsNullOrEmpty(newBotName) || (Bots == null)) { + throw new ArgumentNullException(nameof(newBotName) + " || " + nameof(Bots)); } if (newBotName.Equals(SharedInfo.ASF) || Bots.ContainsKey(newBotName)) { @@ -1318,9 +1240,7 @@ namespace ArchiSteamFarm { internal async Task SendMessage(ulong steamID, string message) { if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || string.IsNullOrEmpty(message)) { - ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(message)); - - return false; + throw new ArgumentNullException(nameof(steamID) + " || " + nameof(message)); } if (!IsConnectedAndLoggedOn) { @@ -1329,7 +1249,8 @@ namespace ArchiSteamFarm { ArchiLogger.LogChatMessage(true, message, steamID: steamID); - ushort maxMessageLength = (ushort) (MaxMessageLength - ReservedMessageLength - (ASF.GlobalConfig.SteamMessagePrefix?.Length ?? 0)); + string? steamMessagePrefix = ASF.GlobalConfig?.SteamMessagePrefix ?? GlobalConfig.DefaultSteamMessagePrefix; + ushort maxMessageLength = (ushort) (MaxMessageLength - ReservedMessageLength - (steamMessagePrefix?.Length ?? 0)); // We must escape our message prior to sending it message = Escape(message); @@ -1363,7 +1284,7 @@ namespace ArchiSteamFarm { // ReSharper restore ArrangeMissingParentheses string messagePart = message.Substring(i, partLength); - messagePart = ASF.GlobalConfig.SteamMessagePrefix + (i > 0 ? "…" : "") + messagePart + (maxMessageLength < message.Length - i ? "…" : ""); + messagePart = steamMessagePrefix + (i > 0 ? "…" : "") + messagePart + (maxMessageLength < message.Length - i ? "…" : ""); await MessagingSemaphore.WaitAsync().ConfigureAwait(false); @@ -1414,9 +1335,7 @@ namespace ArchiSteamFarm { internal async Task SendMessage(ulong chatGroupID, ulong chatID, string message) { if ((chatGroupID == 0) || (chatID == 0) || string.IsNullOrEmpty(message)) { - ArchiLogger.LogNullError(nameof(chatGroupID) + " || " + nameof(chatID) + " || " + nameof(message)); - - return false; + throw new ArgumentNullException(nameof(chatGroupID) + " || " + nameof(chatID) + " || " + nameof(message)); } if (!IsConnectedAndLoggedOn) { @@ -1425,7 +1344,8 @@ namespace ArchiSteamFarm { ArchiLogger.LogChatMessage(true, message, chatGroupID, chatID); - ushort maxMessageLength = (ushort) (MaxMessageLength - ReservedMessageLength - (ASF.GlobalConfig.SteamMessagePrefix?.Length ?? 0)); + string? steamMessagePrefix = ASF.GlobalConfig?.SteamMessagePrefix ?? GlobalConfig.DefaultSteamMessagePrefix; + ushort maxMessageLength = (ushort) (MaxMessageLength - ReservedMessageLength - (steamMessagePrefix?.Length ?? 0)); // We must escape our message prior to sending it message = Escape(message); @@ -1459,7 +1379,7 @@ namespace ArchiSteamFarm { // ReSharper restore ArrangeMissingParentheses string messagePart = message.Substring(i, partLength); - messagePart = ASF.GlobalConfig.SteamMessagePrefix + (i > 0 ? "…" : "") + messagePart + (maxMessageLength < message.Length - i ? "…" : ""); + messagePart = steamMessagePrefix + (i > 0 ? "…" : "") + messagePart + (maxMessageLength < message.Length - i ? "…" : ""); await MessagingSemaphore.WaitAsync().ConfigureAwait(false); @@ -1506,9 +1426,7 @@ namespace ArchiSteamFarm { internal async Task SendTypingMessage(ulong steamID) { if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { - ArchiLogger.LogNullError(nameof(steamID)); - - return false; + throw new ArgumentNullException(nameof(steamID)); } if (!IsConnectedAndLoggedOn) { @@ -1576,35 +1494,33 @@ namespace ArchiSteamFarm { internal static IOrderedDictionary ValidateGamesToRedeemInBackground(IOrderedDictionary gamesToRedeemInBackground) { if ((gamesToRedeemInBackground == null) || (gamesToRedeemInBackground.Count == 0)) { - ASF.ArchiLogger.LogNullError(nameof(gamesToRedeemInBackground)); - - return null; + throw new ArgumentNullException(nameof(gamesToRedeemInBackground)); } HashSet invalidKeys = new HashSet(); - foreach (DictionaryEntry game in gamesToRedeemInBackground) { + foreach (DictionaryEntry? game in gamesToRedeemInBackground) { bool invalid = false; - string key = game.Key as string; + string? key = game?.Key as string; if (string.IsNullOrEmpty(key)) { invalid = true; ASF.ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorIsInvalid, nameof(key))); - } else if (!Utilities.IsValidCdKey(key)) { + } else if (!Utilities.IsValidCdKey(key!)) { invalid = true; ASF.ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorIsInvalid, key)); } - string name = game.Value as string; + string? name = game?.Value as string; if (string.IsNullOrEmpty(name)) { invalid = true; ASF.ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorIsInvalid, nameof(name))); } - if (invalid) { - invalidKeys.Add(game.Key); + if (invalid && !string.IsNullOrEmpty(key)) { + invalidKeys.Add(key!); } } @@ -1655,6 +1571,10 @@ namespace ArchiSteamFarm { } private async Task Destroy(bool force = false) { + if (Bots == null) { + throw new ArgumentNullException(nameof(Bots)); + } + if (KeepRunning) { if (!force) { Stop(); @@ -1675,9 +1595,7 @@ namespace ArchiSteamFarm { private static string Escape(string message) { if (string.IsNullOrEmpty(message)) { - ASF.ArchiLogger.LogNullError(nameof(message)); - - return null; + throw new ArgumentNullException(nameof(message)); } return message.Replace("\\", "\\\\").Replace("[", "\\["); @@ -1685,20 +1603,15 @@ namespace ArchiSteamFarm { private string GetFilePath(EFileType fileType) { if (!Enum.IsDefined(typeof(EFileType), fileType)) { - ASF.ArchiLogger.LogNullError(nameof(fileType)); - - return null; + throw new ArgumentNullException(nameof(fileType)); } return GetFilePath(BotName, fileType); } - [ItemCanBeNull] - private async Task> GetKeysFromFile(string filePath) { + private async Task?> GetKeysFromFile(string filePath) { if (string.IsNullOrEmpty(filePath)) { - ArchiLogger.LogNullError(nameof(filePath)); - - return null; + throw new ArgumentNullException(nameof(filePath)); } if (!File.Exists(filePath)) { @@ -1710,7 +1623,7 @@ namespace ArchiSteamFarm { try { using StreamReader reader = new StreamReader(filePath); - string line; + string? line; while ((line = await reader.ReadLineAsync().ConfigureAwait(false)) != null) { if (line.Length == 0) { @@ -1768,6 +1681,10 @@ namespace ArchiSteamFarm { } private async Task HeartBeat() { + if (ASF.GlobalConfig == null) { + throw new ArgumentNullException(nameof(ASF.GlobalConfig)); + } + if (!KeepRunning || !IsConnectedAndLoggedOn || (HeartBeatFailures == byte.MaxValue)) { return; } @@ -1839,16 +1756,18 @@ namespace ArchiSteamFarm { return; } + byte connectionTimeout = ASF.GlobalConfig?.ConnectionTimeout ?? GlobalConfig.DefaultConnectionTimeout; + ConnectionFailureTimer = new Timer( async e => await InitPermanentConnectionFailure().ConfigureAwait(false), null, - TimeSpan.FromMinutes(Math.Ceiling(ASF.GlobalConfig.ConnectionTimeout / 30.0)), // Delay + TimeSpan.FromMinutes(Math.Ceiling(connectionTimeout / 30.0)), // Delay Timeout.InfiniteTimeSpan // Period ); } private async Task InitializeFamilySharing() { - HashSet steamIDs = await ArchiWebHandler.GetFamilySharingSteamIDs().ConfigureAwait(false); + HashSet? steamIDs = await ArchiWebHandler.GetFamilySharingSteamIDs().ConfigureAwait(false); if (steamIDs == null) { return; @@ -1861,9 +1780,9 @@ namespace ArchiSteamFarm { if (string.IsNullOrEmpty(BotConfig.SteamLogin)) { RequiredInput = ASF.EUserInputType.Login; - string steamLogin = await Logging.GetUserInput(ASF.EUserInputType.Login, BotName).ConfigureAwait(false); + string? steamLogin = await Logging.GetUserInput(ASF.EUserInputType.Login, BotName).ConfigureAwait(false); - if (string.IsNullOrEmpty(steamLogin) || !SetUserInput(ASF.EUserInputType.Login, steamLogin)) { + if (string.IsNullOrEmpty(steamLogin) || !SetUserInput(ASF.EUserInputType.Login, steamLogin!)) { ArchiLogger.LogGenericError(string.Format(Strings.ErrorIsInvalid, nameof(steamLogin))); return false; @@ -1873,9 +1792,9 @@ namespace ArchiSteamFarm { if (requiresPassword && string.IsNullOrEmpty(BotConfig.DecryptedSteamPassword)) { RequiredInput = ASF.EUserInputType.Password; - string steamPassword = await Logging.GetUserInput(ASF.EUserInputType.Password, BotName).ConfigureAwait(false); + string? steamPassword = await Logging.GetUserInput(ASF.EUserInputType.Password, BotName).ConfigureAwait(false); - if (string.IsNullOrEmpty(steamPassword) || !SetUserInput(ASF.EUserInputType.Password, steamPassword)) { + if (string.IsNullOrEmpty(steamPassword) || !SetUserInput(ASF.EUserInputType.Password, steamPassword!)) { ArchiLogger.LogGenericError(string.Format(Strings.ErrorIsInvalid, nameof(steamPassword))); return false; @@ -1886,6 +1805,10 @@ namespace ArchiSteamFarm { } private async Task InitModules() { + if (Bots == null) { + throw new ArgumentNullException(nameof(Bots)); + } + AccountFlags = EAccountFlags.NormalUser; AvatarHash = Nickname = null; MasterChatGroupID = 0; @@ -1959,9 +1882,7 @@ namespace ArchiSteamFarm { private bool IsMasterClanID(ulong steamID) { if ((steamID == 0) || !new SteamID(steamID).IsClanAccount) { - ArchiLogger.LogNullError(nameof(steamID)); - - return false; + throw new ArgumentNullException(nameof(steamID)); } return steamID == BotConfig.SteamMasterClanID; @@ -1969,9 +1890,7 @@ namespace ArchiSteamFarm { private static bool IsRefundable(EPaymentMethod paymentMethod) { if (paymentMethod == EPaymentMethod.None) { - ASF.ArchiLogger.LogNullError(nameof(paymentMethod)); - - return false; + throw new ArgumentNullException(nameof(paymentMethod)); } // Complimentary is also a flag @@ -1999,7 +1918,7 @@ namespace ArchiSteamFarm { MasterChatGroupID = chatGroupID; } - HashSet chatGroupIDs = await ArchiHandler.GetMyChatGroupIDs().ConfigureAwait(false); + HashSet? chatGroupIDs = await ArchiHandler.GetMyChatGroupIDs().ConfigureAwait(false); if (chatGroupIDs?.Contains(MasterChatGroupID) != false) { return; @@ -2017,7 +1936,9 @@ namespace ArchiSteamFarm { return; } - if (ASF.GlobalConfig.LoginLimiterDelay == 0) { + byte loginLimiterDelay = ASF.GlobalConfig?.LoginLimiterDelay ?? GlobalConfig.DefaultLoginLimiterDelay; + + if (loginLimiterDelay == 0) { await ASF.LoginRateLimitingSemaphore.WaitAsync().ConfigureAwait(false); ASF.LoginRateLimitingSemaphore.Release(); @@ -2032,7 +1953,7 @@ namespace ArchiSteamFarm { } finally { Utilities.InBackground( async () => { - await Task.Delay(ASF.GlobalConfig.LoginLimiterDelay * 1000).ConfigureAwait(false); + await Task.Delay(loginLimiterDelay * 1000).ConfigureAwait(false); ASF.LoginSemaphore.Release(); } ); @@ -2067,7 +1988,7 @@ namespace ArchiSteamFarm { return; } - byte[] sentryFileHash = null; + byte[]? sentryFileHash = null; if (File.Exists(sentryFilePath)) { try { @@ -2084,7 +2005,7 @@ namespace ArchiSteamFarm { } } - string loginKey = null; + string? loginKey = null; if (BotConfig.UseLoginKeys) { // Login keys are not guaranteed to be valid, we should use them only if we don't have full details available from the user @@ -2092,7 +2013,7 @@ namespace ArchiSteamFarm { loginKey = BotDatabase.LoginKey; // Decrypt login key if needed - if (!string.IsNullOrEmpty(loginKey) && (loginKey.Length > 19) && (BotConfig.PasswordFormat != ArchiCryptoHelper.ECryptoMethod.PlainText)) { + if (!string.IsNullOrEmpty(loginKey) && (loginKey!.Length > 19) && (BotConfig.PasswordFormat != ArchiCryptoHelper.ECryptoMethod.PlainText)) { loginKey = ArchiCryptoHelper.Decrypt(BotConfig.PasswordFormat, loginKey); } } @@ -2111,7 +2032,7 @@ namespace ArchiSteamFarm { const string nonAsciiPattern = @"[^\u0000-\u007F]+"; string username = Regex.Replace(BotConfig.SteamLogin, nonAsciiPattern, "", RegexOptions.CultureInvariant | RegexOptions.IgnoreCase); - string password = BotConfig.DecryptedSteamPassword; + string? password = BotConfig.DecryptedSteamPassword; if (!string.IsNullOrEmpty(password)) { password = Regex.Replace(password, nonAsciiPattern, "", RegexOptions.CultureInvariant | RegexOptions.IgnoreCase); @@ -2119,7 +2040,7 @@ namespace ArchiSteamFarm { ArchiLogger.LogGenericInfo(Strings.BotLoggingIn); - if (string.IsNullOrEmpty(TwoFactorCode) && HasMobileAuthenticator) { + if (string.IsNullOrEmpty(TwoFactorCode) && (BotDatabase.MobileAuthenticator != null)) { // We should always include 2FA token, even if it's not required TwoFactorCode = await BotDatabase.MobileAuthenticator.GenerateToken().ConfigureAwait(false); } @@ -2128,7 +2049,7 @@ namespace ArchiSteamFarm { SteamUser.LogOnDetails logOnDetails = new SteamUser.LogOnDetails { AuthCode = AuthCode, - CellID = ASF.GlobalDatabase.CellID, + CellID = ASF.GlobalDatabase?.CellID, LoginID = LoginID, LoginKey = loginKey, Password = password, @@ -2146,14 +2067,14 @@ namespace ArchiSteamFarm { } private async void OnDisconnected(SteamClient.DisconnectedCallback callback) { - if (callback == null) { - ArchiLogger.LogNullError(nameof(callback)); + if (ASF.LoginRateLimitingSemaphore == null) { + ASF.ArchiLogger.LogNullError(nameof(ASF.LoginRateLimitingSemaphore)); return; } - if (ASF.LoginRateLimitingSemaphore == null) { - ASF.ArchiLogger.LogNullError(nameof(ASF.LoginRateLimitingSemaphore)); + if (callback == null) { + ArchiLogger.LogNullError(nameof(callback)); return; } @@ -2385,6 +2306,10 @@ namespace ArchiSteamFarm { } private async void OnLicenseList(SteamApps.LicenseListCallback callback) { + if (ASF.GlobalDatabase == null) { + throw new ArgumentNullException(nameof(ASF.GlobalDatabase)); + } + if (callback?.LicenseList == null) { ArchiLogger.LogNullError(nameof(callback) + " || " + nameof(callback.LicenseList)); @@ -2412,7 +2337,7 @@ namespace ArchiSteamFarm { // Package is always due to refresh with access token change packagesToRefresh[license.PackageID] = (uint) license.LastChangeNumber; - } else if (!ASF.GlobalDatabase.PackagesDataReadOnly.TryGetValue(license.PackageID, out (uint ChangeNumber, HashSet AppIDs) packageData) || (packageData.ChangeNumber < license.LastChangeNumber)) { + } else if (!ASF.GlobalDatabase.PackagesDataReadOnly.TryGetValue(license.PackageID, out (uint ChangeNumber, HashSet? AppIDs) packageData) || (packageData.ChangeNumber < license.LastChangeNumber)) { packagesToRefresh[license.PackageID] = (uint) license.LastChangeNumber; } } @@ -2494,9 +2419,9 @@ namespace ArchiSteamFarm { case EResult.AccountLogonDenied: RequiredInput = ASF.EUserInputType.SteamGuard; - string authCode = await Logging.GetUserInput(ASF.EUserInputType.SteamGuard, BotName).ConfigureAwait(false); + string? authCode = await Logging.GetUserInput(ASF.EUserInputType.SteamGuard, BotName).ConfigureAwait(false); - if (string.IsNullOrEmpty(authCode) || !SetUserInput(ASF.EUserInputType.SteamGuard, authCode)) { + if (string.IsNullOrEmpty(authCode) || !SetUserInput(ASF.EUserInputType.SteamGuard, authCode!)) { ArchiLogger.LogGenericError(string.Format(Strings.ErrorIsInvalid, nameof(authCode))); Stop(); @@ -2507,9 +2432,9 @@ namespace ArchiSteamFarm { if (!HasMobileAuthenticator) { RequiredInput = ASF.EUserInputType.TwoFactorAuthentication; - string twoFactorCode = await Logging.GetUserInput(ASF.EUserInputType.TwoFactorAuthentication, BotName).ConfigureAwait(false); + string? twoFactorCode = await Logging.GetUserInput(ASF.EUserInputType.TwoFactorAuthentication, BotName).ConfigureAwait(false); - if (string.IsNullOrEmpty(twoFactorCode) || !SetUserInput(ASF.EUserInputType.TwoFactorAuthentication, twoFactorCode)) { + if (string.IsNullOrEmpty(twoFactorCode) || !SetUserInput(ASF.EUserInputType.TwoFactorAuthentication, twoFactorCode!)) { ArchiLogger.LogGenericError(string.Format(Strings.ErrorIsInvalid, nameof(twoFactorCode))); Stop(); @@ -2519,7 +2444,7 @@ namespace ArchiSteamFarm { break; case EResult.OK: AccountFlags = callback.AccountFlags; - SteamID = callback.ClientSteamID; + SteamID = callback.ClientSteamID ?? throw new ArgumentNullException(nameof(callback.ClientSteamID)); ArchiLogger.LogGenericInfo(string.Format(Strings.BotLoggedOn, SteamID + (!string.IsNullOrEmpty(callback.VanityURL) ? "/" + callback.VanityURL : ""))); @@ -2539,7 +2464,7 @@ namespace ArchiSteamFarm { ArchiLogger.LogGenericWarning(Strings.BotAccountLocked); } - if ((callback.CellID != 0) && (callback.CellID != ASF.GlobalDatabase.CellID)) { + if ((callback.CellID != 0) && (ASF.GlobalDatabase != null) && (callback.CellID != ASF.GlobalDatabase.CellID)) { ASF.GlobalDatabase.CellID = callback.CellID; } @@ -2553,14 +2478,14 @@ namespace ArchiSteamFarm { } if (callback.ParentalSettings != null) { - (bool isSteamParentalEnabled, string steamParentalCode) = ValidateSteamParental(callback.ParentalSettings, BotConfig.SteamParentalCode); + (bool isSteamParentalEnabled, string? steamParentalCode) = ValidateSteamParental(callback.ParentalSettings, BotConfig.SteamParentalCode); if (isSteamParentalEnabled) { SteamParentalActive = true; if (!string.IsNullOrEmpty(steamParentalCode)) { if (BotConfig.SteamParentalCode != steamParentalCode) { - if (!SetUserInput(ASF.EUserInputType.SteamParentalCode, steamParentalCode)) { + if (!SetUserInput(ASF.EUserInputType.SteamParentalCode, steamParentalCode!)) { ArchiLogger.LogGenericError(string.Format(Strings.ErrorIsInvalid, nameof(steamParentalCode))); Stop(); @@ -2568,12 +2493,12 @@ namespace ArchiSteamFarm { break; } } - } else if (string.IsNullOrEmpty(BotConfig.SteamParentalCode) || (BotConfig.SteamParentalCode.Length != BotConfig.SteamParentalCodeLength)) { + } else if (string.IsNullOrEmpty(BotConfig.SteamParentalCode) || (BotConfig.SteamParentalCode!.Length != BotConfig.SteamParentalCodeLength)) { RequiredInput = ASF.EUserInputType.SteamParentalCode; steamParentalCode = await Logging.GetUserInput(ASF.EUserInputType.SteamParentalCode, BotName).ConfigureAwait(false); - if (string.IsNullOrEmpty(steamParentalCode) || !SetUserInput(ASF.EUserInputType.SteamParentalCode, steamParentalCode)) { + if (string.IsNullOrEmpty(steamParentalCode) || !SetUserInput(ASF.EUserInputType.SteamParentalCode, steamParentalCode!)) { ArchiLogger.LogGenericError(string.Format(Strings.ErrorIsInvalid, nameof(steamParentalCode))); Stop(); @@ -2584,12 +2509,12 @@ namespace ArchiSteamFarm { } else { SteamParentalActive = false; } - } else if (SteamParentalActive && !string.IsNullOrEmpty(BotConfig.SteamParentalCode) && (BotConfig.SteamParentalCode.Length != BotConfig.SteamParentalCodeLength)) { + } else if (SteamParentalActive && !string.IsNullOrEmpty(BotConfig.SteamParentalCode) && (BotConfig.SteamParentalCode!.Length != BotConfig.SteamParentalCodeLength)) { RequiredInput = ASF.EUserInputType.SteamParentalCode; - string steamParentalCode = await Logging.GetUserInput(ASF.EUserInputType.SteamParentalCode, BotName).ConfigureAwait(false); + string? steamParentalCode = await Logging.GetUserInput(ASF.EUserInputType.SteamParentalCode, BotName).ConfigureAwait(false); - if (string.IsNullOrEmpty(steamParentalCode) || !SetUserInput(ASF.EUserInputType.SteamParentalCode, steamParentalCode)) { + if (string.IsNullOrEmpty(steamParentalCode) || !SetUserInput(ASF.EUserInputType.SteamParentalCode, steamParentalCode!)) { ArchiLogger.LogGenericError(string.Format(Strings.ErrorIsInvalid, nameof(steamParentalCode))); Stop(); @@ -2600,7 +2525,7 @@ namespace ArchiSteamFarm { ArchiWebHandler.OnVanityURLChanged(callback.VanityURL); - if (!await ArchiWebHandler.Init(SteamID, SteamClient.Universe, callback.WebAPIUserNonce, SteamParentalActive ? BotConfig.SteamParentalCode : null).ConfigureAwait(false)) { + if (!await ArchiWebHandler.Init(SteamID, SteamClient.Universe, callback.WebAPIUserNonce ?? throw new ArgumentNullException(nameof(callback.WebAPIUserNonce)), SteamParentalActive ? BotConfig.SteamParentalCode : null).ConfigureAwait(false)) { if (!await RefreshSession().ConfigureAwait(false)) { break; } @@ -2688,14 +2613,8 @@ namespace ArchiSteamFarm { } private void OnLoginKey(SteamUser.LoginKeyCallback callback) { - if (callback == null) { - ArchiLogger.LogNullError(nameof(callback)); - - return; - } - - if (string.IsNullOrEmpty(callback.LoginKey)) { - ArchiLogger.LogNullError(nameof(callback.LoginKey)); + if (string.IsNullOrEmpty(callback?.LoginKey)) { + ArchiLogger.LogNullError(nameof(callback) + " || " + nameof(callback.LoginKey)); return; } @@ -2704,7 +2623,7 @@ namespace ArchiSteamFarm { return; } - string loginKey = callback.LoginKey; + string? loginKey = callback!.LoginKey; if (BotConfig.PasswordFormat != ArchiCryptoHelper.ECryptoMethod.PlainText) { loginKey = ArchiCryptoHelper.Encrypt(BotConfig.PasswordFormat, loginKey); @@ -2733,10 +2652,11 @@ namespace ArchiSteamFarm { byte[] sentryHash; try { -#if !NETFRAMEWORK - await +#if NETFRAMEWORK + using FileStream fileStream = File.Open(sentryFilePath, FileMode.OpenOrCreate, FileAccess.ReadWrite); +#else + await using FileStream fileStream = File.Open(sentryFilePath, FileMode.OpenOrCreate, FileAccess.ReadWrite); #endif - using FileStream fileStream = File.Open(sentryFilePath, FileMode.OpenOrCreate, FileAccess.ReadWrite); fileStream.Seek(callback.Offset, SeekOrigin.Begin); @@ -2787,7 +2707,7 @@ namespace ArchiSteamFarm { return; } - string avatarHash = null; + string? avatarHash = null; if ((callback.AvatarHash.Length > 0) && callback.AvatarHash.Any(singleByte => singleByte != 0)) { avatarHash = BitConverter.ToString(callback.AvatarHash).Replace("-", "").ToLowerInvariant(); @@ -2957,7 +2877,7 @@ namespace ArchiSteamFarm { bool assumeWalletKeyOnBadActivationCode = BotConfig.RedeemingPreferences.HasFlag(BotConfig.ERedeemingPreferences.AssumeWalletKeyOnBadActivationCode); while (IsConnectedAndLoggedOn && BotDatabase.HasGamesToRedeemInBackground) { - (string key, string name) = BotDatabase.GetGameToRedeemInBackground(); + (string? key, string? name) = BotDatabase.GetGameToRedeemInBackground(); if (string.IsNullOrEmpty(key) || string.IsNullOrEmpty(name)) { ArchiLogger.LogNullError(nameof(key) + " || " + nameof(name)); @@ -2965,7 +2885,7 @@ namespace ArchiSteamFarm { break; } - ArchiHandler.PurchaseResponseCallback result = await Actions.RedeemKey(key).ConfigureAwait(false); + ArchiHandler.PurchaseResponseCallback? result = await Actions.RedeemKey(key!).ConfigureAwait(false); if (result == null) { continue; @@ -2973,7 +2893,7 @@ namespace ArchiSteamFarm { if (((result.PurchaseResultDetail == EPurchaseResultDetail.CannotRedeemCodeFromClient) || ((result.PurchaseResultDetail == EPurchaseResultDetail.BadActivationCode) && assumeWalletKeyOnBadActivationCode)) && (WalletCurrency != ECurrencyCode.Invalid)) { // If it's a wallet code, we try to redeem it first, then handle the inner result as our primary one - (EResult Result, EPurchaseResultDetail? PurchaseResult)? walletResult = await ArchiWebHandler.RedeemWalletKey(key).ConfigureAwait(false); + (EResult Result, EPurchaseResultDetail? PurchaseResult)? walletResult = await ArchiWebHandler.RedeemWalletKey(key!).ConfigureAwait(false); if (walletResult != null) { result.Result = walletResult.Value.Result; @@ -3017,10 +2937,10 @@ namespace ArchiSteamFarm { break; } - BotDatabase.RemoveGameToRedeemInBackground(key); + 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 != null) && (result.Items.Count > 0)) { + if (name!.Equals(key) && (result.Items != null) && (result.Items.Count > 0)) { name = string.Join(", ", result.Items.Values); } @@ -3088,6 +3008,10 @@ namespace ArchiSteamFarm { } private bool ShouldAckChatMessage(ulong steamID) { + if (Bots == null) { + throw new ArgumentNullException(nameof(Bots)); + } + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { ArchiLogger.LogNullError(nameof(steamID)); @@ -3121,19 +3045,15 @@ namespace ArchiSteamFarm { private static string UnEscape(string message) { if (string.IsNullOrEmpty(message)) { - ASF.ArchiLogger.LogNullError(nameof(message)); - - return null; + throw new ArgumentNullException(nameof(message)); } return message.Replace("\\[", "[").Replace("\\\\", "\\"); } - private (bool IsSteamParentalEnabled, string SteamParentalCode) ValidateSteamParental(ParentalSettings settings, string steamParentalCode = null) { + private (bool IsSteamParentalEnabled, string? SteamParentalCode) ValidateSteamParental(ParentalSettings settings, string? steamParentalCode = null) { if (settings == null) { - ArchiLogger.LogNullError(nameof(settings)); - - return (false, null); + throw new ArgumentNullException(nameof(settings)); } if (!settings.is_enabled) { @@ -3166,7 +3086,7 @@ namespace ArchiSteamFarm { } if (i >= steamParentalCode.Length) { - IEnumerable passwordHash = ArchiCryptoHelper.GenerateSteamParentalHash(password, settings.salt, (byte) settings.passwordhash.Length, steamParentalAlgorithm); + IEnumerable? passwordHash = ArchiCryptoHelper.GenerateSteamParentalHash(password, settings.salt, (byte) settings.passwordhash.Length, steamParentalAlgorithm); if (passwordHash?.SequenceEqual(settings.passwordhash) == true) { return (true, steamParentalCode); diff --git a/ArchiSteamFarm/BotConfig.cs b/ArchiSteamFarm/BotConfig.cs index 30d47e375..f0d5c2144 100644 --- a/ArchiSteamFarm/BotConfig.cs +++ b/ArchiSteamFarm/BotConfig.cs @@ -83,10 +83,10 @@ namespace ArchiSteamFarm { public readonly EBotBehaviour BotBehaviour = DefaultBotBehaviour; [JsonProperty] - public readonly string CustomGamePlayedWhileFarming = DefaultCustomGamePlayedWhileFarming; + public readonly string? CustomGamePlayedWhileFarming = DefaultCustomGamePlayedWhileFarming; [JsonProperty] - public readonly string CustomGamePlayedWhileIdle = DefaultCustomGamePlayedWhileIdle; + public readonly string? CustomGamePlayedWhileIdle = DefaultCustomGamePlayedWhileIdle; [JsonProperty(Required = Required.DisallowNull)] public readonly bool Enabled = DefaultEnabled; @@ -134,7 +134,7 @@ namespace ArchiSteamFarm { public readonly bool ShutdownOnFarmingFinished = DefaultShutdownOnFarmingFinished; [JsonProperty] - public readonly string SteamTradeToken = DefaultSteamTradeToken; + public readonly string? SteamTradeToken = DefaultSteamTradeToken; [JsonProperty(ObjectCreationHandling = ObjectCreationHandling.Replace, Required = Required.DisallowNull)] public readonly ImmutableDictionary SteamUserPermissions = DefaultSteamUserPermissions; @@ -152,13 +152,13 @@ namespace ArchiSteamFarm { public ulong SteamMasterClanID { get; private set; } = DefaultSteamMasterClanID; [JsonExtensionData] - internal Dictionary AdditionalProperties { + internal Dictionary? AdditionalProperties { get; [UsedImplicitly] set; } - internal string DecryptedSteamPassword { + internal string? DecryptedSteamPassword { get { if (string.IsNullOrEmpty(SteamPassword)) { return null; @@ -168,7 +168,7 @@ namespace ArchiSteamFarm { return SteamPassword; } - string result = ArchiCryptoHelper.Decrypt(PasswordFormat, SteamPassword); + string? result = ArchiCryptoHelper.Decrypt(PasswordFormat, SteamPassword!); if (string.IsNullOrEmpty(result)) { ASF.ArchiLogger.LogGenericError(string.Format(Strings.ErrorIsInvalid, nameof(SteamPassword))); @@ -181,7 +181,7 @@ namespace ArchiSteamFarm { set { if (!string.IsNullOrEmpty(value) && (PasswordFormat != ArchiCryptoHelper.ECryptoMethod.PlainText)) { - value = ArchiCryptoHelper.Encrypt(PasswordFormat, value); + value = ArchiCryptoHelper.Encrypt(PasswordFormat, value!); } SteamPassword = value; @@ -196,7 +196,7 @@ namespace ArchiSteamFarm { internal bool ShouldSerializeSensitiveDetails { private get; set; } [JsonProperty] - internal string SteamLogin { + internal string? SteamLogin { get => BackingSteamLogin; set { @@ -206,7 +206,7 @@ namespace ArchiSteamFarm { } [JsonProperty] - internal string SteamParentalCode { + internal string? SteamParentalCode { get => BackingSteamParentalCode; set { @@ -216,7 +216,7 @@ namespace ArchiSteamFarm { } [JsonProperty] - internal string SteamPassword { + internal string? SteamPassword { get => BackingSteamPassword; set { @@ -225,12 +225,12 @@ namespace ArchiSteamFarm { } } - private string BackingSteamLogin = DefaultSteamLogin; - private string BackingSteamParentalCode = DefaultSteamParentalCode; - private string BackingSteamPassword = DefaultSteamPassword; + private string? BackingSteamLogin = DefaultSteamLogin; + private string? BackingSteamParentalCode = DefaultSteamParentalCode; + private string? BackingSteamPassword = DefaultSteamPassword; [JsonProperty(PropertyName = SharedInfo.UlongCompatibilityStringPrefix + nameof(SteamMasterClanID), Required = Required.DisallowNull)] - [JetBrains.Annotations.NotNull] + private string SSteamMasterClanID { get => SteamMasterClanID.ToString(); @@ -248,7 +248,7 @@ namespace ArchiSteamFarm { [JsonConstructor] private BotConfig() { } - internal (bool Valid, string ErrorMessage) CheckValidation() { + internal (bool Valid, string? ErrorMessage) CheckValidation() { if (BotBehaviour > EBotBehaviour.All) { return (false, string.Format(Strings.ErrorConfigPropertyInvalid, nameof(BotBehaviour), BotBehaviour)); } @@ -285,11 +285,11 @@ namespace ArchiSteamFarm { return (false, string.Format(Strings.ErrorConfigPropertyInvalid, nameof(SteamMasterClanID), SteamMasterClanID)); } - if (!string.IsNullOrEmpty(SteamParentalCode) && (SteamParentalCode != "0") && (SteamParentalCode.Length != SteamParentalCodeLength)) { + if (!string.IsNullOrEmpty(SteamParentalCode) && (SteamParentalCode != "0") && (SteamParentalCode!.Length != SteamParentalCodeLength)) { return (false, string.Format(Strings.ErrorConfigPropertyInvalid, nameof(SteamParentalCode), SteamParentalCode)); } - if (!string.IsNullOrEmpty(SteamTradeToken) && (SteamTradeToken.Length != SteamTradeTokenLength)) { + if (!string.IsNullOrEmpty(SteamTradeToken) && (SteamTradeToken!.Length != SteamTradeTokenLength)) { return (false, string.Format(Strings.ErrorConfigPropertyInvalid, nameof(SteamTradeToken), SteamTradeToken)); } @@ -303,15 +303,12 @@ namespace ArchiSteamFarm { } } - return TradingPreferences <= ETradingPreferences.All ? (true, null) : (false, string.Format(Strings.ErrorConfigPropertyInvalid, nameof(TradingPreferences), TradingPreferences)); + return TradingPreferences <= ETradingPreferences.All ? (true, (string?) null) : (false, string.Format(Strings.ErrorConfigPropertyInvalid, nameof(TradingPreferences), TradingPreferences)); } - [ItemCanBeNull] - internal static async Task Load(string filePath) { + internal static async Task Load(string filePath) { if (string.IsNullOrEmpty(filePath)) { - ASF.ArchiLogger.LogNullError(nameof(filePath)); - - return null; + throw new ArgumentNullException(nameof(filePath)); } if (!File.Exists(filePath)) { @@ -342,10 +339,12 @@ namespace ArchiSteamFarm { return null; } - (bool valid, string errorMessage) = botConfig.CheckValidation(); + (bool valid, string? errorMessage) = botConfig.CheckValidation(); if (!valid) { - ASF.ArchiLogger.LogGenericError(errorMessage); + if (!string.IsNullOrEmpty(errorMessage)) { + ASF.ArchiLogger.LogGenericError(errorMessage!); + } return null; } @@ -355,9 +354,7 @@ namespace ArchiSteamFarm { internal static async Task Write(string filePath, BotConfig botConfig) { if (string.IsNullOrEmpty(filePath) || (botConfig == null)) { - ASF.ArchiLogger.LogNullError(nameof(filePath) + " || " + nameof(botConfig)); - - return false; + throw new ArgumentNullException(nameof(filePath) + " || " + nameof(botConfig)); } string json = JsonConvert.SerializeObject(botConfig, Formatting.Indented); diff --git a/ArchiSteamFarm/BotDatabase.cs b/ArchiSteamFarm/BotDatabase.cs index a873fb8b8..c52476142 100644 --- a/ArchiSteamFarm/BotDatabase.cs +++ b/ArchiSteamFarm/BotDatabase.cs @@ -29,7 +29,6 @@ using System.Threading.Tasks; using ArchiSteamFarm.Collections; using ArchiSteamFarm.Helpers; using ArchiSteamFarm.Localization; -using JetBrains.Annotations; using Newtonsoft.Json; using SteamKit2; @@ -58,7 +57,7 @@ namespace ArchiSteamFarm { [JsonProperty(Required = Required.DisallowNull)] private readonly ConcurrentHashSet IdlingPriorityAppIDs = new ConcurrentHashSet(); - internal string LoginKey { + internal string? LoginKey { get => BackingLoginKey; set { @@ -71,7 +70,7 @@ namespace ArchiSteamFarm { } } - internal MobileAuthenticator MobileAuthenticator { + internal MobileAuthenticator? MobileAuthenticator { get => BackingMobileAuthenticator; set { @@ -85,12 +84,12 @@ namespace ArchiSteamFarm { } [JsonProperty(PropertyName = "_" + nameof(LoginKey))] - private string BackingLoginKey; + private string? BackingLoginKey; [JsonProperty(PropertyName = "_" + nameof(MobileAuthenticator))] - private MobileAuthenticator BackingMobileAuthenticator; + private MobileAuthenticator? BackingMobileAuthenticator; - private BotDatabase([NotNull] string filePath) { + private BotDatabase(string filePath) { if (string.IsNullOrEmpty(filePath)) { throw new ArgumentNullException(nameof(filePath)); } @@ -109,9 +108,7 @@ namespace ArchiSteamFarm { internal void AddBlacklistedFromTradesSteamIDs(IReadOnlyCollection steamIDs) { if ((steamIDs == null) || (steamIDs.Count == 0)) { - ASF.ArchiLogger.LogNullError(nameof(steamIDs)); - - return; + throw new ArgumentNullException(nameof(steamIDs)); } if (BlacklistedFromTradesSteamIDs.AddRange(steamIDs)) { @@ -121,9 +118,7 @@ namespace ArchiSteamFarm { internal void AddGamesToRedeemInBackground(IOrderedDictionary games) { if ((games == null) || (games.Count == 0)) { - ASF.ArchiLogger.LogNullError(nameof(games)); - - return; + throw new ArgumentNullException(nameof(games)); } bool save = false; @@ -142,9 +137,7 @@ namespace ArchiSteamFarm { internal void AddIdlingBlacklistedAppIDs(IReadOnlyCollection appIDs) { if ((appIDs == null) || (appIDs.Count == 0)) { - ASF.ArchiLogger.LogNullError(nameof(appIDs)); - - return; + throw new ArgumentNullException(nameof(appIDs)); } if (IdlingBlacklistedAppIDs.AddRange(appIDs)) { @@ -154,9 +147,7 @@ namespace ArchiSteamFarm { internal void AddIdlingPriorityAppIDs(IReadOnlyCollection appIDs) { if ((appIDs == null) || (appIDs.Count == 0)) { - ASF.ArchiLogger.LogNullError(nameof(appIDs)); - - return; + throw new ArgumentNullException(nameof(appIDs)); } if (IdlingPriorityAppIDs.AddRange(appIDs)) { @@ -164,12 +155,9 @@ namespace ArchiSteamFarm { } } - [ItemCanBeNull] - internal static async Task CreateOrLoad(string filePath) { + internal static async Task CreateOrLoad(string filePath) { if (string.IsNullOrEmpty(filePath)) { - ASF.ArchiLogger.LogNullError(nameof(filePath)); - - return null; + throw new ArgumentNullException(nameof(filePath)); } if (!File.Exists(filePath)) { @@ -207,10 +195,10 @@ namespace ArchiSteamFarm { internal IReadOnlyCollection GetBlacklistedFromTradesSteamIDs() => BlacklistedFromTradesSteamIDs; - internal (string Key, string Name) GetGameToRedeemInBackground() { + internal (string? Key, string? Name) GetGameToRedeemInBackground() { lock (GamesToRedeemInBackground) { - foreach (DictionaryEntry game in GamesToRedeemInBackground) { - return (game.Key as string, game.Value as string); + foreach (DictionaryEntry? game in GamesToRedeemInBackground) { + return (game?.Key as string, game?.Value as string); } } @@ -222,9 +210,7 @@ namespace ArchiSteamFarm { internal bool IsBlacklistedFromIdling(uint appID) { if (appID == 0) { - ASF.ArchiLogger.LogNullError(nameof(appID)); - - return false; + throw new ArgumentNullException(nameof(appID)); } return IdlingBlacklistedAppIDs.Contains(appID); @@ -232,9 +218,7 @@ namespace ArchiSteamFarm { internal bool IsBlacklistedFromTrades(ulong steamID) { if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { - ASF.ArchiLogger.LogNullError(nameof(steamID)); - - return false; + throw new ArgumentNullException(nameof(steamID)); } return BlacklistedFromTradesSteamIDs.Contains(steamID); @@ -242,9 +226,7 @@ namespace ArchiSteamFarm { internal bool IsPriorityIdling(uint appID) { if (appID == 0) { - ASF.ArchiLogger.LogNullError(nameof(appID)); - - return false; + throw new ArgumentNullException(nameof(appID)); } return IdlingPriorityAppIDs.Contains(appID); @@ -252,9 +234,7 @@ namespace ArchiSteamFarm { internal void RemoveBlacklistedFromTradesSteamIDs(IReadOnlyCollection steamIDs) { if ((steamIDs == null) || (steamIDs.Count == 0)) { - ASF.ArchiLogger.LogNullError(nameof(steamIDs)); - - return; + throw new ArgumentNullException(nameof(steamIDs)); } if (BlacklistedFromTradesSteamIDs.RemoveRange(steamIDs)) { @@ -264,9 +244,7 @@ namespace ArchiSteamFarm { internal void RemoveGameToRedeemInBackground(string key) { if (string.IsNullOrEmpty(key)) { - ASF.ArchiLogger.LogNullError(nameof(key)); - - return; + throw new ArgumentNullException(nameof(key)); } lock (GamesToRedeemInBackground) { @@ -282,9 +260,7 @@ namespace ArchiSteamFarm { internal void RemoveIdlingBlacklistedAppIDs(IReadOnlyCollection appIDs) { if ((appIDs == null) || (appIDs.Count == 0)) { - ASF.ArchiLogger.LogNullError(nameof(appIDs)); - - return; + throw new ArgumentNullException(nameof(appIDs)); } if (IdlingBlacklistedAppIDs.RemoveRange(appIDs)) { @@ -294,9 +270,7 @@ namespace ArchiSteamFarm { internal void RemoveIdlingPriorityAppIDs(IReadOnlyCollection appIDs) { if ((appIDs == null) || (appIDs.Count == 0)) { - ASF.ArchiLogger.LogNullError(nameof(appIDs)); - - return; + throw new ArgumentNullException(nameof(appIDs)); } if (IdlingPriorityAppIDs.RemoveRange(appIDs)) { diff --git a/ArchiSteamFarm/CardsFarmer.cs b/ArchiSteamFarm/CardsFarmer.cs index e60a62b5f..f2a6918b7 100755 --- a/ArchiSteamFarm/CardsFarmer.cs +++ b/ArchiSteamFarm/CardsFarmer.cs @@ -76,7 +76,7 @@ namespace ArchiSteamFarm { private readonly SemaphoreSlim FarmingInitializationSemaphore = new SemaphoreSlim(1, 1); private readonly SemaphoreSlim FarmingResetSemaphore = new SemaphoreSlim(0, 1); private readonly ConcurrentList GamesToFarm = new ConcurrentList(); - private readonly Timer IdleFarmingTimer; + private readonly Timer? IdleFarmingTimer; private readonly ConcurrentDictionary LocallyIgnoredAppIDs = new ConcurrentDictionary(); private IEnumerable> SourcesOfIgnoredAppIDs { @@ -97,15 +97,17 @@ namespace ArchiSteamFarm { private bool PermanentlyPaused; private bool ShouldResumeFarming = true; - internal CardsFarmer([NotNull] Bot bot) { + internal CardsFarmer(Bot bot) { Bot = bot ?? throw new ArgumentNullException(nameof(bot)); - if (ASF.GlobalConfig.IdleFarmingPeriod > 0) { + byte idleFarmingPeriod = ASF.GlobalConfig?.IdleFarmingPeriod ?? GlobalConfig.DefaultIdleFarmingPeriod; + + if (idleFarmingPeriod > 0) { IdleFarmingTimer = new Timer( async e => await CheckGamesForFarming().ConfigureAwait(false), null, - TimeSpan.FromHours(ASF.GlobalConfig.IdleFarmingPeriod) + TimeSpan.FromSeconds(ASF.LoadBalancingDelay * Bot.Bots.Count), // Delay - TimeSpan.FromHours(ASF.GlobalConfig.IdleFarmingPeriod) // Period + TimeSpan.FromHours(idleFarmingPeriod) + TimeSpan.FromSeconds(ASF.LoadBalancingDelay * Bot.Bots?.Count ?? 0), // Delay + TimeSpan.FromHours(idleFarmingPeriod) // Period ); } } @@ -344,9 +346,7 @@ namespace ArchiSteamFarm { private async Task CheckGame(uint appID, string name, float hours, byte badgeLevel) { if ((appID == 0) || string.IsNullOrEmpty(name) || (hours < 0)) { - Bot.ArchiLogger.LogNullError(nameof(appID) + " || " + nameof(name) + " || " + nameof(hours)); - - return; + throw new ArgumentNullException(nameof(appID) + " || " + nameof(name) + " || " + nameof(hours)); } ushort? cardsRemaining = await GetCardsRemaining(appID).ConfigureAwait(false); @@ -374,9 +374,7 @@ namespace ArchiSteamFarm { private async Task CheckPage(IDocument htmlDocument, ISet parsedAppIDs) { if ((htmlDocument == null) || (parsedAppIDs == null)) { - Bot.ArchiLogger.LogNullError(nameof(htmlDocument) + " || " + nameof(parsedAppIDs)); - - return; + throw new ArgumentNullException(nameof(htmlDocument) + " || " + nameof(parsedAppIDs)); } List htmlNodes = htmlDocument.SelectNodes("//div[@class='badge_row_inner']"); @@ -386,18 +384,18 @@ namespace ArchiSteamFarm { return; } - HashSet backgroundTasks = null; + HashSet? backgroundTasks = null; foreach (IElement htmlNode in htmlNodes) { - IElement statsNode = htmlNode.SelectSingleElementNode(".//div[@class='badge_title_stats_content']"); - IElement appIDNode = statsNode?.SelectSingleElementNode(".//div[@class='card_drop_info_dialog']"); + IElement? statsNode = htmlNode.SelectSingleElementNode(".//div[@class='badge_title_stats_content']"); + IElement? appIDNode = statsNode?.SelectSingleElementNode(".//div[@class='card_drop_info_dialog']"); if (appIDNode == null) { // It's just a badge, nothing more continue; } - string appIDText = appIDNode.GetAttributeValue("id"); + string? appIDText = appIDNode.GetAttribute("id"); if (string.IsNullOrEmpty(appIDText)) { Bot.ArchiLogger.LogNullError(nameof(appIDText)); @@ -426,7 +424,7 @@ namespace ArchiSteamFarm { continue; } - if (SalesBlacklist.Contains(appID) || ASF.GlobalConfig.Blacklist.Contains(appID) || Bot.IsBlacklistedFromIdling(appID) || (Bot.BotConfig.IdlePriorityQueueOnly && !Bot.IsPriorityIdling(appID))) { + if (SalesBlacklist.Contains(appID) || (ASF.GlobalConfig?.Blacklist.Contains(appID) == true) || Bot.IsBlacklistedFromIdling(appID) || (Bot.BotConfig.IdlePriorityQueueOnly && !Bot.IsPriorityIdling(appID))) { // We're configured to ignore this appID, so skip it continue; } @@ -454,7 +452,7 @@ namespace ArchiSteamFarm { } // Cards - IElement progressNode = statsNode.SelectSingleElementNode(".//span[@class='progress_info_bold']"); + IElement? progressNode = statsNode?.SelectSingleElementNode(".//span[@class='progress_info_bold']"); if (progressNode == null) { Bot.ArchiLogger.LogNullError(nameof(progressNode)); @@ -493,7 +491,7 @@ namespace ArchiSteamFarm { } // To save us on extra work, check cards earned so far first - IElement cardsEarnedNode = statsNode.SelectSingleElementNode(".//div[@class='card_drop_info_header']"); + IElement? cardsEarnedNode = statsNode?.SelectSingleElementNode(".//div[@class='card_drop_info_header']"); if (cardsEarnedNode == null) { Bot.ArchiLogger.LogNullError(nameof(cardsEarnedNode)); @@ -538,7 +536,7 @@ namespace ArchiSteamFarm { } // Hours - IElement timeNode = statsNode.SelectSingleElementNode(".//div[@class='badge_title_stats_playtime']"); + IElement? timeNode = statsNode?.SelectSingleElementNode(".//div[@class='badge_title_stats_playtime']"); if (timeNode == null) { Bot.ArchiLogger.LogNullError(nameof(timeNode)); @@ -567,7 +565,7 @@ namespace ArchiSteamFarm { } // Names - IElement nameNode = statsNode.SelectSingleElementNode("(.//div[@class='card_drop_info_body'])[last()]"); + IElement? nameNode = statsNode?.SelectSingleElementNode("(.//div[@class='card_drop_info_body'])[last()]"); if (nameNode == null) { Bot.ArchiLogger.LogNullError(nameof(nameNode)); @@ -619,7 +617,7 @@ namespace ArchiSteamFarm { // Levels byte badgeLevel = 0; - IElement levelNode = htmlNode.SelectSingleElementNode(".//div[@class='badge_info_description']/div[2]"); + IElement? levelNode = htmlNode.SelectSingleElementNode(".//div[@class='badge_info_description']/div[2]"); if (levelNode != null) { // There is no levelNode if we didn't craft that badge yet (level 0) @@ -664,7 +662,7 @@ namespace ArchiSteamFarm { } else { Task task = CheckGame(appID, name, hours, badgeLevel); - switch (ASF.GlobalConfig.OptimizationMode) { + switch (ASF.GlobalConfig?.OptimizationMode) { case GlobalConfig.EOptimizationMode.MinMemoryUsage: await task.ConfigureAwait(false); @@ -687,12 +685,10 @@ namespace ArchiSteamFarm { private async Task CheckPage(byte page, ISet parsedAppIDs) { if ((page == 0) || (parsedAppIDs == null)) { - Bot.ArchiLogger.LogNullError(nameof(page) + " || " + nameof(parsedAppIDs)); - - return; + throw new ArgumentNullException(nameof(page) + " || " + nameof(parsedAppIDs)); } - using IDocument htmlDocument = await Bot.ArchiWebHandler.GetBadgePage(page).ConfigureAwait(false); + using IDocument? htmlDocument = await Bot.ArchiWebHandler.GetBadgePage(page).ConfigureAwait(false); if (htmlDocument == null) { return; @@ -802,9 +798,7 @@ namespace ArchiSteamFarm { private async Task FarmCards(Game game) { if (game == null) { - Bot.ArchiLogger.LogNullError(nameof(game)); - - return false; + throw new ArgumentNullException(nameof(game)); } if (game.AppID != game.PlayableAppID) { @@ -814,14 +808,14 @@ namespace ArchiSteamFarm { await Bot.IdleGame(game).ConfigureAwait(false); bool success = true; - DateTime endFarmingDate = DateTime.UtcNow.AddHours(ASF.GlobalConfig.MaxFarmingTime); + 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)); DateTime startFarmingPeriod = DateTime.UtcNow; - if (await FarmingResetSemaphore.WaitAsync((ASF.GlobalConfig.FarmingDelay * 60 * 1000) + (ExtraFarmingDelaySeconds * 1000)).ConfigureAwait(false)) { + if (await FarmingResetSemaphore.WaitAsync((ASF.GlobalConfig?.FarmingDelay ?? GlobalConfig.DefaultFarmingDelay * 60 * 1000) + (ExtraFarmingDelaySeconds * 1000)).ConfigureAwait(false)) { success = KeepFarming; } @@ -840,9 +834,7 @@ namespace ArchiSteamFarm { private async Task FarmHours(IReadOnlyCollection games) { if ((games == null) || (games.Count == 0)) { - Bot.ArchiLogger.LogNullError(nameof(games)); - - return false; + throw new ArgumentNullException(nameof(games)); } float maxHour = games.Max(game => game.HoursPlayed); @@ -868,7 +860,7 @@ namespace ArchiSteamFarm { DateTime startFarmingPeriod = DateTime.UtcNow; - if (await FarmingResetSemaphore.WaitAsync((ASF.GlobalConfig.FarmingDelay * 60 * 1000) + (ExtraFarmingDelaySeconds * 1000)).ConfigureAwait(false)) { + if (await FarmingResetSemaphore.WaitAsync((ASF.GlobalConfig?.FarmingDelay ?? GlobalConfig.DefaultFarmingDelay * 60 * 1000) + (ExtraFarmingDelaySeconds * 1000)).ConfigureAwait(false)) { success = KeepFarming; } @@ -893,9 +885,7 @@ namespace ArchiSteamFarm { private async Task FarmMultiple(IReadOnlyCollection games) { if ((games == null) || (games.Count == 0)) { - Bot.ArchiLogger.LogNullError(nameof(games)); - - return false; + throw new ArgumentNullException(nameof(games)); } CurrentGamesFarming.ReplaceWith(games); @@ -910,9 +900,7 @@ namespace ArchiSteamFarm { private async Task FarmSolo(Game game) { if (game == null) { - Bot.ArchiLogger.LogNullError(nameof(game)); - - return true; + throw new ArgumentNullException(nameof(game)); } CurrentGamesFarming.Add(game); @@ -935,14 +923,12 @@ namespace ArchiSteamFarm { private async Task GetCardsRemaining(uint appID) { if (appID == 0) { - Bot.ArchiLogger.LogNullError(nameof(appID)); - - return 0; + throw new ArgumentNullException(nameof(appID)); } - using IDocument htmlDocument = await Bot.ArchiWebHandler.GetGameCardsPage(appID).ConfigureAwait(false); + using IDocument? htmlDocument = await Bot.ArchiWebHandler.GetGameCardsPage(appID).ConfigureAwait(false); - IElement progressNode = htmlDocument?.SelectSingleNode("//span[@class='progress_info_bold']"); + IElement? progressNode = htmlDocument?.SelectSingleNode("//span[@class='progress_info_bold']"); if (progressNode == null) { return null; @@ -975,7 +961,7 @@ namespace ArchiSteamFarm { // Find the number of badge pages Bot.ArchiLogger.LogGenericInfo(Strings.CheckingFirstBadgePage); - using IDocument htmlDocument = await Bot.ArchiWebHandler.GetBadgePage(1).ConfigureAwait(false); + using IDocument? htmlDocument = await Bot.ArchiWebHandler.GetBadgePage(1).ConfigureAwait(false); if (htmlDocument == null) { Bot.ArchiLogger.LogGenericWarning(Strings.WarningCouldNotCheckBadges); @@ -985,7 +971,7 @@ namespace ArchiSteamFarm { byte maxPages = 1; - IElement htmlNode = htmlDocument.SelectSingleNode("(//a[@class='pagelink'])[last()]"); + IElement? htmlNode = htmlDocument.SelectSingleNode("(//a[@class='pagelink'])[last()]"); if (htmlNode != null) { string lastPage = htmlNode.TextContent; @@ -1009,7 +995,7 @@ namespace ArchiSteamFarm { Task mainTask = CheckPage(htmlDocument, parsedAppIDs); - switch (ASF.GlobalConfig.OptimizationMode) { + switch (ASF.GlobalConfig?.OptimizationMode) { case GlobalConfig.EOptimizationMode.MinMemoryUsage: await mainTask.ConfigureAwait(false); @@ -1054,9 +1040,7 @@ namespace ArchiSteamFarm { private async Task IsPlayableGame(Game game) { if (game == null) { - Bot.ArchiLogger.LogNullError(nameof(game)); - - return false; + throw new ArgumentNullException(nameof(game)); } (uint playableAppID, DateTime ignoredUntil, bool ignoredGlobally) = await Bot.GetAppDataForIdling(game.AppID, game.HoursPlayed).ConfigureAwait(false); @@ -1077,9 +1061,7 @@ namespace ArchiSteamFarm { private async Task ShouldFarm(Game game) { if (game == null) { - Bot.ArchiLogger.LogNullError(nameof(game)); - - return false; + throw new ArgumentNullException(nameof(game)); } ushort? cardsRemaining = await GetCardsRemaining(game.AppID).ConfigureAwait(false); @@ -1131,16 +1113,18 @@ namespace ArchiSteamFarm { break; case BotConfig.EFarmingOrder.MarketableAscending: case BotConfig.EFarmingOrder.MarketableDescending: - HashSet marketableAppIDs = await Bot.GetMarketableAppIDs().ConfigureAwait(false); + HashSet? marketableAppIDs = await Bot.GetMarketableAppIDs().ConfigureAwait(false); if ((marketableAppIDs != null) && (marketableAppIDs.Count > 0)) { + ImmutableHashSet immutableMarketableAppIDs = marketableAppIDs.ToImmutableHashSet(); + switch (farmingOrder) { case BotConfig.EFarmingOrder.MarketableAscending: - orderedGamesToFarm = orderedGamesToFarm.ThenBy(game => marketableAppIDs.Contains(game.AppID)); + orderedGamesToFarm = orderedGamesToFarm.ThenBy(game => immutableMarketableAppIDs.Contains(game.AppID)); break; case BotConfig.EFarmingOrder.MarketableDescending: - orderedGamesToFarm = orderedGamesToFarm.ThenByDescending(game => marketableAppIDs.Contains(game.AppID)); + orderedGamesToFarm = orderedGamesToFarm.ThenByDescending(game => immutableMarketableAppIDs.Contains(game.AppID)); break; default: @@ -1177,7 +1161,7 @@ namespace ArchiSteamFarm { foreach (Game game in GamesToFarm) { DateTime redeemDate = DateTime.MinValue; - HashSet packageIDs = ASF.GlobalDatabase.GetPackageIDs(game.AppID, Bot.OwnedPackageIDs.Keys); + HashSet? packageIDs = ASF.GlobalDatabase?.GetPackageIDs(game.AppID, Bot.OwnedPackageIDs.Keys); if (packageIDs != null) { foreach (uint packageID in packageIDs) { @@ -1196,13 +1180,17 @@ namespace ArchiSteamFarm { redeemDates[game.AppID] = redeemDate; } + ImmutableDictionary immutableRedeemDates = redeemDates.ToImmutableDictionary(); + switch (farmingOrder) { case BotConfig.EFarmingOrder.RedeemDateTimesAscending: - orderedGamesToFarm = orderedGamesToFarm.ThenBy(game => redeemDates[game.AppID]); + // ReSharper disable once AccessToModifiedClosure - you're wrong + orderedGamesToFarm = orderedGamesToFarm.ThenBy(game => immutableRedeemDates[game.AppID]); break; case BotConfig.EFarmingOrder.RedeemDateTimesDescending: - orderedGamesToFarm = orderedGamesToFarm.ThenByDescending(game => redeemDates[game.AppID]); + // ReSharper disable once AccessToModifiedClosure - you're wrong + orderedGamesToFarm = orderedGamesToFarm.ThenByDescending(game => immutableRedeemDates[game.AppID]); break; default: @@ -1241,7 +1229,7 @@ namespace ArchiSteamFarm { internal uint PlayableAppID { get; set; } - internal Game(uint appID, [NotNull] string gameName, float hoursPlayed, ushort cardsRemaining, byte badgeLevel) { + 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)); } @@ -1255,8 +1243,8 @@ namespace ArchiSteamFarm { PlayableAppID = appID; } - public bool Equals(Game other) => (other != null) && (ReferenceEquals(other, this) || ((AppID == other.AppID) && (BadgeLevel == other.BadgeLevel) && (GameName == other.GameName))); - public override bool Equals(object obj) => (obj != null) && ((obj == this) || (obj is Game game && Equals(game))); + public bool Equals(Game? other) => (other != null) && (ReferenceEquals(other, this) || ((AppID == other.AppID) && (BadgeLevel == other.BadgeLevel) && (GameName == other.GameName))); + public override bool Equals(object? obj) => (obj != null) && ((obj == this) || (obj is Game game && Equals(game))); public override int GetHashCode() => RuntimeCompatibility.HashCode.Combine(AppID, BadgeLevel, GameName); } } diff --git a/ArchiSteamFarm/Collections/ConcurrentEnumerator.cs b/ArchiSteamFarm/Collections/ConcurrentEnumerator.cs index 04e489291..8cb0bd8ab 100644 --- a/ArchiSteamFarm/Collections/ConcurrentEnumerator.cs +++ b/ArchiSteamFarm/Collections/ConcurrentEnumerator.cs @@ -22,7 +22,6 @@ using System; using System.Collections; using System.Collections.Generic; -using JetBrains.Annotations; namespace ArchiSteamFarm.Collections { internal sealed class ConcurrentEnumerator : IEnumerator { @@ -31,9 +30,9 @@ namespace ArchiSteamFarm.Collections { private readonly IEnumerator Enumerator; private readonly IDisposable Lock; - object IEnumerator.Current => Current; + object? IEnumerator.Current => Current; - internal ConcurrentEnumerator([NotNull] IReadOnlyCollection collection, [NotNull] IDisposable @lock) { + internal ConcurrentEnumerator(IReadOnlyCollection collection, IDisposable @lock) { if ((collection == null) || (@lock == null)) { throw new ArgumentNullException(nameof(collection) + " || " + nameof(@lock)); } diff --git a/ArchiSteamFarm/Collections/ConcurrentHashSet.cs b/ArchiSteamFarm/Collections/ConcurrentHashSet.cs index eee44ba86..33098f1cd 100644 --- a/ArchiSteamFarm/Collections/ConcurrentHashSet.cs +++ b/ArchiSteamFarm/Collections/ConcurrentHashSet.cs @@ -23,12 +23,11 @@ using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; using System.Linq; using JetBrains.Annotations; namespace ArchiSteamFarm.Collections { - public sealed class ConcurrentHashSet : IReadOnlyCollection, ISet { + public sealed class ConcurrentHashSet : IReadOnlyCollection, ISet where T : notnull { public int Count => BackingCollection.Count; public bool IsReadOnly => false; @@ -36,7 +35,7 @@ namespace ArchiSteamFarm.Collections { public ConcurrentHashSet() => BackingCollection = new ConcurrentDictionary(); - public ConcurrentHashSet([JetBrains.Annotations.NotNull] IEqualityComparer comparer) { + public ConcurrentHashSet(IEqualityComparer comparer) { if (comparer == null) { throw new ArgumentNullException(nameof(comparer)); } @@ -47,7 +46,6 @@ namespace ArchiSteamFarm.Collections { public bool Add(T item) => BackingCollection.TryAdd(item, true); public void Clear() => BackingCollection.Clear(); - [SuppressMessage("ReSharper", "AssignNullToNotNullAttribute")] public bool Contains(T item) => BackingCollection.ContainsKey(item); public void CopyTo(T[] array, int arrayIndex) => BackingCollection.Keys.CopyTo(array, arrayIndex); @@ -98,7 +96,6 @@ namespace ArchiSteamFarm.Collections { return otherSet.Any(Contains); } - [SuppressMessage("ReSharper", "AssignNullToNotNullAttribute")] public bool Remove(T item) => BackingCollection.TryRemove(item, out _); public bool SetEquals(IEnumerable other) { @@ -127,22 +124,20 @@ namespace ArchiSteamFarm.Collections { } } - [SuppressMessage("ReSharper", "AnnotationConflictInHierarchy")] - [SuppressMessage("ReSharper", "AssignNullToNotNullAttribute")] - void ICollection.Add([JetBrains.Annotations.NotNull] T item) => Add(item); + void ICollection.Add(T item) => Add(item); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); // We use Count() and not Any() because we must ensure full loop pass [PublicAPI] - public bool AddRange([JetBrains.Annotations.NotNull] IEnumerable items) => items.Count(Add) > 0; + public bool AddRange(IEnumerable items) => items.Count(Add) > 0; // We use Count() and not Any() because we must ensure full loop pass [PublicAPI] - public bool RemoveRange([JetBrains.Annotations.NotNull] IEnumerable items) => items.Count(Remove) > 0; + public bool RemoveRange(IEnumerable items) => items.Count(Remove) > 0; [PublicAPI] - public bool ReplaceIfNeededWith([JetBrains.Annotations.NotNull] IReadOnlyCollection other) { + public bool ReplaceIfNeededWith(IReadOnlyCollection other) { if (SetEquals(other)) { return false; } @@ -153,7 +148,7 @@ namespace ArchiSteamFarm.Collections { } [PublicAPI] - public void ReplaceWith([JetBrains.Annotations.NotNull] IEnumerable other) { + public void ReplaceWith(IEnumerable other) { BackingCollection.Clear(); foreach (T item in other) { diff --git a/ArchiSteamFarm/Collections/ConcurrentList.cs b/ArchiSteamFarm/Collections/ConcurrentList.cs index ac2878955..d4e2eb29e 100644 --- a/ArchiSteamFarm/Collections/ConcurrentList.cs +++ b/ArchiSteamFarm/Collections/ConcurrentList.cs @@ -21,7 +21,6 @@ using System.Collections; using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; using Nito.AsyncEx; namespace ArchiSteamFarm.Collections { @@ -80,8 +79,6 @@ namespace ArchiSteamFarm.Collections { } } - [JetBrains.Annotations.NotNull] - [SuppressMessage("ReSharper", "AnnotationRedundancyInHierarchy")] public IEnumerator GetEnumerator() => new ConcurrentEnumerator(BackingCollection, Lock.ReaderLock()); public int IndexOf(T item) { @@ -108,11 +105,9 @@ namespace ArchiSteamFarm.Collections { } } - [JetBrains.Annotations.NotNull] - [SuppressMessage("ReSharper", "AnnotationRedundancyInHierarchy")] IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - internal void ReplaceWith([JetBrains.Annotations.NotNull] IEnumerable collection) { + internal void ReplaceWith(IEnumerable collection) { using (Lock.WriterLock()) { BackingCollection.Clear(); BackingCollection.AddRange(collection); diff --git a/ArchiSteamFarm/Commands.cs b/ArchiSteamFarm/Commands.cs index c12d17236..d8e160edd 100644 --- a/ArchiSteamFarm/Commands.cs +++ b/ArchiSteamFarm/Commands.cs @@ -39,14 +39,12 @@ namespace ArchiSteamFarm { private readonly Bot Bot; private readonly Dictionary CachedGamesOwned = new Dictionary(); - internal Commands([NotNull] Bot bot) => Bot = bot ?? throw new ArgumentNullException(nameof(bot)); + 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)) { - ASF.ArchiLogger.LogNullError(nameof(response) + " || " + nameof(botName)); - - return null; + throw new ArgumentNullException(nameof(response) + " || " + nameof(botName)); } return Environment.NewLine + "<" + botName + "> " + response; @@ -55,9 +53,7 @@ namespace ArchiSteamFarm { [PublicAPI] public string FormatBotResponse(string response) { if (string.IsNullOrEmpty(response)) { - ASF.ArchiLogger.LogNullError(nameof(response)); - - return null; + throw new ArgumentNullException(nameof(response)); } return "<" + Bot.BotName + "> " + response; @@ -66,29 +62,23 @@ namespace ArchiSteamFarm { [PublicAPI] public static string FormatStaticResponse(string response) { if (string.IsNullOrEmpty(response)) { - ASF.ArchiLogger.LogNullError(nameof(response)); - - return null; + throw new ArgumentNullException(nameof(response)); } return "<" + SharedInfo.ASF + "> " + response; } [PublicAPI] - public async Task Response(ulong steamID, string message) { + public async Task Response(ulong steamID, string message) { if ((steamID == 0) || string.IsNullOrEmpty(message)) { - Bot.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(message)); - - return null; + throw new ArgumentNullException(nameof(steamID) + " || " + nameof(message)); } - string[] args = message.Split((char[]) null, StringSplitOptions.RemoveEmptyEntries); + string[] args = message.Split(new char[0], StringSplitOptions.RemoveEmptyEntries); switch (args.Length) { case 0: - Bot.ArchiLogger.LogNullError(nameof(args)); - - return null; + throw new ArgumentOutOfRangeException(nameof(args.Length)); case 1: switch (args[0].ToUpperInvariant()) { case "2FA": @@ -146,7 +136,7 @@ namespace ArchiSteamFarm { case "VERSION": return ResponseVersion(steamID); default: - string pluginsResponse = await PluginsCore.OnBotCommand(Bot, steamID, message, args).ConfigureAwait(false); + string? pluginsResponse = await PluginsCore.OnBotCommand(Bot, steamID, message, args).ConfigureAwait(false); return !string.IsNullOrEmpty(pluginsResponse) ? pluginsResponse : ResponseUnknown(steamID); } @@ -287,7 +277,7 @@ namespace ArchiSteamFarm { case "UNPACK": return await ResponseUnpackBoosters(steamID, Utilities.GetArgsAsText(args, 1, ",")).ConfigureAwait(false); default: - string pluginsResponse = await PluginsCore.OnBotCommand(Bot, steamID, message, args).ConfigureAwait(false); + string? pluginsResponse = await PluginsCore.OnBotCommand(Bot, steamID, message, args).ConfigureAwait(false); return !string.IsNullOrEmpty(pluginsResponse) ? pluginsResponse : ResponseUnknown(steamID); } @@ -296,17 +286,17 @@ namespace ArchiSteamFarm { internal async Task HandleMessage(ulong steamID, string message) { if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || string.IsNullOrEmpty(message)) { - Bot.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(message)); - - return; + throw new ArgumentNullException(nameof(steamID) + " || " + nameof(message)); } - if (!string.IsNullOrEmpty(ASF.GlobalConfig.CommandPrefix)) { - if (!message.StartsWith(ASF.GlobalConfig.CommandPrefix, StringComparison.OrdinalIgnoreCase)) { - string pluginsResponse = await PluginsCore.OnBotMessage(Bot, steamID, message).ConfigureAwait(false); + string commandPrefix = ASF.GlobalConfig?.CommandPrefix ?? GlobalConfig.DefaultCommandPrefix; + + if (!string.IsNullOrEmpty(commandPrefix)) { + if (!message.StartsWith(commandPrefix, StringComparison.OrdinalIgnoreCase)) { + string? pluginsResponse = await PluginsCore.OnBotMessage(Bot, steamID, message).ConfigureAwait(false); if (!string.IsNullOrEmpty(pluginsResponse)) { - if (!await Bot.SendMessage(steamID, pluginsResponse).ConfigureAwait(false)) { + 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)); } @@ -315,10 +305,10 @@ namespace ArchiSteamFarm { return; } - message = message.Substring(ASF.GlobalConfig.CommandPrefix.Length); + message = message.Substring(commandPrefix.Length); } - Task responseTask = Response(steamID, message); + Task responseTask = Response(steamID, message); bool feedback = Bot.HasPermission(steamID, BotConfig.EPermission.FamilySharing); @@ -334,7 +324,7 @@ namespace ArchiSteamFarm { } } - string response = await responseTask.ConfigureAwait(false); + string? response = await responseTask.ConfigureAwait(false); if (string.IsNullOrEmpty(response)) { if (!feedback) { @@ -345,7 +335,7 @@ namespace ArchiSteamFarm { response = FormatBotResponse(Strings.UnknownCommand); } - if (!await Bot.SendMessage(steamID, response).ConfigureAwait(false)) { + 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)); } @@ -353,17 +343,17 @@ namespace ArchiSteamFarm { 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)) { - Bot.ArchiLogger.LogNullError(nameof(chatGroupID) + " || " + nameof(chatID) + " || " + nameof(steamID) + " || " + nameof(message)); - - return; + throw new ArgumentNullException(nameof(chatGroupID) + " || " + nameof(chatID) + " || " + nameof(steamID) + " || " + nameof(message)); } - if (!string.IsNullOrEmpty(ASF.GlobalConfig.CommandPrefix)) { - if (!message.StartsWith(ASF.GlobalConfig.CommandPrefix, StringComparison.OrdinalIgnoreCase)) { - string pluginsResponse = await PluginsCore.OnBotMessage(Bot, steamID, message).ConfigureAwait(false); + string commandPrefix = ASF.GlobalConfig?.CommandPrefix ?? GlobalConfig.DefaultCommandPrefix; + + if (!string.IsNullOrEmpty(commandPrefix)) { + if (!message.StartsWith(commandPrefix, StringComparison.OrdinalIgnoreCase)) { + string? pluginsResponse = await PluginsCore.OnBotMessage(Bot, steamID, message).ConfigureAwait(false); if (!string.IsNullOrEmpty(pluginsResponse)) { - if (!await Bot.SendMessage(chatGroupID, chatID, pluginsResponse).ConfigureAwait(false)) { + 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)); } @@ -372,10 +362,10 @@ namespace ArchiSteamFarm { return; } - message = message.Substring(ASF.GlobalConfig.CommandPrefix.Length); + message = message.Substring(commandPrefix.Length); } - Task responseTask = Response(steamID, message); + Task responseTask = Response(steamID, message); bool feedback = Bot.HasPermission(steamID, BotConfig.EPermission.FamilySharing); @@ -393,7 +383,7 @@ namespace ArchiSteamFarm { } } - string response = await responseTask.ConfigureAwait(false); + string? response = await responseTask.ConfigureAwait(false); if (string.IsNullOrEmpty(response)) { if (!feedback) { @@ -404,7 +394,7 @@ namespace ArchiSteamFarm { response = FormatBotResponse(Strings.UnknownCommand); } - if (!await Bot.SendMessage(chatGroupID, chatID, response).ConfigureAwait(false)) { + 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)); } @@ -417,7 +407,7 @@ namespace ArchiSteamFarm { } } - private async Task> FetchGamesOwned(bool cachedOnly = false) { + private async Task?> FetchGamesOwned(bool cachedOnly = false) { lock (CachedGamesOwned) { if (CachedGamesOwned.Count > 0) { return new Dictionary(CachedGamesOwned); @@ -430,7 +420,7 @@ namespace ArchiSteamFarm { bool? hasValidApiKey = await Bot.ArchiWebHandler.HasValidApiKey().ConfigureAwait(false); - Dictionary gamesOwned = hasValidApiKey.GetValueOrDefault() ? await Bot.ArchiWebHandler.GetOwnedGames(Bot.SteamID).ConfigureAwait(false) : await Bot.ArchiWebHandler.GetMyOwnedGames().ConfigureAwait(false); + Dictionary? gamesOwned = hasValidApiKey.GetValueOrDefault() ? await Bot.ArchiWebHandler.GetOwnedGames(Bot.SteamID).ConfigureAwait(false) : await Bot.ArchiWebHandler.GetMyOwnedGames().ConfigureAwait(false); if ((gamesOwned != null) && (gamesOwned.Count > 0)) { lock (CachedGamesOwned) { @@ -447,48 +437,41 @@ namespace ArchiSteamFarm { return gamesOwned; } - private async Task Response2FA(ulong steamID) { - if (steamID == 0) { - Bot.ArchiLogger.LogNullError(nameof(steamID)); - - return null; + private async Task Response2FA(ulong steamID) { + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentNullException(nameof(steamID)); } if (!Bot.HasPermission(steamID, BotConfig.EPermission.Master)) { return null; } - (bool success, string token, string message) = await Bot.Actions.GenerateTwoFactorAuthenticationToken().ConfigureAwait(false); + (bool success, string? token, string message) = await Bot.Actions.GenerateTwoFactorAuthenticationToken().ConfigureAwait(false); - return FormatBotResponse(success ? string.Format(Strings.BotAuthenticatorToken, token) : string.Format(Strings.WarningFailedWithError, message)); + return FormatBotResponse(success && !string.IsNullOrEmpty(token) ? string.Format(Strings.BotAuthenticatorToken, token) : string.Format(Strings.WarningFailedWithError, message)); } - [ItemCanBeNull] - private static async Task Response2FA(ulong steamID, string botNames) { - if ((steamID == 0) || string.IsNullOrEmpty(botNames)) { - ASF.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(botNames)); - - return null; + 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)); } - HashSet bots = Bot.GetBots(botNames); + HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } - IList results = await Utilities.InParallel(bots.Select(bot => bot.Commands.Response2FA(steamID))).ConfigureAwait(false); + 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 List(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) { - Bot.ArchiLogger.LogNullError(nameof(steamID)); - - return null; + private async Task Response2FAConfirm(ulong steamID, bool confirm) { + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentNullException(nameof(steamID)); } if (!Bot.HasPermission(steamID, BotConfig.EPermission.Master)) { @@ -508,32 +491,27 @@ namespace ArchiSteamFarm { return FormatBotResponse(success ? message : string.Format(Strings.WarningFailedWithError, message)); } - [ItemCanBeNull] - private static async Task Response2FAConfirm(ulong steamID, string botNames, bool confirm) { - if ((steamID == 0) || string.IsNullOrEmpty(botNames)) { - ASF.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(botNames)); - - return null; + 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)); } - HashSet bots = Bot.GetBots(botNames); + HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } - IList results = await Utilities.InParallel(bots.Select(bot => bot.Commands.Response2FAConfirm(steamID, confirm))).ConfigureAwait(false); + 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 List(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) || string.IsNullOrEmpty(query)) { - ASF.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(query)); - - return 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 (!Bot.HasPermission(steamID, BotConfig.EPermission.Operator)) { @@ -603,32 +581,27 @@ namespace ArchiSteamFarm { return response.Length > 0 ? response.ToString() : null; } - [ItemCanBeNull] - private static async Task ResponseAddLicense(ulong steamID, string botNames, string query) { - if ((steamID == 0) || string.IsNullOrEmpty(botNames) || string.IsNullOrEmpty(query)) { - ASF.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(botNames) + " || " + nameof(query)); - - return null; + 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)); } - HashSet bots = Bot.GetBots(botNames); + HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } - IList results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseAddLicense(steamID, query))).ConfigureAwait(false); + 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 List(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) || string.IsNullOrEmpty(targetAppID) || string.IsNullOrEmpty(targetContextID)) { - Bot.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(targetAppID) + " || " + nameof(targetContextID)); - - return 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 (!Bot.HasPermission(steamID, BotConfig.EPermission.Master)) { @@ -652,32 +625,27 @@ namespace ArchiSteamFarm { return FormatBotResponse(success ? message : string.Format(Strings.WarningFailedWithError, message)); } - [ItemCanBeNull] - private static async Task ResponseAdvancedLoot(ulong steamID, string botNames, string appID, string contextID) { - if ((steamID == 0) || string.IsNullOrEmpty(botNames) || string.IsNullOrEmpty(appID) || string.IsNullOrEmpty(contextID)) { - ASF.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(botNames) + " || " + nameof(appID) + " || " + nameof(contextID)); - - return null; + 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)); } - HashSet bots = Bot.GetBots(botNames); + HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } - IList results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseAdvancedLoot(steamID, appID, contextID))).ConfigureAwait(false); + 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 List(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) || string.IsNullOrEmpty(options) || string.IsNullOrEmpty(keys)) { - ASF.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(options) + " || " + nameof(keys)); - - return 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 (!Bot.HasPermission(steamID, BotConfig.EPermission.Operator)) { @@ -752,32 +720,27 @@ namespace ArchiSteamFarm { return await ResponseRedeem(steamID, keys, redeemFlags).ConfigureAwait(false); } - [ItemCanBeNull] - private static async Task ResponseAdvancedRedeem(ulong steamID, string botNames, string options, string keys) { - if ((steamID == 0) || string.IsNullOrEmpty(botNames) || string.IsNullOrEmpty(options) || string.IsNullOrEmpty(keys)) { - ASF.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(botNames) + " || " + nameof(options) + " || " + nameof(keys)); - - return null; + 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)); } - HashSet bots = Bot.GetBots(botNames); + HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } - IList results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseAdvancedRedeem(steamID, options, keys))).ConfigureAwait(false); + 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 List(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) || (appID == 0) || (contextID == 0) || (targetBot == null)) { - Bot.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(appID) + " || " + nameof(contextID) + " || " + nameof(targetBot)); - - return 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 (!Bot.HasPermission(steamID, BotConfig.EPermission.Master)) { @@ -797,14 +760,12 @@ namespace ArchiSteamFarm { return FormatBotResponse(success ? message : string.Format(Strings.WarningFailedWithError, message)); } - private async Task ResponseAdvancedTransfer(ulong steamID, string targetAppID, string targetContextID, string botNameTo) { - if ((steamID == 0) || string.IsNullOrEmpty(targetAppID) || string.IsNullOrEmpty(targetContextID) || string.IsNullOrEmpty(botNameTo)) { - Bot.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(targetAppID) + " || " + nameof(targetContextID) + " || " + nameof(botNameTo)); - - return null; + 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)); } - Bot targetBot = Bot.GetBot(botNameTo); + Bot? targetBot = Bot.GetBot(botNameTo); if (targetBot == null) { return ASF.IsOwner(steamID) ? FormatBotResponse(string.Format(Strings.BotNotFound, botNameTo)) : null; @@ -821,14 +782,12 @@ namespace ArchiSteamFarm { 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) || string.IsNullOrEmpty(botNames) || string.IsNullOrEmpty(targetAppID) || string.IsNullOrEmpty(targetContextID) || string.IsNullOrEmpty(botNameTo)) { - ASF.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(botNames) + " || " + nameof(targetAppID) + " || " + nameof(targetContextID) + " || " + nameof(botNameTo)); - - return null; + 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)); } - HashSet bots = Bot.GetBots(botNames); + HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; @@ -842,24 +801,22 @@ namespace ArchiSteamFarm { return FormatStaticResponse(string.Format(Strings.ErrorIsInvalid, nameof(contextID))); } - Bot targetBot = Bot.GetBot(botNameTo); + Bot? targetBot = Bot.GetBot(botNameTo); if (targetBot == null) { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNameTo)) : null; } - IList results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseAdvancedTransfer(steamID, appID, contextID, targetBot))).ConfigureAwait(false); + 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 List(results.Where(result => !string.IsNullOrEmpty(result))!); return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; } - private string ResponseBackgroundGamesRedeemer(ulong steamID) { - if (steamID == 0) { - Bot.ArchiLogger.LogNullError(nameof(steamID)); - - return null; + private string? ResponseBackgroundGamesRedeemer(ulong steamID) { + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentNullException(nameof(steamID)); } if (!Bot.HasPermission(steamID, BotConfig.EPermission.Master)) { @@ -871,32 +828,27 @@ namespace ArchiSteamFarm { return FormatBotResponse(string.Format(Strings.BotGamesToRedeemInBackgroundCount, count)); } - [ItemCanBeNull] - private static async Task ResponseBackgroundGamesRedeemer(ulong steamID, string botNames) { - if ((steamID == 0) || string.IsNullOrEmpty(botNames)) { - ASF.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(botNames)); - - return null; + 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)); } - HashSet bots = Bot.GetBots(botNames); + HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } - IList results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseBackgroundGamesRedeemer(steamID)))).ConfigureAwait(false); + 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 List(results.Where(result => !string.IsNullOrEmpty(result))!); return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; } - private string ResponseBlacklist(ulong steamID) { - if (steamID == 0) { - Bot.ArchiLogger.LogNullError(nameof(steamID)); - - return null; + private string? ResponseBlacklist(ulong steamID) { + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentNullException(nameof(steamID)); } if (!Bot.HasPermission(steamID, BotConfig.EPermission.Master)) { @@ -908,32 +860,27 @@ namespace ArchiSteamFarm { return FormatBotResponse(blacklist.Count > 0 ? string.Join(", ", blacklist) : string.Format(Strings.ErrorIsEmpty, nameof(blacklist))); } - [ItemCanBeNull] - private static async Task ResponseBlacklist(ulong steamID, string botNames) { - if ((steamID == 0) || string.IsNullOrEmpty(botNames)) { - ASF.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(botNames)); - - return null; + 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)); } - HashSet bots = Bot.GetBots(botNames); + HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } - IList results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseBlacklist(steamID)))).ConfigureAwait(false); + 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 List(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) || string.IsNullOrEmpty(targetSteamIDs)) { - Bot.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(targetSteamIDs)); - - return 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 (!Bot.HasPermission(steamID, BotConfig.EPermission.Master)) { @@ -961,32 +908,27 @@ namespace ArchiSteamFarm { return FormatBotResponse(Strings.Done); } - [ItemCanBeNull] - private static async Task ResponseBlacklistAdd(ulong steamID, string botNames, string targetSteamIDs) { - if ((steamID == 0) || string.IsNullOrEmpty(botNames) || string.IsNullOrEmpty(targetSteamIDs)) { - ASF.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(botNames) + " || " + nameof(targetSteamIDs)); - - return null; + 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)); } - HashSet bots = Bot.GetBots(botNames); + HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } - IList results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseBlacklistAdd(steamID, targetSteamIDs)))).ConfigureAwait(false); + 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 List(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) || string.IsNullOrEmpty(targetSteamIDs)) { - Bot.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(targetSteamIDs)); - - return 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 (!Bot.HasPermission(steamID, BotConfig.EPermission.Master)) { @@ -1014,32 +956,27 @@ namespace ArchiSteamFarm { return FormatBotResponse(Strings.Done); } - [ItemCanBeNull] - private static async Task ResponseBlacklistRemove(ulong steamID, string botNames, string targetSteamIDs) { - if ((steamID == 0) || string.IsNullOrEmpty(botNames) || string.IsNullOrEmpty(targetSteamIDs)) { - ASF.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(botNames) + " || " + nameof(targetSteamIDs)); - - return null; + 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)); } - HashSet bots = Bot.GetBots(botNames); + HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } - IList results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseBlacklistRemove(steamID, targetSteamIDs)))).ConfigureAwait(false); + 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 List(results.Where(result => !string.IsNullOrEmpty(result))!); return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; } - private static string ResponseExit(ulong steamID) { - if (steamID == 0) { - ASF.ArchiLogger.LogNullError(nameof(steamID)); - - return null; + private static string? ResponseExit(ulong steamID) { + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentNullException(nameof(steamID)); } if (!ASF.IsOwner(steamID)) { @@ -1051,11 +988,9 @@ namespace ArchiSteamFarm { return FormatStaticResponse(success ? message : string.Format(Strings.WarningFailedWithError, message)); } - private async Task ResponseFarm(ulong steamID) { - if (steamID == 0) { - Bot.ArchiLogger.LogNullError(nameof(steamID)); - - return null; + private async Task ResponseFarm(ulong steamID) { + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentNullException(nameof(steamID)); } if (!Bot.HasPermission(steamID, BotConfig.EPermission.Master)) { @@ -1075,42 +1010,35 @@ namespace ArchiSteamFarm { return FormatBotResponse(Strings.Done); } - [ItemCanBeNull] - private static async Task ResponseFarm(ulong steamID, string botNames) { - if ((steamID == 0) || string.IsNullOrEmpty(botNames)) { - ASF.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(botNames)); - - return null; + 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)); } - HashSet bots = Bot.GetBots(botNames); + HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } - IList results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseFarm(steamID))).ConfigureAwait(false); + 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 List(results.Where(result => !string.IsNullOrEmpty(result))!); return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; } - private string ResponseHelp(ulong steamID) { - if (steamID == 0) { - Bot.ArchiLogger.LogNullError(nameof(steamID)); - - return null; + private string? ResponseHelp(ulong steamID) { + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentNullException(nameof(steamID)); } return Bot.HasPermission(steamID, BotConfig.EPermission.FamilySharing) ? FormatBotResponse(SharedInfo.ProjectURL + "/wiki/Commands") : null; } - private string ResponseIdleBlacklist(ulong steamID) { - if (steamID == 0) { - Bot.ArchiLogger.LogNullError(nameof(steamID)); - - return null; + private string? ResponseIdleBlacklist(ulong steamID) { + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentNullException(nameof(steamID)); } if (!Bot.HasPermission(steamID, BotConfig.EPermission.Master)) { @@ -1122,32 +1050,27 @@ namespace ArchiSteamFarm { return FormatBotResponse(idleBlacklist.Count > 0 ? string.Join(", ", idleBlacklist) : string.Format(Strings.ErrorIsEmpty, nameof(idleBlacklist))); } - [ItemCanBeNull] - private static async Task ResponseIdleBlacklist(ulong steamID, string botNames) { - if ((steamID == 0) || string.IsNullOrEmpty(botNames)) { - ASF.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(botNames)); - - return null; + 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)); } - HashSet bots = Bot.GetBots(botNames); + HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } - IList results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseIdleBlacklist(steamID)))).ConfigureAwait(false); + 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 List(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) || string.IsNullOrEmpty(targetAppIDs)) { - Bot.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(targetAppIDs)); - - return 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 (!Bot.HasPermission(steamID, BotConfig.EPermission.Master)) { @@ -1184,32 +1107,27 @@ namespace ArchiSteamFarm { return FormatBotResponse(Strings.Done); } - [ItemCanBeNull] - private static async Task ResponseIdleBlacklistAdd(ulong steamID, string botNames, string targetAppIDs) { - if ((steamID == 0) || string.IsNullOrEmpty(botNames) || string.IsNullOrEmpty(targetAppIDs)) { - ASF.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(botNames) + " || " + nameof(targetAppIDs)); - - return null; + 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)); } - HashSet bots = Bot.GetBots(botNames); + HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } - IList results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseIdleBlacklistAdd(steamID, targetAppIDs)))).ConfigureAwait(false); + 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 List(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) || string.IsNullOrEmpty(targetAppIDs)) { - Bot.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(targetAppIDs)); - - return 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 (!Bot.HasPermission(steamID, BotConfig.EPermission.Master)) { @@ -1237,32 +1155,27 @@ namespace ArchiSteamFarm { return FormatBotResponse(Strings.Done); } - [ItemCanBeNull] - private static async Task ResponseIdleBlacklistRemove(ulong steamID, string botNames, string targetAppIDs) { - if ((steamID == 0) || string.IsNullOrEmpty(botNames) || string.IsNullOrEmpty(targetAppIDs)) { - ASF.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(botNames) + " || " + nameof(targetAppIDs)); - - return null; + 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)); } - HashSet bots = Bot.GetBots(botNames); + HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } - IList results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseIdleBlacklistRemove(steamID, targetAppIDs)))).ConfigureAwait(false); + 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 List(results.Where(result => !string.IsNullOrEmpty(result))!); return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; } - private string ResponseIdleQueue(ulong steamID) { - if (steamID == 0) { - Bot.ArchiLogger.LogNullError(nameof(steamID)); - - return null; + private string? ResponseIdleQueue(ulong steamID) { + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentNullException(nameof(steamID)); } if (!Bot.HasPermission(steamID, BotConfig.EPermission.Master)) { @@ -1274,32 +1187,27 @@ namespace ArchiSteamFarm { return FormatBotResponse(idleQueue.Count > 0 ? string.Join(", ", idleQueue) : string.Format(Strings.ErrorIsEmpty, nameof(idleQueue))); } - [ItemCanBeNull] - private static async Task ResponseIdleQueue(ulong steamID, string botNames) { - if ((steamID == 0) || string.IsNullOrEmpty(botNames)) { - ASF.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(botNames)); - - return null; + 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)); } - HashSet bots = Bot.GetBots(botNames); + HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } - IList results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseIdleQueue(steamID)))).ConfigureAwait(false); + 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 List(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) || string.IsNullOrEmpty(targetAppIDs)) { - Bot.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(targetAppIDs)); - - return 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 (!Bot.HasPermission(steamID, BotConfig.EPermission.Master)) { @@ -1327,32 +1235,27 @@ namespace ArchiSteamFarm { return FormatBotResponse(Strings.Done); } - [ItemCanBeNull] - private static async Task ResponseIdleQueueAdd(ulong steamID, string botNames, string targetAppIDs) { - if ((steamID == 0) || string.IsNullOrEmpty(botNames) || string.IsNullOrEmpty(targetAppIDs)) { - ASF.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(botNames) + " || " + nameof(targetAppIDs)); - - return null; + 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)); } - HashSet bots = Bot.GetBots(botNames); + HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } - IList results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseIdleQueueAdd(steamID, targetAppIDs)))).ConfigureAwait(false); + 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 List(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) || string.IsNullOrEmpty(targetAppIDs)) { - Bot.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(targetAppIDs)); - - return 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 (!Bot.HasPermission(steamID, BotConfig.EPermission.Master)) { @@ -1380,39 +1283,36 @@ namespace ArchiSteamFarm { return FormatBotResponse(Strings.Done); } - [ItemCanBeNull] - private static async Task ResponseIdleQueueRemove(ulong steamID, string botNames, string targetAppIDs) { - if ((steamID == 0) || string.IsNullOrEmpty(botNames) || string.IsNullOrEmpty(targetAppIDs)) { - ASF.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(botNames) + " || " + nameof(targetAppIDs)); - - return null; + 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)); } - HashSet bots = Bot.GetBots(botNames); + HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } - IList results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseIdleQueueRemove(steamID, targetAppIDs)))).ConfigureAwait(false); + 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 List(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) || string.IsNullOrEmpty(propertyName) || string.IsNullOrEmpty(inputValue)) { - ASF.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(propertyName) + " || " + nameof(inputValue)); - - return 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 (!Bot.HasPermission(steamID, BotConfig.EPermission.Master)) { return null; } - if (!ASF.GlobalConfig.Headless) { + bool headless = ASF.GlobalConfig?.Headless ?? GlobalConfig.DefaultHeadless; + + if (!headless) { return FormatBotResponse(Strings.ErrorFunctionOnlyInHeadlessMode); } @@ -1425,32 +1325,27 @@ namespace ArchiSteamFarm { return FormatBotResponse(result ? Strings.Done : Strings.WarningFailed); } - [ItemCanBeNull] - private static async Task ResponseInput(ulong steamID, string botNames, string propertyName, string inputValue) { - if ((steamID == 0) || string.IsNullOrEmpty(botNames) || string.IsNullOrEmpty(propertyName) || string.IsNullOrEmpty(inputValue)) { - ASF.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(botNames) + " || " + nameof(propertyName) + " || " + nameof(inputValue)); - - return null; + 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)); } - HashSet bots = Bot.GetBots(botNames); + HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } - IList results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseInput(steamID, propertyName, inputValue)))).ConfigureAwait(false); + 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 List(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) { - Bot.ArchiLogger.LogNullError(nameof(steamID)); - - return null; + private async Task ResponseLevel(ulong steamID) { + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentNullException(nameof(steamID)); } if (!Bot.IsConnectedAndLoggedOn) { @@ -1466,32 +1361,27 @@ namespace ArchiSteamFarm { return FormatBotResponse(level.HasValue ? string.Format(Strings.BotLevel, level.Value) : Strings.WarningFailed); } - [ItemCanBeNull] - private static async Task ResponseLevel(ulong steamID, string botNames) { - if ((steamID == 0) || string.IsNullOrEmpty(botNames)) { - ASF.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(botNames)); - - return null; + 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)); } - HashSet bots = Bot.GetBots(botNames); + HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } - IList results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseLevel(steamID))).ConfigureAwait(false); + 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 List(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) { - Bot.ArchiLogger.LogNullError(nameof(steamID)); - - return null; + private async Task ResponseLoot(ulong steamID) { + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentNullException(nameof(steamID)); } if (!Bot.HasPermission(steamID, BotConfig.EPermission.Master)) { @@ -1511,32 +1401,27 @@ namespace ArchiSteamFarm { return FormatBotResponse(success ? message : string.Format(Strings.WarningFailedWithError, message)); } - [ItemCanBeNull] - private static async Task ResponseLoot(ulong steamID, string botNames) { - if ((steamID == 0) || string.IsNullOrEmpty(botNames)) { - ASF.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(botNames)); - - return null; + 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)); } - HashSet bots = Bot.GetBots(botNames); + HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } - IList results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseLoot(steamID))).ConfigureAwait(false); + 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 List(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) || string.IsNullOrEmpty(realAppIDsText)) { - Bot.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(realAppIDsText)); - - return 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 (!Bot.HasPermission(steamID, BotConfig.EPermission.Master)) { @@ -1572,32 +1457,27 @@ namespace ArchiSteamFarm { return FormatBotResponse(success ? message : string.Format(Strings.WarningFailedWithError, message)); } - [ItemCanBeNull] - private static async Task ResponseLootByRealAppIDs(ulong steamID, string botNames, string realAppIDsText, bool exclude = false) { - if ((steamID == 0) || string.IsNullOrEmpty(botNames) || string.IsNullOrEmpty(realAppIDsText)) { - ASF.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(botNames) + " || " + nameof(realAppIDsText)); - - return null; + 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)); } - HashSet bots = Bot.GetBots(botNames); + HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } - IList results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseLootByRealAppIDs(steamID, realAppIDsText, exclude))).ConfigureAwait(false); + 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 List(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) || string.IsNullOrEmpty(nickname)) { - Bot.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(nickname)); - - return 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 (!Bot.HasPermission(steamID, BotConfig.EPermission.Master)) { @@ -1613,32 +1493,27 @@ namespace ArchiSteamFarm { return FormatBotResponse(Strings.Done); } - [ItemCanBeNull] - private static async Task ResponseNickname(ulong steamID, string botNames, string nickname) { - if ((steamID == 0) || string.IsNullOrEmpty(botNames) || string.IsNullOrEmpty(nickname)) { - ASF.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(botNames) + " || " + nameof(nickname)); - - return null; + 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)); } - HashSet bots = Bot.GetBots(botNames); + HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } - IList results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseNickname(steamID, nickname)))).ConfigureAwait(false); + 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 List(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) || string.IsNullOrEmpty(query)) { - Bot.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(query)); - - return (null, 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 (!Bot.HasPermission(steamID, BotConfig.EPermission.Operator)) { @@ -1649,7 +1524,7 @@ namespace ArchiSteamFarm { return (FormatBotResponse(Strings.BotNotConnected), null); } - Dictionary gamesOwned = await FetchGamesOwned(true).ConfigureAwait(false); + Dictionary? gamesOwned = await FetchGamesOwned(true).ConfigureAwait(false); StringBuilder response = new StringBuilder(); Dictionary result = new Dictionary(); @@ -1676,14 +1551,14 @@ namespace ArchiSteamFarm { switch (type.ToUpperInvariant()) { case "A" when uint.TryParse(game, out uint appID) && (appID > 0): case "APP" when uint.TryParse(game, out appID) && (appID > 0): - HashSet packageIDs = ASF.GlobalDatabase.GetPackageIDs(appID, Bot.OwnedPackageIDs.Keys); + HashSet? packageIDs = ASF.GlobalDatabase?.GetPackageIDs(appID, Bot.OwnedPackageIDs.Keys); if ((packageIDs != null) && (packageIDs.Count > 0)) { - if ((gamesOwned != null) && gamesOwned.TryGetValue(appID, out string cachedGameName)) { + if ((gamesOwned != null) && gamesOwned.TryGetValue(appID, out string? cachedGameName)) { result["app/" + appID] = cachedGameName; response.AppendLine(FormatBotResponse(string.Format(Strings.BotOwnedAlreadyWithName, "app/" + appID, cachedGameName))); } else { - result["app/" + appID] = null; + result["app/" + appID] = appID.ToString(); response.AppendLine(FormatBotResponse(string.Format(Strings.BotOwnedAlready, "app/" + appID))); } } else { @@ -1697,7 +1572,7 @@ namespace ArchiSteamFarm { } } - if (gamesOwned.TryGetValue(appID, out string gameName)) { + if (gamesOwned.TryGetValue(appID, out string? gameName)) { result["app/" + appID] = gameName; response.AppendLine(FormatBotResponse(string.Format(Strings.BotOwnedAlreadyWithName, "app/" + appID, gameName))); } else { @@ -1746,7 +1621,7 @@ namespace ArchiSteamFarm { 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] = null; + result["sub/" + packageID] = packageID.ToString(); response.AppendLine(FormatBotResponse(string.Format(Strings.BotOwnedAlready, "sub/" + packageID))); } else { response.AppendLine(FormatBotResponse(string.Format(Strings.BotNotOwnedYet, "sub/" + packageID))); @@ -1784,23 +1659,20 @@ namespace ArchiSteamFarm { return (response.Length > 0 ? response.ToString() : FormatBotResponse(string.Format(Strings.BotNotOwnedYet, query)), result); } - [ItemCanBeNull] - private static async Task ResponseOwns(ulong steamID, string botNames, string query) { - if ((steamID == 0) || string.IsNullOrEmpty(botNames) || string.IsNullOrEmpty(query)) { - ASF.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(botNames) + " || " + nameof(query)); - - return null; + 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)); } - HashSet bots = Bot.GetBots(botNames); + HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } - IList<(string Response, Dictionary OwnedGames)> results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseOwns(steamID, query))).ConfigureAwait(false); + 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))); + List<(string Response, Dictionary OwnedGames)> validResults = new List<(string Response, Dictionary OwnedGames)>(results.Where(result => !string.IsNullOrEmpty(result.Response) && (result.OwnedGames != null))!); if (validResults.Count == 0) { return null; @@ -1827,11 +1699,9 @@ namespace ArchiSteamFarm { return string.Join(Environment.NewLine, validResults.Select(result => result.Response).Concat(extraResponses)); } - private string ResponsePassword(ulong steamID) { - if (steamID == 0) { - Bot.ArchiLogger.LogNullError(nameof(steamID)); - - return null; + private string? ResponsePassword(ulong steamID) { + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentNullException(nameof(steamID)); } if (!Bot.HasPermission(steamID, BotConfig.EPermission.Master)) { @@ -1843,39 +1713,34 @@ namespace ArchiSteamFarm { } Dictionary encryptedPasswords = new Dictionary(2) { - { ArchiCryptoHelper.ECryptoMethod.AES, ArchiCryptoHelper.Encrypt(ArchiCryptoHelper.ECryptoMethod.AES, Bot.BotConfig.DecryptedSteamPassword) }, - { ArchiCryptoHelper.ECryptoMethod.ProtectedDataForCurrentUser, ArchiCryptoHelper.Encrypt(ArchiCryptoHelper.ECryptoMethod.ProtectedDataForCurrentUser, Bot.BotConfig.DecryptedSteamPassword) } + { 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)))); } - [ItemCanBeNull] - private static async Task ResponsePassword(ulong steamID, string botNames) { - if ((steamID == 0) || string.IsNullOrEmpty(botNames)) { - ASF.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(botNames)); - - return null; + 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)); } - HashSet bots = Bot.GetBots(botNames); + HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } - IList results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponsePassword(steamID)))).ConfigureAwait(false); + 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 List(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) { - Bot.ArchiLogger.LogNullError(nameof(steamID)); - - return null; + private async Task ResponsePause(ulong steamID, bool permanent, string? resumeInSecondsText = null) { + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentNullException(nameof(steamID)); } if (!Bot.HasPermission(steamID, BotConfig.EPermission.FamilySharing)) { @@ -1897,32 +1762,27 @@ namespace ArchiSteamFarm { return FormatBotResponse(success ? message : string.Format(Strings.WarningFailedWithError, message)); } - [ItemCanBeNull] - private static async Task ResponsePause(ulong steamID, string botNames, bool permanent, string resumeInSecondsText = null) { - if ((steamID == 0) || string.IsNullOrEmpty(botNames)) { - ASF.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(botNames)); - - return null; + 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)); } - HashSet bots = Bot.GetBots(botNames); + HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } - IList results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponsePause(steamID, permanent, resumeInSecondsText))).ConfigureAwait(false); + 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 List(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) || (gameIDs == null) || (gameIDs.Count > ArchiHandler.MaxGamesPlayedConcurrently)) { - Bot.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(gameIDs)); - - return 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 (!Bot.HasPermission(steamID, BotConfig.EPermission.Master)) { @@ -1938,11 +1798,9 @@ namespace ArchiSteamFarm { return FormatBotResponse(success ? message : string.Format(Strings.WarningFailedWithError, message)); } - private async Task ResponsePlay(ulong steamID, string targetGameIDs) { - if ((steamID == 0) || string.IsNullOrEmpty(targetGameIDs)) { - ASF.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(targetGameIDs)); - - return null; + 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 (!Bot.HasPermission(steamID, BotConfig.EPermission.Master)) { @@ -1979,32 +1837,27 @@ namespace ArchiSteamFarm { return await ResponsePlay(steamID, gamesToPlay, gameName.Length > 0 ? gameName.ToString() : null).ConfigureAwait(false); } - [ItemCanBeNull] - private static async Task ResponsePlay(ulong steamID, string botNames, string targetGameIDs) { - if ((steamID == 0) || string.IsNullOrEmpty(botNames) || string.IsNullOrEmpty(targetGameIDs)) { - ASF.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(botNames) + " || " + nameof(targetGameIDs)); - - return null; + 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)); } - HashSet bots = Bot.GetBots(botNames); + HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } - IList results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponsePlay(steamID, targetGameIDs))).ConfigureAwait(false); + 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 List(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) || string.IsNullOrEmpty(privacySettingsText)) { - Bot.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(privacySettingsText)); - - return 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 (!Bot.HasPermission(steamID, BotConfig.EPermission.Master)) { @@ -2131,32 +1984,27 @@ namespace ArchiSteamFarm { return FormatBotResponse(await Bot.ArchiWebHandler.ChangePrivacySettings(userPrivacy).ConfigureAwait(false) ? Strings.Success : Strings.WarningFailed); } - [ItemCanBeNull] - private static async Task ResponsePrivacy(ulong steamID, string botNames, string privacySettingsText) { - if ((steamID == 0) || string.IsNullOrEmpty(botNames) || string.IsNullOrEmpty(privacySettingsText)) { - ASF.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(botNames) + " || " + nameof(privacySettingsText)); - - return null; + 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)); } - HashSet bots = Bot.GetBots(botNames); + HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } - IList results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponsePrivacy(steamID, privacySettingsText))).ConfigureAwait(false); + 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 List(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) || string.IsNullOrEmpty(keysText)) { - Bot.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(keysText)); - - return null; + private async Task ResponseRedeem(ulong steamID, string keysText, ERedeemFlags redeemFlags = ERedeemFlags.None) { + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || string.IsNullOrEmpty(keysText)) { + throw new ArgumentNullException(nameof(steamID) + " || " + nameof(keysText)); } if (!Bot.HasPermission(steamID, BotConfig.EPermission.Operator)) { @@ -2188,14 +2036,14 @@ namespace ArchiSteamFarm { using (HashSet.Enumerator keysEnumerator = pendingKeys.GetEnumerator()) { // Initial key - string key = keysEnumerator.MoveNext() ? keysEnumerator.Current : null; - string previousKey = key; + string? key = keysEnumerator.MoveNext() ? keysEnumerator.Current : null; + string? previousKey = key; while (!string.IsNullOrEmpty(key)) { - string startingKey = 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()) { - Bot currentBot = Bot; + 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()) { + Bot? currentBot = Bot; while (!string.IsNullOrEmpty(key) && (currentBot != null)) { if (previousKey != key) { @@ -2203,7 +2051,7 @@ namespace ArchiSteamFarm { previousKey = key; } - if (redeemFlags.HasFlag(ERedeemFlags.Validate) && !Utilities.IsValidCdKey(key)) { + if (redeemFlags.HasFlag(ERedeemFlags.Validate) && !Utilities.IsValidCdKey(key!)) { // Next key key = keysEnumerator.MoveNext() ? keysEnumerator.Current : null; @@ -2217,7 +2065,7 @@ namespace ArchiSteamFarm { } else { bool skipRequest = triedBots.Contains(currentBot) || rateLimitedBots.Contains(currentBot); - ArchiHandler.PurchaseResponseCallback result = skipRequest ? new ArchiHandler.PurchaseResponseCallback(EResult.Fail, EPurchaseResultDetail.CancelledByUser) : await currentBot.Actions.RedeemKey(key).ConfigureAwait(false); + 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)); @@ -2229,7 +2077,7 @@ namespace ArchiSteamFarm { if (((result.PurchaseResultDetail == EPurchaseResultDetail.CannotRedeemCodeFromClient) || ((result.PurchaseResultDetail == EPurchaseResultDetail.BadActivationCode) && assumeWalletKeyOnBadActivationCode)) && (Bot.WalletCurrency != ECurrencyCode.Invalid)) { // If it's a wallet code, we try to redeem it first, then handle the inner result as our primary one - (EResult Result, EPurchaseResultDetail? PurchaseResult)? walletResult = await currentBot.ArchiWebHandler.RedeemWalletKey(key).ConfigureAwait(false); + (EResult Result, EPurchaseResultDetail? PurchaseResult)? walletResult = await currentBot.ArchiWebHandler.RedeemWalletKey(key!).ConfigureAwait(false); if (walletResult != null) { result.Result = walletResult.Value.Result; @@ -2255,7 +2103,7 @@ namespace ArchiSteamFarm { case EPurchaseResultDetail.NoDetail: // OK case EPurchaseResultDetail.Timeout: if ((result.Result != EResult.Timeout) && (result.PurchaseResultDetail != EPurchaseResultDetail.Timeout)) { - unusedKeys.Remove(key); + unusedKeys.Remove(key!); } // Next key @@ -2291,7 +2139,7 @@ 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)) { - ArchiHandler.PurchaseResponseCallback otherResult = await innerBot.Actions.RedeemKey(key).ConfigureAwait(false); + 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)); @@ -2307,7 +2155,7 @@ namespace ArchiSteamFarm { case EPurchaseResultDetail.NoDetail: // OK // This key is already handled, as we either redeemed it or we're sure it's dupe/invalid alreadyHandled = true; - unusedKeys.Remove(key); + unusedKeys.Remove(key!); break; case EPurchaseResultDetail.RateLimited: @@ -2347,7 +2195,7 @@ namespace ArchiSteamFarm { default: ASF.ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(result.PurchaseResultDetail), result.PurchaseResultDetail)); - unusedKeys.Remove(key); + unusedKeys.Remove(key!); // Next key key = keysEnumerator.MoveNext() ? keysEnumerator.Current : null; @@ -2381,53 +2229,27 @@ namespace ArchiSteamFarm { return response.Length > 0 ? response.ToString() : null; } - [ItemCanBeNull] - private static async Task ResponseRedeem(ulong steamID, string botNames, string keys, ERedeemFlags redeemFlags = ERedeemFlags.None) { - if ((steamID == 0) || string.IsNullOrEmpty(botNames) || string.IsNullOrEmpty(keys)) { - ASF.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(botNames) + " || " + nameof(keys)); - - return 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)); } - HashSet bots = Bot.GetBots(botNames); + HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(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, keys, redeemFlags))).ConfigureAwait(false); - List responses = new List(results.Where(result => !string.IsNullOrEmpty(result))); + List responses = new List(results.Where(result => !string.IsNullOrEmpty(result))!); return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; } - [ItemCanBeNull] - private static async Task ResponseReset(ulong steamID, string botNames) { - if ((steamID == 0) || string.IsNullOrEmpty(botNames)) { - ASF.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(botNames)); - - return null; - } - - HashSet bots = Bot.GetBots(botNames); - - if ((bots == null) || (bots.Count == 0)) { - return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(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))); - - return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; - } - - private async Task ResponseReset(ulong steamID) { - if (steamID == 0) { - Bot.ArchiLogger.LogNullError(nameof(steamID)); - - return null; + private async Task ResponseReset(ulong steamID) { + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentNullException(nameof(steamID)); } if (!Bot.HasPermission(steamID, BotConfig.EPermission.Master)) { @@ -2443,11 +2265,27 @@ namespace ArchiSteamFarm { return FormatBotResponse(success ? message : string.Format(Strings.WarningFailedWithError, message)); } - private static string ResponseRestart(ulong steamID) { - if (steamID == 0) { - ASF.ArchiLogger.LogNullError(nameof(steamID)); + 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)); + } - return null; + HashSet? bots = Bot.GetBots(botNames); + + if ((bots == null) || (bots.Count == 0)) { + return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(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))!); + + 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)); } if (!ASF.IsOwner(steamID)) { @@ -2459,11 +2297,9 @@ namespace ArchiSteamFarm { return FormatStaticResponse(success ? message : string.Format(Strings.WarningFailedWithError, message)); } - private string ResponseResume(ulong steamID) { - if (steamID == 0) { - Bot.ArchiLogger.LogNullError(nameof(steamID)); - - return null; + private string? ResponseResume(ulong steamID) { + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentNullException(nameof(steamID)); } if (!Bot.HasPermission(steamID, BotConfig.EPermission.FamilySharing)) { @@ -2475,32 +2311,27 @@ namespace ArchiSteamFarm { return FormatBotResponse(success ? message : string.Format(Strings.WarningFailedWithError, message)); } - [ItemCanBeNull] - private static async Task ResponseResume(ulong steamID, string botNames) { - if ((steamID == 0) || string.IsNullOrEmpty(botNames)) { - ASF.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(botNames)); - - return null; + 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)); } - HashSet bots = Bot.GetBots(botNames); + HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } - IList results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseResume(steamID)))).ConfigureAwait(false); + 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 List(results.Where(result => !string.IsNullOrEmpty(result))!); return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; } - private string ResponseStart(ulong steamID) { - if (steamID == 0) { - Bot.ArchiLogger.LogNullError(nameof(steamID)); - - return null; + private string? ResponseStart(ulong steamID) { + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentNullException(nameof(steamID)); } if (!Bot.HasPermission(steamID, BotConfig.EPermission.Master)) { @@ -2512,32 +2343,27 @@ namespace ArchiSteamFarm { return FormatBotResponse(success ? message : string.Format(Strings.WarningFailedWithError, message)); } - [ItemCanBeNull] - private static async Task ResponseStart(ulong steamID, string botNames) { - if ((steamID == 0) || string.IsNullOrEmpty(botNames)) { - ASF.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(botNames)); - - return null; + 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)); } - HashSet bots = Bot.GetBots(botNames); + HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } - IList results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseStart(steamID)))).ConfigureAwait(false); + 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 List(results.Where(result => !string.IsNullOrEmpty(result))!); return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; } - private string ResponseStats(ulong steamID) { - if (steamID == 0) { - Bot.ArchiLogger.LogNullError(nameof(steamID)); - - return null; + private string? ResponseStats(ulong steamID) { + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentNullException(nameof(steamID)); } if (!ASF.IsOwner(steamID)) { @@ -2550,11 +2376,9 @@ namespace ArchiSteamFarm { return FormatBotResponse(string.Format(Strings.BotStats, memoryInMegabytes, uptime.ToHumanReadable())); } - private (string Response, Bot Bot) ResponseStatus(ulong steamID) { - if (steamID == 0) { - Bot.ArchiLogger.LogNullError(nameof(steamID)); - - return (null, Bot); + private (string? Response, Bot Bot) ResponseStatus(ulong steamID) { + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentNullException(nameof(steamID)); } if (!Bot.HasPermission(steamID, BotConfig.EPermission.FamilySharing)) { @@ -2594,23 +2418,20 @@ namespace ArchiSteamFarm { 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); } - [ItemCanBeNull] - private static async Task ResponseStatus(ulong steamID, string botNames) { - if ((steamID == 0) || string.IsNullOrEmpty(botNames)) { - ASF.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(botNames)); - - return null; + 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)); } - HashSet bots = Bot.GetBots(botNames); + HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } - IList<(string Response, Bot Bot)> results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseStatus(steamID)))).ConfigureAwait(false); + 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 List<(string Response, Bot Bot)>(results.Where(result => !string.IsNullOrEmpty(result.Response))!); if (validResults.Count == 0) { return null; @@ -2623,11 +2444,9 @@ namespace ArchiSteamFarm { return string.Join(Environment.NewLine, validResults.Select(result => result.Response).Union(extraResponse.ToEnumerable())); } - private string ResponseStop(ulong steamID) { - if (steamID == 0) { - Bot.ArchiLogger.LogNullError(nameof(steamID)); - - return null; + private string? ResponseStop(ulong steamID) { + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentNullException(nameof(steamID)); } if (!Bot.HasPermission(steamID, BotConfig.EPermission.Master)) { @@ -2639,32 +2458,27 @@ namespace ArchiSteamFarm { return FormatBotResponse(success ? message : string.Format(Strings.WarningFailedWithError, message)); } - [ItemCanBeNull] - private static async Task ResponseStop(ulong steamID, string botNames) { - if ((steamID == 0) || string.IsNullOrEmpty(botNames)) { - ASF.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(botNames)); - - return null; + 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)); } - HashSet bots = Bot.GetBots(botNames); + HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } - IList results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseStop(steamID)))).ConfigureAwait(false); + 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 List(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) || string.IsNullOrEmpty(botNameTo)) { - ASF.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(botNameTo)); - - return 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 (!Bot.HasPermission(steamID, BotConfig.EPermission.Master)) { @@ -2679,7 +2493,7 @@ namespace ArchiSteamFarm { return FormatBotResponse(string.Format(Strings.ErrorIsEmpty, nameof(Bot.BotConfig.TransferableTypes))); } - Bot targetBot = Bot.GetBot(botNameTo); + Bot? targetBot = Bot.GetBot(botNameTo); if (targetBot == null) { return ASF.IsOwner(steamID) ? FormatBotResponse(string.Format(Strings.BotNotFound, botNameTo)) : null; @@ -2698,32 +2512,27 @@ namespace ArchiSteamFarm { return FormatBotResponse(success ? message : string.Format(Strings.WarningFailedWithError, message)); } - [ItemCanBeNull] - private static async Task ResponseTransfer(ulong steamID, string botNames, string botNameTo) { - if ((steamID == 0) || string.IsNullOrEmpty(botNames) || string.IsNullOrEmpty(botNameTo)) { - ASF.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(botNames) + " || " + nameof(botNameTo)); - - return null; + 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)); } - HashSet bots = Bot.GetBots(botNames); + HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } - IList results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseTransfer(steamID, botNameTo))).ConfigureAwait(false); + 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 List(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) || (realAppIDs == null) || (realAppIDs.Count == 0) || (targetBot == null)) { - Bot.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(realAppIDs) + " || " + nameof(targetBot)); - - return 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 (!Bot.HasPermission(steamID, BotConfig.EPermission.Master)) { @@ -2751,18 +2560,16 @@ namespace ArchiSteamFarm { return FormatBotResponse(success ? message : string.Format(Strings.WarningFailedWithError, message)); } - private async Task ResponseTransferByRealAppIDs(ulong steamID, string realAppIDsText, string botNameTo, bool exclude = false) { - if ((steamID == 0) || string.IsNullOrEmpty(realAppIDsText) || string.IsNullOrEmpty(botNameTo)) { - Bot.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(realAppIDsText) + " || " + nameof(botNameTo)); - - return null; + 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 (!Bot.HasPermission(steamID, BotConfig.EPermission.Master)) { return null; } - Bot targetBot = Bot.GetBot(botNameTo); + Bot? targetBot = Bot.GetBot(botNameTo); if (targetBot == null) { return ASF.IsOwner(steamID) ? FormatBotResponse(string.Format(Strings.BotNotFound, botNameTo)) : null; @@ -2787,14 +2594,12 @@ namespace ArchiSteamFarm { return await ResponseTransferByRealAppIDs(steamID, realAppIDs, targetBot, exclude).ConfigureAwait(false); } - private static async Task ResponseTransferByRealAppIDs(ulong steamID, string botNames, string realAppIDsText, string botNameTo, bool exclude = false) { - if ((steamID == 0) || string.IsNullOrEmpty(botNames) || string.IsNullOrEmpty(realAppIDsText) || string.IsNullOrEmpty(botNameTo)) { - ASF.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(botNames) + " || " + nameof(realAppIDsText) + " || " + nameof(botNameTo)); - - return null; + 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)); } - HashSet bots = Bot.GetBots(botNames); + HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; @@ -2816,34 +2621,30 @@ namespace ArchiSteamFarm { realAppIDs.Add(appID); } - Bot targetBot = Bot.GetBot(botNameTo); + Bot? targetBot = Bot.GetBot(botNameTo); if (targetBot == null) { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNameTo)) : null; } - IList results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseTransferByRealAppIDs(steamID, realAppIDs, targetBot, exclude))).ConfigureAwait(false); + 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 List(results.Where(result => !string.IsNullOrEmpty(result))!); return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; } - private string ResponseUnknown(ulong steamID) { - if (steamID == 0) { - Bot.ArchiLogger.LogNullError(nameof(steamID)); - - return null; + private string? ResponseUnknown(ulong steamID) { + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentNullException(nameof(steamID)); } return Bot.HasPermission(steamID, BotConfig.EPermission.Operator) ? FormatBotResponse(Strings.UnknownCommand) : null; } - private async Task ResponseUnpackBoosters(ulong steamID) { - if (steamID == 0) { - Bot.ArchiLogger.LogNullError(nameof(steamID)); - - return null; + private async Task ResponseUnpackBoosters(ulong steamID) { + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentNullException(nameof(steamID)); } if (!Bot.HasPermission(steamID, BotConfig.EPermission.Master)) { @@ -2877,58 +2678,49 @@ namespace ArchiSteamFarm { return FormatBotResponse(completeSuccess ? Strings.Success : Strings.Done); } - [ItemCanBeNull] - private static async Task ResponseUnpackBoosters(ulong steamID, string botNames) { - if ((steamID == 0) || string.IsNullOrEmpty(botNames)) { - ASF.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(botNames)); - - return null; + 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)); } - HashSet bots = Bot.GetBots(botNames); + HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } - IList results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseUnpackBoosters(steamID))).ConfigureAwait(false); + 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 List(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) { - ASF.ArchiLogger.LogNullError(nameof(steamID)); - - return null; + private static async Task ResponseUpdate(ulong steamID) { + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentNullException(nameof(steamID)); } if (!ASF.IsOwner(steamID)) { return null; } - (bool success, string message, Version version) = await Actions.Update().ConfigureAwait(false); + (bool success, string? message, Version? version) = await Actions.Update().ConfigureAwait(false); return FormatStaticResponse((success ? Strings.Success : Strings.WarningFailed) + (!string.IsNullOrEmpty(message) ? " " + message : version != null ? " " + version : "")); } - private string ResponseVersion(ulong steamID) { - if (steamID == 0) { - Bot.ArchiLogger.LogNullError(nameof(steamID)); - - return null; + private string? ResponseVersion(ulong steamID) { + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentNullException(nameof(steamID)); } return Bot.HasPermission(steamID, BotConfig.EPermission.Operator) ? FormatBotResponse(string.Format(Strings.BotVersion, SharedInfo.ASF, SharedInfo.Version)) : null; } - private string ResponseWalletBalance(ulong steamID) { - if (steamID == 0) { - Bot.ArchiLogger.LogNullError(nameof(steamID)); - - return null; + private string? ResponseWalletBalance(ulong steamID) { + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentNullException(nameof(steamID)); } if (!Bot.HasPermission(steamID, BotConfig.EPermission.Master)) { @@ -2938,23 +2730,20 @@ namespace ArchiSteamFarm { return !Bot.IsConnectedAndLoggedOn ? FormatBotResponse(Strings.BotNotConnected) : FormatBotResponse(Bot.WalletCurrency != ECurrencyCode.Invalid ? string.Format(Strings.BotWalletBalance, Bot.WalletBalance / 100.0, Bot.WalletCurrency.ToString()) : Strings.BotHasNoWallet); } - [ItemCanBeNull] - private static async Task ResponseWalletBalance(ulong steamID, string botNames) { - if ((steamID == 0) || string.IsNullOrEmpty(botNames)) { - ASF.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(botNames)); - - return null; + 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)); } - HashSet bots = Bot.GetBots(botNames); + HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } - IList results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseWalletBalance(steamID)))).ConfigureAwait(false); + 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 List(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 df0aafca1..fa3578f11 100644 --- a/ArchiSteamFarm/Debugging.cs +++ b/ArchiSteamFarm/Debugging.cs @@ -19,6 +19,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +using System; using SteamKit2; namespace ArchiSteamFarm { @@ -29,16 +30,14 @@ namespace ArchiSteamFarm { internal static bool IsDebugBuild => false; #endif - internal static bool IsDebugConfigured => ASF.GlobalConfig.Debug; + internal static bool IsDebugConfigured => ASF.GlobalConfig?.Debug ?? throw new ArgumentNullException(nameof(ASF.GlobalConfig)); internal static bool IsUserDebugging => IsDebugBuild || IsDebugConfigured; internal sealed class DebugListener : IDebugListener { public void WriteLine(string category, string msg) { if (string.IsNullOrEmpty(category) && string.IsNullOrEmpty(msg)) { - ASF.ArchiLogger.LogNullError(nameof(category) + " && " + nameof(msg)); - - return; + throw new ArgumentNullException(nameof(category) + " && " + nameof(msg)); } ASF.ArchiLogger.LogGenericDebug(category + " | " + msg); diff --git a/ArchiSteamFarm/Events.cs b/ArchiSteamFarm/Events.cs index 99970c343..d7f02d574 100644 --- a/ArchiSteamFarm/Events.cs +++ b/ArchiSteamFarm/Events.cs @@ -26,7 +26,7 @@ using ArchiSteamFarm.Localization; namespace ArchiSteamFarm { internal static class Events { internal static async Task OnBotShutdown() { - if (Program.ProcessRequired || Bot.Bots.Values.Any(bot => bot.KeepRunning)) { + if (Program.ProcessRequired || ((Bot.Bots != null) && Bot.Bots.Values.Any(bot => bot.KeepRunning))) { return; } @@ -35,7 +35,7 @@ namespace ArchiSteamFarm { // We give user extra 5 seconds for eventual config changes await Task.Delay(5000).ConfigureAwait(false); - if (Program.ProcessRequired || Bot.Bots.Values.Any(bot => bot.KeepRunning)) { + if (Program.ProcessRequired || ((Bot.Bots != null) && Bot.Bots.Values.Any(bot => bot.KeepRunning))) { return; } diff --git a/ArchiSteamFarm/GitHub.cs b/ArchiSteamFarm/GitHub.cs index 65a1cee97..6769d23b1 100644 --- a/ArchiSteamFarm/GitHub.cs +++ b/ArchiSteamFarm/GitHub.cs @@ -25,7 +25,6 @@ using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using System.Threading.Tasks; -using JetBrains.Annotations; using Markdig; using Markdig.Renderers; using Markdig.Syntax; @@ -34,35 +33,29 @@ using Newtonsoft.Json; namespace ArchiSteamFarm { internal static class GitHub { - [ItemCanBeNull] - internal static async Task GetLatestRelease(bool stable = true) { + internal static async Task GetLatestRelease(bool stable = true) { string releaseURL = SharedInfo.GithubReleaseURL + (stable ? "/latest" : "?per_page=1"); if (stable) { return await GetReleaseFromURL(releaseURL).ConfigureAwait(false); } - ImmutableList response = await GetReleasesFromURL(releaseURL).ConfigureAwait(false); + ImmutableList? response = await GetReleasesFromURL(releaseURL).ConfigureAwait(false); return response?.FirstOrDefault(); } - [ItemCanBeNull] - internal static async Task GetRelease(string version) { + internal static async Task GetRelease(string version) { if (string.IsNullOrEmpty(version)) { - ASF.ArchiLogger.LogNullError(nameof(version)); - - return null; + throw new ArgumentNullException(nameof(version)); } return await GetReleaseFromURL(SharedInfo.GithubReleaseURL + "/tags/" + version).ConfigureAwait(false); } - private static MarkdownDocument ExtractChangelogFromBody(string markdownText) { + private static MarkdownDocument? ExtractChangelogFromBody(string markdownText) { if (string.IsNullOrEmpty(markdownText)) { - ASF.ArchiLogger.LogNullError(nameof(markdownText)); - - return null; + throw new ArgumentNullException(nameof(markdownText)); } MarkdownDocument markdownDocument = Markdown.Parse(markdownText); @@ -77,28 +70,22 @@ namespace ArchiSteamFarm { return result; } - [ItemCanBeNull] - private static async Task GetReleaseFromURL(string releaseURL) { - if (string.IsNullOrEmpty(releaseURL)) { - ASF.ArchiLogger.LogNullError(nameof(releaseURL)); - - return null; + private static async Task GetReleaseFromURL(string releaseURL) { + if ((ASF.WebBrowser == null) || string.IsNullOrEmpty(releaseURL)) { + throw new ArgumentNullException(nameof(ASF.WebBrowser) + " || " + nameof(releaseURL)); } - WebBrowser.ObjectResponse objectResponse = await ASF.WebBrowser.UrlGetToJsonObject(releaseURL).ConfigureAwait(false); + WebBrowser.ObjectResponse? objectResponse = await ASF.WebBrowser.UrlGetToJsonObject(releaseURL).ConfigureAwait(false); return objectResponse?.Content; } - [ItemCanBeNull] - private static async Task> GetReleasesFromURL(string releaseURL) { - if (string.IsNullOrEmpty(releaseURL)) { - ASF.ArchiLogger.LogNullError(nameof(releaseURL)); - - return null; + private static async Task?> GetReleasesFromURL(string releaseURL) { + if ((ASF.WebBrowser == null) || string.IsNullOrEmpty(releaseURL)) { + throw new ArgumentNullException(nameof(ASF.WebBrowser) + " || " + nameof(releaseURL)); } - WebBrowser.ObjectResponse> objectResponse = await ASF.WebBrowser.UrlGetToJsonObject>(releaseURL).ConfigureAwait(false); + WebBrowser.ObjectResponse>? objectResponse = await ASF.WebBrowser.UrlGetToJsonObject>(releaseURL).ConfigureAwait(false); return objectResponse?.Content; } @@ -106,7 +93,7 @@ namespace ArchiSteamFarm { [SuppressMessage("ReSharper", "ClassCannotBeInstantiated")] internal sealed class ReleaseResponse { [JsonProperty(PropertyName = "assets", Required = Required.Always)] - internal readonly ImmutableHashSet Assets; + internal readonly ImmutableHashSet? Assets; [JsonProperty(PropertyName = "prerelease", Required = Required.Always)] internal readonly bool IsPreRelease; @@ -115,9 +102,9 @@ namespace ArchiSteamFarm { internal readonly DateTime PublishedAt; [JsonProperty(PropertyName = "tag_name", Required = Required.Always)] - internal readonly string Tag; + internal readonly string? Tag; - internal string ChangelogHTML { + internal string? ChangelogHTML { get { if (BackingChangelogHTML != null) { return BackingChangelogHTML; @@ -139,7 +126,7 @@ namespace ArchiSteamFarm { } } - internal string ChangelogPlainText { + internal string? ChangelogPlainText { get { if (BackingChangelogPlainText != null) { return BackingChangelogPlainText; @@ -167,32 +154,38 @@ namespace ArchiSteamFarm { #pragma warning disable 649 [JsonProperty(PropertyName = "body", Required = Required.Always)] - private readonly string MarkdownBody; + private readonly string? MarkdownBody; #pragma warning restore 649 - private MarkdownDocument Changelog { + private MarkdownDocument? Changelog { get { if (BackingChangelog != null) { return BackingChangelog; } - return BackingChangelog = ExtractChangelogFromBody(MarkdownBody); + if (string.IsNullOrEmpty(MarkdownBody)) { + ASF.ArchiLogger.LogNullError(nameof(MarkdownBody)); + + return null; + } + + return BackingChangelog = ExtractChangelogFromBody(MarkdownBody!); } } - private MarkdownDocument BackingChangelog; - private string BackingChangelogHTML; - private string BackingChangelogPlainText; + private MarkdownDocument? BackingChangelog; + private string? BackingChangelogHTML; + private string? BackingChangelogPlainText; [JsonConstructor] private ReleaseResponse() { } internal sealed class Asset { [JsonProperty(PropertyName = "browser_download_url", Required = Required.Always)] - internal readonly string DownloadURL; + internal readonly string? DownloadURL; [JsonProperty(PropertyName = "name", Required = Required.Always)] - internal readonly string Name; + internal readonly string? Name; [JsonProperty(PropertyName = "size", Required = Required.Always)] internal readonly uint Size; diff --git a/ArchiSteamFarm/GlobalConfig.cs b/ArchiSteamFarm/GlobalConfig.cs index f37ae2ec9..af2bb434b 100644 --- a/ArchiSteamFarm/GlobalConfig.cs +++ b/ArchiSteamFarm/GlobalConfig.cs @@ -36,31 +36,31 @@ using SteamKit2; namespace ArchiSteamFarm { [SuppressMessage("ReSharper", "ClassCannotBeInstantiated")] public sealed class GlobalConfig { + internal const string DefaultCommandPrefix = "!"; + internal const byte DefaultConfirmationsLimiterDelay = 10; + internal const byte DefaultConnectionTimeout = 90; + internal const byte DefaultFarmingDelay = 15; + internal const byte DefaultGiftsLimiterDelay = 1; + internal const bool DefaultHeadless = false; + internal const byte DefaultIdleFarmingPeriod = 8; + internal const byte DefaultInventoryLimiterDelay = 3; + internal const string DefaultIPCPassword = null; internal const byte DefaultLoginLimiterDelay = 10; + internal const byte DefaultMaxFarmingTime = 10; + internal const byte DefaultMaxTradeHoldDuration = 15; + internal const string DefaultSteamMessagePrefix = "/me "; + internal const ulong DefaultSteamOwnerID = 0; + internal const ushort DefaultWebLimiterDelay = 300; private const bool DefaultAutoRestart = true; - private const string DefaultCommandPrefix = "!"; - private const byte DefaultConfirmationsLimiterDelay = 10; - private const byte DefaultConnectionTimeout = 90; private const string DefaultCurrentCulture = null; private const bool DefaultDebug = false; - private const byte DefaultFarmingDelay = 15; - private const byte DefaultGiftsLimiterDelay = 1; - private const bool DefaultHeadless = false; - private const byte DefaultIdleFarmingPeriod = 8; - private const byte DefaultInventoryLimiterDelay = 3; private const bool DefaultIPC = false; - private const string DefaultIPCPassword = null; - private const byte DefaultMaxFarmingTime = 10; - private const byte DefaultMaxTradeHoldDuration = 15; private const EOptimizationMode DefaultOptimizationMode = EOptimizationMode.MaxPerformance; private const bool DefaultStatistics = true; - private const string DefaultSteamMessagePrefix = "/me "; - private const ulong DefaultSteamOwnerID = 0; private const ProtocolTypes DefaultSteamProtocols = ProtocolTypes.All; private const EUpdateChannel DefaultUpdateChannel = EUpdateChannel.Stable; private const byte DefaultUpdatePeriod = 24; - private const ushort DefaultWebLimiterDelay = 300; private const string DefaultWebProxyPassword = null; private const string DefaultWebProxyText = null; private const string DefaultWebProxyUsername = null; @@ -75,7 +75,7 @@ namespace ArchiSteamFarm { public readonly ImmutableHashSet Blacklist = DefaultBlacklist; [JsonProperty] - public readonly string CommandPrefix = DefaultCommandPrefix; + public readonly string? CommandPrefix = DefaultCommandPrefix; [JsonProperty(Required = Required.DisallowNull)] public readonly byte ConfirmationsLimiterDelay = DefaultConfirmationsLimiterDelay; @@ -84,7 +84,7 @@ namespace ArchiSteamFarm { public readonly byte ConnectionTimeout = DefaultConnectionTimeout; [JsonProperty] - public readonly string CurrentCulture = DefaultCurrentCulture; + public readonly string? CurrentCulture = DefaultCurrentCulture; [JsonProperty(Required = Required.DisallowNull)] public readonly bool Debug = DefaultDebug; @@ -108,7 +108,7 @@ namespace ArchiSteamFarm { public readonly bool IPC = DefaultIPC; [JsonProperty] - public readonly string IPCPassword = DefaultIPCPassword; + public readonly string? IPCPassword = DefaultIPCPassword; [JsonProperty(Required = Required.DisallowNull)] public readonly byte LoginLimiterDelay = DefaultLoginLimiterDelay; @@ -126,7 +126,7 @@ namespace ArchiSteamFarm { public readonly bool Statistics = DefaultStatistics; [JsonProperty] - public readonly string SteamMessagePrefix = DefaultSteamMessagePrefix; + public readonly string? SteamMessagePrefix = DefaultSteamMessagePrefix; [JsonProperty(Required = Required.DisallowNull)] public readonly EUpdateChannel UpdateChannel = DefaultUpdateChannel; @@ -138,14 +138,14 @@ namespace ArchiSteamFarm { public readonly ushort WebLimiterDelay = DefaultWebLimiterDelay; [JsonProperty(PropertyName = nameof(WebProxy))] - public readonly string WebProxyText = DefaultWebProxyText; + public readonly string? WebProxyText = DefaultWebProxyText; [JsonProperty] - public readonly string WebProxyUsername = DefaultWebProxyUsername; + public readonly string? WebProxyUsername = DefaultWebProxyUsername; [JsonIgnore] [PublicAPI] - public WebProxy WebProxy { + public WebProxy? WebProxy { get { if (BackingWebProxy != null) { return BackingWebProxy; @@ -197,7 +197,7 @@ namespace ArchiSteamFarm { public ProtocolTypes SteamProtocols { get; private set; } = DefaultSteamProtocols; [JsonExtensionData] - internal Dictionary AdditionalProperties { + internal Dictionary? AdditionalProperties { get; [UsedImplicitly] set; @@ -209,7 +209,7 @@ namespace ArchiSteamFarm { internal bool ShouldSerializeSensitiveDetails { private get; set; } [JsonProperty] - internal string WebProxyPassword { + internal string? WebProxyPassword { get => BackingWebProxyPassword; set { @@ -218,11 +218,11 @@ namespace ArchiSteamFarm { } } - private WebProxy BackingWebProxy; - private string BackingWebProxyPassword = DefaultWebProxyPassword; + private WebProxy? BackingWebProxy; + private string? BackingWebProxyPassword = DefaultWebProxyPassword; [JsonProperty(PropertyName = SharedInfo.UlongCompatibilityStringPrefix + nameof(SteamOwnerID), Required = Required.DisallowNull)] - [JetBrains.Annotations.NotNull] + private string SSteamOwnerID { get => SteamOwnerID.ToString(); @@ -240,7 +240,7 @@ namespace ArchiSteamFarm { [JsonConstructor] internal GlobalConfig() { } - internal (bool Valid, string ErrorMessage) CheckValidation() { + internal (bool Valid, string? ErrorMessage) CheckValidation() { if (ConnectionTimeout == 0) { return (false, string.Format(Strings.ErrorConfigPropertyInvalid, nameof(ConnectionTimeout), ConnectionTimeout)); } @@ -257,7 +257,7 @@ namespace ArchiSteamFarm { return (false, string.Format(Strings.ErrorConfigPropertyInvalid, nameof(OptimizationMode), OptimizationMode)); } - if (!string.IsNullOrEmpty(SteamMessagePrefix) && (SteamMessagePrefix.Length > Bot.MaxMessagePrefixLength)) { + if (!string.IsNullOrEmpty(SteamMessagePrefix) && (SteamMessagePrefix!.Length > Bot.MaxMessagePrefixLength)) { return (false, string.Format(Strings.ErrorConfigPropertyInvalid, nameof(SteamMessagePrefix), SteamMessagePrefix)); } @@ -269,15 +269,12 @@ namespace ArchiSteamFarm { return (false, string.Format(Strings.ErrorConfigPropertyInvalid, nameof(SteamProtocols), SteamProtocols)); } - return Enum.IsDefined(typeof(EUpdateChannel), UpdateChannel) ? (true, null) : (false, string.Format(Strings.ErrorConfigPropertyInvalid, nameof(UpdateChannel), UpdateChannel)); + return Enum.IsDefined(typeof(EUpdateChannel), UpdateChannel) ? (true, (string?) null) : (false, string.Format(Strings.ErrorConfigPropertyInvalid, nameof(UpdateChannel), UpdateChannel)); } - [ItemCanBeNull] - internal static async Task Load(string filePath) { + internal static async Task Load(string filePath) { if (string.IsNullOrEmpty(filePath)) { - ASF.ArchiLogger.LogNullError(nameof(filePath)); - - return null; + throw new ArgumentNullException(nameof(filePath)); } if (!File.Exists(filePath)) { @@ -308,10 +305,12 @@ namespace ArchiSteamFarm { return null; } - (bool valid, string errorMessage) = globalConfig.CheckValidation(); + (bool valid, string? errorMessage) = globalConfig.CheckValidation(); if (!valid) { - ASF.ArchiLogger.LogGenericError(errorMessage); + if (!string.IsNullOrEmpty(errorMessage)) { + ASF.ArchiLogger.LogGenericError(errorMessage!); + } return null; } @@ -321,9 +320,7 @@ namespace ArchiSteamFarm { internal static async Task Write(string filePath, GlobalConfig globalConfig) { if (string.IsNullOrEmpty(filePath) || (globalConfig == null)) { - ASF.ArchiLogger.LogNullError(nameof(filePath) + " || " + nameof(globalConfig)); - - return false; + throw new ArgumentNullException(nameof(filePath) + " || " + nameof(globalConfig)); } string json = JsonConvert.SerializeObject(globalConfig, Formatting.Indented); diff --git a/ArchiSteamFarm/GlobalDatabase.cs b/ArchiSteamFarm/GlobalDatabase.cs index c30a3b9ab..b7cbb8699 100644 --- a/ArchiSteamFarm/GlobalDatabase.cs +++ b/ArchiSteamFarm/GlobalDatabase.cs @@ -44,7 +44,7 @@ namespace ArchiSteamFarm { [JsonIgnore] [PublicAPI] - public IReadOnlyDictionary AppIDs)> PackagesDataReadOnly => PackagesData; + public IReadOnlyDictionary? AppIDs)> PackagesDataReadOnly => PackagesData; [JsonProperty(Required = Required.DisallowNull)] internal readonly InMemoryServerListProvider ServerListProvider = new InMemoryServerListProvider(); @@ -53,7 +53,7 @@ namespace ArchiSteamFarm { private readonly ConcurrentDictionary PackagesAccessTokens = new ConcurrentDictionary(); [JsonProperty(Required = Required.DisallowNull)] - private readonly ConcurrentDictionary AppIDs)> PackagesData = new ConcurrentDictionary AppIDs)>(); + private readonly ConcurrentDictionary? AppIDs)> PackagesData = new ConcurrentDictionary? AppIDs)>(); private readonly SemaphoreSlim PackagesRefreshSemaphore = new SemaphoreSlim(1, 1); @@ -73,7 +73,7 @@ namespace ArchiSteamFarm { [JsonProperty(PropertyName = "_" + nameof(CellID), Required = Required.DisallowNull)] private uint BackingCellID; - private GlobalDatabase([NotNull] string filePath) : this() { + private GlobalDatabase(string filePath) : this() { if (string.IsNullOrEmpty(filePath)) { throw new ArgumentNullException(nameof(filePath)); } @@ -95,12 +95,9 @@ namespace ArchiSteamFarm { base.Dispose(); } - [ItemCanBeNull] - internal static async Task CreateOrLoad(string filePath) { + internal static async Task CreateOrLoad(string filePath) { if (string.IsNullOrEmpty(filePath)) { - ASF.ArchiLogger.LogNullError(nameof(filePath)); - - return null; + throw new ArgumentNullException(nameof(filePath)); } if (!File.Exists(filePath)) { @@ -138,15 +135,13 @@ namespace ArchiSteamFarm { internal HashSet GetPackageIDs(uint appID, IEnumerable packageIDs) { if ((appID == 0) || (packageIDs == null)) { - ASF.ArchiLogger.LogNullError(nameof(appID) + " || " + nameof(packageIDs)); - - return null; + throw new ArgumentNullException(nameof(appID) + " || " + nameof(packageIDs)); } HashSet result = new HashSet(); foreach (uint packageID in packageIDs.Where(packageID => packageID != 0)) { - if (!PackagesData.TryGetValue(packageID, out (uint ChangeNumber, HashSet AppIDs) packagesData) || (packagesData.AppIDs?.Contains(appID) != true)) { + if (!PackagesData.TryGetValue(packageID, out (uint ChangeNumber, HashSet? AppIDs) packagesData) || (packagesData.AppIDs?.Contains(appID) != true)) { continue; } @@ -158,9 +153,7 @@ namespace ArchiSteamFarm { internal void RefreshPackageAccessTokens(IReadOnlyDictionary packageAccessTokens) { if ((packageAccessTokens == null) || (packageAccessTokens.Count == 0)) { - ASF.ArchiLogger.LogNullError(nameof(packageAccessTokens)); - - return; + throw new ArgumentNullException(nameof(packageAccessTokens)); } bool save = false; @@ -179,21 +172,19 @@ namespace ArchiSteamFarm { internal async Task RefreshPackages(Bot bot, IReadOnlyDictionary packages) { if ((bot == null) || (packages == null) || (packages.Count == 0)) { - ASF.ArchiLogger.LogNullError(nameof(bot) + " || " + nameof(packages)); - - return; + throw new ArgumentNullException(nameof(bot) + " || " + nameof(packages)); } await PackagesRefreshSemaphore.WaitAsync().ConfigureAwait(false); try { - HashSet packageIDs = packages.Where(package => (package.Key != 0) && (!PackagesData.TryGetValue(package.Key, out (uint ChangeNumber, HashSet AppIDs) packageData) || (packageData.ChangeNumber < package.Value))).Select(package => package.Key).ToHashSet(); + HashSet packageIDs = packages.Where(package => (package.Key != 0) && (!PackagesData.TryGetValue(package.Key, out (uint ChangeNumber, HashSet? AppIDs) packageData) || (packageData.ChangeNumber < package.Value))).Select(package => package.Key).ToHashSet(); if (packageIDs.Count == 0) { return; } - Dictionary AppIDs)> packagesData = await bot.GetPackagesData(packageIDs).ConfigureAwait(false); + Dictionary? AppIDs)>? packagesData = await bot.GetPackagesData(packageIDs).ConfigureAwait(false); if (packagesData == null) { bot.ArchiLogger.LogGenericWarning(Strings.WarningFailed); @@ -203,8 +194,8 @@ namespace ArchiSteamFarm { bool save = false; - foreach ((uint packageID, (uint ChangeNumber, HashSet AppIDs) packageData) in packagesData) { - if (PackagesData.TryGetValue(packageID, out (uint ChangeNumber, HashSet AppIDs) previousData) && (packageData.ChangeNumber < previousData.ChangeNumber)) { + foreach ((uint packageID, (uint ChangeNumber, HashSet? AppIDs) packageData) in packagesData) { + if (PackagesData.TryGetValue(packageID, out (uint ChangeNumber, HashSet? AppIDs) previousData) && (packageData.ChangeNumber < previousData.ChangeNumber)) { continue; } @@ -220,7 +211,7 @@ namespace ArchiSteamFarm { } } - private async void OnServerListUpdated(object sender, EventArgs e) => await Save().ConfigureAwait(false); + private async void OnServerListUpdated(object? sender, EventArgs e) => await Save().ConfigureAwait(false); // ReSharper disable UnusedMember.Global public bool ShouldSerializeCellID() => CellID != 0; diff --git a/ArchiSteamFarm/Helpers/ArchiCacheable.cs b/ArchiSteamFarm/Helpers/ArchiCacheable.cs index 8a394c563..fee71dba2 100644 --- a/ArchiSteamFarm/Helpers/ArchiCacheable.cs +++ b/ArchiSteamFarm/Helpers/ArchiCacheable.cs @@ -26,19 +26,19 @@ using ArchiSteamFarm.Localization; using JetBrains.Annotations; namespace ArchiSteamFarm.Helpers { - public sealed class ArchiCacheable : IDisposable { + public sealed class ArchiCacheable : IDisposable where T : class { private readonly TimeSpan CacheLifetime; private readonly SemaphoreSlim InitSemaphore = new SemaphoreSlim(1, 1); - private readonly Func> ResolveFunction; + private readonly Func> ResolveFunction; private bool IsInitialized => InitializedAt > DateTime.MinValue; private bool IsPermanentCache => CacheLifetime == Timeout.InfiniteTimeSpan; private bool IsRecent => IsPermanentCache || (DateTime.UtcNow.Subtract(InitializedAt) < CacheLifetime); private DateTime InitializedAt; - private T InitializedValue; + private T? InitializedValue; - public ArchiCacheable([NotNull] Func> resolveFunction, TimeSpan? cacheLifetime = null) { + public ArchiCacheable(Func> resolveFunction, TimeSpan? cacheLifetime = null) { ResolveFunction = resolveFunction ?? throw new ArgumentNullException(nameof(resolveFunction)); CacheLifetime = cacheLifetime ?? Timeout.InfiniteTimeSpan; } @@ -46,11 +46,9 @@ namespace ArchiSteamFarm.Helpers { public void Dispose() => InitSemaphore.Dispose(); [PublicAPI] - public async Task<(bool Success, T Result)> GetValue(EFallback fallback = EFallback.DefaultForType) { + public async Task<(bool Success, T? Result)> GetValue(EFallback fallback = EFallback.DefaultForType) { if (!Enum.IsDefined(typeof(EFallback), fallback)) { - ASF.ArchiLogger.LogNullError(nameof(fallback)); - - return (false, default); + throw new ArgumentNullException(nameof(fallback)); } if (IsInitialized && IsRecent) { @@ -64,7 +62,7 @@ namespace ArchiSteamFarm.Helpers { return (true, InitializedValue); } - (bool success, T result) = await ResolveFunction().ConfigureAwait(false); + (bool success, T? result) = await ResolveFunction().ConfigureAwait(false); if (!success) { switch (fallback) { diff --git a/ArchiSteamFarm/Helpers/CrossProcessFileBasedSemaphore.cs b/ArchiSteamFarm/Helpers/CrossProcessFileBasedSemaphore.cs index 4083dcb6c..2b3d564ff 100644 --- a/ArchiSteamFarm/Helpers/CrossProcessFileBasedSemaphore.cs +++ b/ArchiSteamFarm/Helpers/CrossProcessFileBasedSemaphore.cs @@ -25,7 +25,6 @@ using System.IO; using System.Security.AccessControl; using System.Threading; using System.Threading.Tasks; -using JetBrains.Annotations; namespace ArchiSteamFarm.Helpers { internal sealed class CrossProcessFileBasedSemaphore : ICrossProcessSemaphore { @@ -34,9 +33,9 @@ namespace ArchiSteamFarm.Helpers { private readonly string FilePath; private readonly SemaphoreSlim LocalSemaphore = new SemaphoreSlim(1, 1); - private FileStream FileLock; + private FileStream? FileLock; - internal CrossProcessFileBasedSemaphore([NotNull] string name) { + internal CrossProcessFileBasedSemaphore(string name) { if (string.IsNullOrEmpty(name)) { throw new ArgumentNullException(nameof(name)); } @@ -151,7 +150,7 @@ namespace ArchiSteamFarm.Helpers { return; } - string directoryPath = Path.GetDirectoryName(FilePath); + string? directoryPath = Path.GetDirectoryName(FilePath); if (string.IsNullOrEmpty(directoryPath)) { ASF.ArchiLogger.LogNullError(nameof(directoryPath)); diff --git a/ArchiSteamFarm/Helpers/SemaphoreLock.cs b/ArchiSteamFarm/Helpers/SemaphoreLock.cs index 9d5e1f389..29e79c652 100644 --- a/ArchiSteamFarm/Helpers/SemaphoreLock.cs +++ b/ArchiSteamFarm/Helpers/SemaphoreLock.cs @@ -21,13 +21,12 @@ using System; using System.Threading; -using JetBrains.Annotations; namespace ArchiSteamFarm.Helpers { internal sealed class SemaphoreLock : IDisposable { private readonly SemaphoreSlim Semaphore; - internal SemaphoreLock([NotNull] SemaphoreSlim semaphore) => Semaphore = semaphore ?? throw new ArgumentNullException(nameof(semaphore)); + internal SemaphoreLock(SemaphoreSlim semaphore) => Semaphore = semaphore ?? throw new ArgumentNullException(nameof(semaphore)); public void Dispose() => Semaphore.Release(); } diff --git a/ArchiSteamFarm/Helpers/SerializableFile.cs b/ArchiSteamFarm/Helpers/SerializableFile.cs index f81792cb4..cd2101f55 100644 --- a/ArchiSteamFarm/Helpers/SerializableFile.cs +++ b/ArchiSteamFarm/Helpers/SerializableFile.cs @@ -29,7 +29,7 @@ namespace ArchiSteamFarm.Helpers { public abstract class SerializableFile : IDisposable { private readonly SemaphoreSlim FileSemaphore = new SemaphoreSlim(1, 1); - protected string FilePath { private get; set; } + protected string? FilePath { private get; set; } private bool ReadOnly; private bool SavingScheduled; @@ -37,7 +37,11 @@ namespace ArchiSteamFarm.Helpers { public virtual void Dispose() => FileSemaphore.Dispose(); protected async Task Save() { - if (ReadOnly || string.IsNullOrEmpty(FilePath)) { + if (string.IsNullOrEmpty(FilePath)) { + throw new ArgumentNullException(nameof(FilePath)); + } + + if (ReadOnly) { return; } diff --git a/ArchiSteamFarm/IPC/ArchiKestrel.cs b/ArchiSteamFarm/IPC/ArchiKestrel.cs index 23c264ccf..894aca36c 100644 --- a/ArchiSteamFarm/IPC/ArchiKestrel.cs +++ b/ArchiSteamFarm/IPC/ArchiKestrel.cs @@ -31,23 +31,23 @@ using Microsoft.Extensions.Logging; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using NLog.Web; - #if !NETFRAMEWORK using Microsoft.Extensions.Hosting; + #endif namespace ArchiSteamFarm.IPC { internal static class ArchiKestrel { - internal static HistoryTarget HistoryTarget { get; private set; } + internal static HistoryTarget? HistoryTarget { get; private set; } internal static string WebsiteDirectory { get; private set; } = Path.Combine(AppContext.BaseDirectory, SharedInfo.WebsiteDirectory); #if NETFRAMEWORK - private static IWebHost KestrelWebHost; + private static IWebHost? KestrelWebHost; #else - private static IHost KestrelWebHost; + private static IHost? KestrelWebHost; #endif - internal static void OnNewHistoryTarget(HistoryTarget historyTarget = null) { + internal static void OnNewHistoryTarget(HistoryTarget? historyTarget = null) { if (HistoryTarget != null) { HistoryTarget.NewHistoryEntry -= NLogController.OnNewHistoryEntry; HistoryTarget = null; @@ -140,9 +140,9 @@ namespace ArchiSteamFarm.IPC { // Start the server #if NETFRAMEWORK - IWebHost kestrelWebHost = null; + IWebHost? kestrelWebHost = null; #else - IHost kestrelWebHost = null; + IHost? kestrelWebHost = null; #endif try { diff --git a/ArchiSteamFarm/IPC/Controllers/Api/ASFController.cs b/ArchiSteamFarm/IPC/Controllers/Api/ASFController.cs index 189d4f20f..30117a976 100644 --- a/ArchiSteamFarm/IPC/Controllers/Api/ASFController.cs +++ b/ArchiSteamFarm/IPC/Controllers/Api/ASFController.cs @@ -39,6 +39,10 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { [HttpGet] [ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.OK)] public ActionResult> ASFGet() { + if (ASF.GlobalConfig == null) { + throw new ArgumentNullException(nameof(ASF.GlobalConfig)); + } + uint memoryUsage = (uint) GC.GetTotalMemory(false) / 1024; ASFResponse result = new ASFResponse(SharedInfo.BuildInfo.Variant, ASF.GlobalConfig, memoryUsage, RuntimeCompatibility.ProcessStartTime, SharedInfo.Version); @@ -54,13 +58,15 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { [ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.OK)] [ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.BadRequest)] public async Task> ASFPost([FromBody] ASFRequest request) { - if (request == null) { - ASF.ArchiLogger.LogNullError(nameof(request)); - - return BadRequest(new GenericResponse(false, string.Format(Strings.ErrorIsEmpty, nameof(request)))); + if ((request == null) || (ASF.GlobalConfig == null)) { + throw new ArgumentNullException(nameof(request) + " || " + nameof(ASF.GlobalConfig)); } - (bool valid, string errorMessage) = request.GlobalConfig.CheckValidation(); + if (request.GlobalConfig == null) { + return BadRequest(new GenericResponse(false, string.Format(Strings.ErrorIsEmpty, nameof(request.GlobalConfig)))); + } + + (bool valid, string? errorMessage) = request.GlobalConfig.CheckValidation(); if (!valid) { return BadRequest(new GenericResponse(false, errorMessage)); @@ -125,13 +131,13 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { [HttpPost("Update")] [ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.OK)] public async Task>> UpdatePost() { - (bool success, string message, Version version) = await Actions.Update().ConfigureAwait(false); + (bool success, string? message, Version? version) = await Actions.Update().ConfigureAwait(false); if (string.IsNullOrEmpty(message)) { message = success ? Strings.Success : Strings.WarningFailed; } - return Ok(new GenericResponse(success, message, version?.ToString())); + return Ok(new GenericResponse(success, message!, version?.ToString())); } } } diff --git a/ArchiSteamFarm/IPC/Controllers/Api/BotController.cs b/ArchiSteamFarm/IPC/Controllers/Api/BotController.cs index 39d4a536a..73b156efd 100644 --- a/ArchiSteamFarm/IPC/Controllers/Api/BotController.cs +++ b/ArchiSteamFarm/IPC/Controllers/Api/BotController.cs @@ -42,12 +42,10 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { [ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.BadRequest)] public async Task> BotDelete(string botNames) { if (string.IsNullOrEmpty(botNames)) { - ASF.ArchiLogger.LogNullError(nameof(botNames)); - - return BadRequest(new GenericResponse(false, string.Format(Strings.ErrorIsEmpty, nameof(botNames)))); + throw new ArgumentNullException(nameof(botNames)); } - HashSet bots = Bot.GetBots(botNames); + HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { return BadRequest(new GenericResponse(false, string.Format(Strings.BotNotFound, botNames))); @@ -66,18 +64,16 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { [ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.BadRequest)] public ActionResult BotGet(string botNames) { if (string.IsNullOrEmpty(botNames)) { - ASF.ArchiLogger.LogNullError(nameof(botNames)); - - return BadRequest(new GenericResponse(false, string.Format(Strings.ErrorIsEmpty, nameof(botNames)))); + throw new ArgumentNullException(nameof(botNames)); } - HashSet bots = Bot.GetBots(botNames); + HashSet? bots = Bot.GetBots(botNames); if (bots == null) { return BadRequest(new GenericResponse(false, string.Format(Strings.ErrorIsInvalid, nameof(bots)))); } - return Ok(new GenericResponse>(bots.ToDictionary(bot => bot.BotName, bot => bot, Bot.BotsComparer))); + return Ok(new GenericResponse>(bots.Where(bot => !string.IsNullOrEmpty(bot.BotName)).ToDictionary(bot => bot.BotName, bot => bot, Bot.BotsComparer)!)); } /// @@ -88,13 +84,15 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { [ProducesResponseType(typeof(GenericResponse>), (int) HttpStatusCode.OK)] [ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.BadRequest)] public async Task> BotPost(string botNames, [FromBody] BotRequest request) { - if (string.IsNullOrEmpty(botNames) || (request == null)) { - ASF.ArchiLogger.LogNullError(nameof(botNames) + " || " + nameof(request)); - - return BadRequest(new GenericResponse(false, string.Format(Strings.ErrorIsEmpty, nameof(botNames) + " || " + nameof(request)))); + if (string.IsNullOrEmpty(botNames) || (request == null) || (Bot.Bots == null)) { + throw new ArgumentNullException(nameof(botNames) + " || " + nameof(request) + " || " + nameof(Bot.Bots)); } - (bool valid, string errorMessage) = request.BotConfig.CheckValidation(); + if (request.BotConfig == null) { + return BadRequest(new GenericResponse(false, string.Format(Strings.ErrorIsEmpty, nameof(request.BotConfig)))); + } + + (bool valid, string? errorMessage) = request.BotConfig.CheckValidation(); if (!valid) { return BadRequest(new GenericResponse(false, errorMessage)); @@ -109,7 +107,7 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { Dictionary result = new Dictionary(bots.Count, Bot.BotsComparer); foreach (string botName in bots) { - if (Bot.Bots.TryGetValue(botName, out Bot bot)) { + if (Bot.Bots.TryGetValue(botName, out Bot? bot)) { if (!request.BotConfig.IsSteamLoginSet && bot.BotConfig.IsSteamLoginSet) { request.BotConfig.SteamLogin = bot.BotConfig.SteamLogin; } @@ -155,12 +153,10 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { [ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.BadRequest)] public async Task> GamesToRedeemInBackgroundDelete(string botNames) { if (string.IsNullOrEmpty(botNames)) { - ASF.ArchiLogger.LogNullError(nameof(botNames)); - - return BadRequest(new GenericResponse(false, string.Format(Strings.ErrorIsEmpty, nameof(botNames)))); + throw new ArgumentNullException(nameof(botNames)); } - HashSet bots = Bot.GetBots(botNames); + HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { return BadRequest(new GenericResponse(false, string.Format(Strings.BotNotFound, botNames))); @@ -179,23 +175,21 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { [ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.BadRequest)] public async Task> GamesToRedeemInBackgroundGet(string botNames) { if (string.IsNullOrEmpty(botNames)) { - ASF.ArchiLogger.LogNullError(nameof(botNames)); - - return BadRequest(new GenericResponse(false, string.Format(Strings.ErrorIsEmpty, nameof(botNames)))); + throw new ArgumentNullException(nameof(botNames)); } - HashSet bots = Bot.GetBots(botNames); + HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { return BadRequest(new GenericResponse(false, string.Format(Strings.BotNotFound, botNames))); } - IList<(Dictionary UnusedKeys, Dictionary UsedKeys)> results = await Utilities.InParallel(bots.Select(bot => bot.GetUsedAndUnusedKeys())).ConfigureAwait(false); + IList<(Dictionary? UnusedKeys, Dictionary? UsedKeys)> results = await Utilities.InParallel(bots.Select(bot => bot.GetUsedAndUnusedKeys())).ConfigureAwait(false); Dictionary result = new Dictionary(bots.Count, Bot.BotsComparer); foreach (Bot bot in bots) { - (Dictionary unusedKeys, Dictionary usedKeys) = results[result.Count]; + (Dictionary? unusedKeys, Dictionary? usedKeys) = results[result.Count]; result[bot.BotName] = new GamesToRedeemInBackgroundResponse(unusedKeys, usedKeys); } @@ -211,16 +205,14 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { [ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.BadRequest)] public async Task> GamesToRedeemInBackgroundPost(string botNames, [FromBody] BotGamesToRedeemInBackgroundRequest request) { if (string.IsNullOrEmpty(botNames) || (request == null)) { - ASF.ArchiLogger.LogNullError(nameof(botNames) + " || " + nameof(request)); - - return BadRequest(new GenericResponse(false, string.Format(Strings.ErrorIsEmpty, nameof(botNames) + " || " + nameof(request)))); + throw new ArgumentNullException(nameof(botNames) + " || " + nameof(request)); } - if (request.GamesToRedeemInBackground.Count == 0) { + if ((request.GamesToRedeemInBackground == null) || (request.GamesToRedeemInBackground.Count == 0)) { return BadRequest(new GenericResponse(false, string.Format(Strings.ErrorIsEmpty, nameof(request.GamesToRedeemInBackground)))); } - HashSet bots = Bot.GetBots(botNames); + HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { return BadRequest(new GenericResponse(false, string.Format(Strings.BotNotFound, botNames))); @@ -252,22 +244,20 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { [ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.BadRequest)] public async Task> InputPost(string botNames, [FromBody] BotInputRequest request) { if (string.IsNullOrEmpty(botNames) || (request == null)) { - ASF.ArchiLogger.LogNullError(nameof(botNames) + " || " + nameof(request)); - - return BadRequest(new GenericResponse(false, string.Format(Strings.ErrorIsEmpty, nameof(botNames) + " || " + nameof(request)))); + throw new ArgumentNullException(nameof(botNames) + " || " + nameof(request)); } if ((request.Type == ASF.EUserInputType.None) || !Enum.IsDefined(typeof(ASF.EUserInputType), request.Type) || string.IsNullOrEmpty(request.Value)) { return BadRequest(new GenericResponse(false, string.Format(Strings.ErrorIsInvalid, nameof(request.Type) + " || " + nameof(request.Value)))); } - HashSet bots = Bot.GetBots(botNames); + HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { return BadRequest(new GenericResponse(false, string.Format(Strings.BotNotFound, botNames))); } - IList results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.SetUserInput(request.Type, request.Value)))).ConfigureAwait(false); + IList results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.SetUserInput(request.Type, request.Value!)))).ConfigureAwait(false); return Ok(results.All(result => result) ? new GenericResponse(true) : new GenericResponse(false, Strings.WarningFailed)); } @@ -281,12 +271,10 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { [ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.BadRequest)] public async Task> PausePost(string botNames, [FromBody] BotPauseRequest request) { if (string.IsNullOrEmpty(botNames) || (request == null)) { - ASF.ArchiLogger.LogNullError(nameof(botNames) + " || " + nameof(request)); - - return BadRequest(new GenericResponse(false, string.Format(Strings.ErrorIsEmpty, nameof(botNames) + " || " + nameof(request)))); + throw new ArgumentNullException(nameof(botNames) + " || " + nameof(request)); } - HashSet bots = Bot.GetBots(botNames); + HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { return BadRequest(new GenericResponse(false, string.Format(Strings.BotNotFound, botNames))); @@ -310,29 +298,27 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { [ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.BadRequest)] public async Task> RedeemPost(string botNames, [FromBody] BotRedeemRequest request) { if (string.IsNullOrEmpty(botNames) || (request == null)) { - ASF.ArchiLogger.LogNullError(nameof(botNames) + " || " + nameof(request)); - - return BadRequest(new GenericResponse(false, string.Format(Strings.ErrorIsEmpty, nameof(botNames) + " || " + nameof(request)))); + throw new ArgumentNullException(nameof(botNames) + " || " + nameof(request)); } - if (request.KeysToRedeem.Count == 0) { + if ((request.KeysToRedeem == null) || (request.KeysToRedeem.Count == 0)) { return BadRequest(new GenericResponse(false, string.Format(Strings.ErrorIsEmpty, nameof(request.KeysToRedeem)))); } - HashSet bots = Bot.GetBots(botNames); + HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { return BadRequest(new GenericResponse(false, string.Format(Strings.BotNotFound, botNames))); } - IList results = await Utilities.InParallel(bots.Select(bot => request.KeysToRedeem.Select(key => bot.Actions.RedeemKey(key))).SelectMany(task => task)).ConfigureAwait(false); + 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 Dictionary>(bots.Count, Bot.BotsComparer); int count = 0; foreach (Bot bot in bots) { - Dictionary responses = new Dictionary(request.KeysToRedeem.Count, StringComparer.Ordinal); + Dictionary responses = new Dictionary(request.KeysToRedeem.Count, StringComparer.Ordinal); result[bot.BotName] = responses; foreach (string key in request.KeysToRedeem) { @@ -340,7 +326,7 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { } } - return Ok(new GenericResponse>>(result.Values.SelectMany(responses => responses.Values).All(value => value != null), result)); + return Ok(new GenericResponse>>(result.Values.SelectMany(responses => responses.Values).All(value => value != null), result)); } /// @@ -351,17 +337,15 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { [ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.OK)] [ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.BadRequest)] public async Task> RenamePost(string botName, [FromBody] BotRenameRequest request) { - if (string.IsNullOrEmpty(botName) || (request == null)) { - ASF.ArchiLogger.LogNullError(nameof(botName) + " || " + nameof(request)); - - return BadRequest(new GenericResponse(false, string.Format(Strings.ErrorIsEmpty, nameof(botName) + " || " + nameof(request)))); + if (string.IsNullOrEmpty(botName) || (request == null) || (Bot.Bots == null)) { + throw new ArgumentNullException(nameof(botName) + " || " + nameof(request) + " || " + nameof(Bot.Bots)); } - if (string.IsNullOrEmpty(request.NewName) || request.NewName.Equals(SharedInfo.ASF) || Bot.Bots.ContainsKey(request.NewName)) { + if (string.IsNullOrEmpty(request.NewName) || request.NewName!.Equals(SharedInfo.ASF) || Bot.Bots.ContainsKey(request.NewName)) { return BadRequest(new GenericResponse(false, string.Format(Strings.ErrorIsInvalid, nameof(request.NewName)))); } - if (!Bot.Bots.TryGetValue(botName, out Bot bot)) { + if (!Bot.Bots.TryGetValue(botName, out Bot? bot)) { return BadRequest(new GenericResponse(false, string.Format(Strings.BotNotFound, botName))); } @@ -378,12 +362,10 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { [ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.BadRequest)] public async Task> ResumePost(string botNames) { if (string.IsNullOrEmpty(botNames)) { - ASF.ArchiLogger.LogNullError(nameof(botNames)); - - return BadRequest(new GenericResponse(false, string.Format(Strings.ErrorIsEmpty, nameof(botNames)))); + throw new ArgumentNullException(nameof(botNames)); } - HashSet bots = Bot.GetBots(botNames); + HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { return BadRequest(new GenericResponse(false, string.Format(Strings.BotNotFound, botNames))); @@ -402,12 +384,10 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { [ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.BadRequest)] public async Task> StartPost(string botNames) { if (string.IsNullOrEmpty(botNames)) { - ASF.ArchiLogger.LogNullError(nameof(botNames)); - - return BadRequest(new GenericResponse(false, string.Format(Strings.ErrorIsEmpty, nameof(botNames)))); + throw new ArgumentNullException(nameof(botNames)); } - HashSet bots = Bot.GetBots(botNames); + HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { return BadRequest(new GenericResponse(false, string.Format(Strings.BotNotFound, botNames))); @@ -426,12 +406,10 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { [ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.BadRequest)] public async Task> StopPost(string botNames) { if (string.IsNullOrEmpty(botNames)) { - ASF.ArchiLogger.LogNullError(nameof(botNames)); - - return BadRequest(new GenericResponse(false, string.Format(Strings.ErrorIsEmpty, nameof(botNames)))); + throw new ArgumentNullException(nameof(botNames)); } - HashSet bots = Bot.GetBots(botNames); + HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { return BadRequest(new GenericResponse(false, string.Format(Strings.BotNotFound, botNames))); @@ -450,16 +428,14 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { [ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.BadRequest)] public async Task> TwoFactorAuthenticationConfirmationsPost(string botNames, [FromBody] TwoFactorAuthenticationConfirmationsRequest request) { if (string.IsNullOrEmpty(botNames) || (request == null)) { - ASF.ArchiLogger.LogNullError(nameof(botNames)); - - return BadRequest(new GenericResponse>(false, string.Format(Strings.ErrorIsEmpty, nameof(botNames) + " || " + nameof(request)))); + throw new ArgumentNullException(nameof(botNames)); } if (request.AcceptedType.HasValue && ((request.AcceptedType.Value == MobileAuthenticator.Confirmation.EType.Unknown) || !Enum.IsDefined(typeof(MobileAuthenticator.Confirmation.EType), request.AcceptedType.Value))) { return BadRequest(new GenericResponse(false, string.Format(Strings.ErrorIsInvalid, nameof(request.AcceptedType)))); } - HashSet bots = Bot.GetBots(botNames); + HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { return BadRequest(new GenericResponse>(false, string.Format(Strings.BotNotFound, botNames))); @@ -485,23 +461,21 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { [ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.BadRequest)] public async Task> TwoFactorAuthenticationTokenGet(string botNames) { if (string.IsNullOrEmpty(botNames)) { - ASF.ArchiLogger.LogNullError(nameof(botNames)); - - return BadRequest(new GenericResponse>>(false, string.Format(Strings.ErrorIsEmpty, nameof(botNames)))); + throw new ArgumentNullException(nameof(botNames)); } - HashSet bots = Bot.GetBots(botNames); + HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { return BadRequest(new GenericResponse>>(false, string.Format(Strings.BotNotFound, botNames))); } - IList<(bool Success, string Token, string Message)> results = await Utilities.InParallel(bots.Select(bot => bot.Actions.GenerateTwoFactorAuthenticationToken())).ConfigureAwait(false); + 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); foreach (Bot bot in bots) { - (bool success, string token, string message) = results[result.Count]; + (bool success, string? token, string message) = results[result.Count]; result[bot.BotName] = new GenericResponse(success, message, token); } diff --git a/ArchiSteamFarm/IPC/Controllers/Api/CommandController.cs b/ArchiSteamFarm/IPC/Controllers/Api/CommandController.cs index 9c8d0e163..33146de65 100644 --- a/ArchiSteamFarm/IPC/Controllers/Api/CommandController.cs +++ b/ArchiSteamFarm/IPC/Controllers/Api/CommandController.cs @@ -44,36 +44,37 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { [ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.BadRequest)] public async Task> CommandPost([FromBody] CommandRequest request) { if (request == null) { - ASF.ArchiLogger.LogNullError(nameof(request)); - - return BadRequest(new GenericResponse(false, string.Format(Strings.ErrorIsEmpty, nameof(request)))); + throw new ArgumentNullException(nameof(request)); } if (string.IsNullOrEmpty(request.Command)) { return BadRequest(new GenericResponse(false, string.Format(Strings.ErrorIsEmpty, nameof(request.Command)))); } - if (ASF.GlobalConfig.SteamOwnerID == 0) { + ulong steamOwnerID = ASF.GlobalConfig?.SteamOwnerID ?? GlobalConfig.DefaultSteamOwnerID; + + if (steamOwnerID == 0) { return BadRequest(new GenericResponse(false, string.Format(Strings.ErrorIsInvalid, nameof(ASF.GlobalConfig.SteamOwnerID)))); } - Bot targetBot = Bot.Bots.OrderBy(bot => bot.Key, Bot.BotsComparer).Select(bot => bot.Value).FirstOrDefault(); + Bot? targetBot = Bot.Bots.OrderBy(bot => bot.Key, Bot.BotsComparer).Select(bot => bot.Value).FirstOrDefault(); if (targetBot == null) { return BadRequest(new GenericResponse(false, Strings.ErrorNoBotsDefined)); } - string command = request.Command; + string command = request.Command!; + string? commandPrefix = ASF.GlobalConfig?.CommandPrefix ?? GlobalConfig.DefaultCommandPrefix; - if (!string.IsNullOrEmpty(ASF.GlobalConfig.CommandPrefix) && command.StartsWith(ASF.GlobalConfig.CommandPrefix, StringComparison.Ordinal)) { - command = command.Substring(ASF.GlobalConfig.CommandPrefix.Length); + if (!string.IsNullOrEmpty(commandPrefix) && command.StartsWith(commandPrefix, StringComparison.Ordinal)) { + command = command.Substring(commandPrefix!.Length); if (string.IsNullOrEmpty(command)) { return BadRequest(new GenericResponse(false, string.Format(Strings.ErrorIsEmpty, nameof(command)))); } } - string response = await targetBot.Commands.Response(ASF.GlobalConfig.SteamOwnerID, command).ConfigureAwait(false); + string? response = await targetBot.Commands.Response(steamOwnerID, command).ConfigureAwait(false); return Ok(new GenericResponse(response)); } diff --git a/ArchiSteamFarm/IPC/Controllers/Api/NLogController.cs b/ArchiSteamFarm/IPC/Controllers/Api/NLogController.cs index 97cfa0813..c7d885538 100644 --- a/ArchiSteamFarm/IPC/Controllers/Api/NLogController.cs +++ b/ArchiSteamFarm/IPC/Controllers/Api/NLogController.cs @@ -19,6 +19,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; @@ -86,7 +87,7 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { break; } } finally { - if (ActiveLogWebSockets.TryRemove(webSocket, out SemaphoreSlim closedSemaphore)) { + if (ActiveLogWebSockets.TryRemove(webSocket, out SemaphoreSlim? closedSemaphore)) { await closedSemaphore.WaitAsync().ConfigureAwait(false); // Ensure that our semaphore is truly closed by now closedSemaphore.Dispose(); } @@ -98,11 +99,9 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { return new EmptyResult(); } - internal static async void OnNewHistoryEntry(object sender, HistoryTarget.NewHistoryEntryArgs newHistoryEntryArgs) { - if ((sender == null) || (newHistoryEntryArgs == null)) { - ASF.ArchiLogger.LogNullError(nameof(sender) + " || " + nameof(newHistoryEntryArgs)); - - return; + internal static async void OnNewHistoryEntry(object? sender, HistoryTarget.NewHistoryEntryArgs newHistoryEntryArgs) { + if (newHistoryEntryArgs == null) { + throw new ArgumentNullException(nameof(newHistoryEntryArgs)); } if (ActiveLogWebSockets.Count == 0) { @@ -110,14 +109,13 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { } string json = JsonConvert.SerializeObject(new GenericResponse(newHistoryEntryArgs.Message)); + await Task.WhenAll(ActiveLogWebSockets.Where(kv => kv.Key.State == WebSocketState.Open).Select(kv => PostLoggedJsonUpdate(kv.Key, kv.Value, json))).ConfigureAwait(false); } private static async Task PostLoggedJsonUpdate(WebSocket webSocket, SemaphoreSlim sendSemaphore, string json) { if ((webSocket == null) || (sendSemaphore == null) || string.IsNullOrEmpty(json)) { - ASF.ArchiLogger.LogNullError(nameof(webSocket) + " || " + nameof(sendSemaphore) + " || " + nameof(json)); - - return; + throw new ArgumentNullException(nameof(webSocket) + " || " + nameof(sendSemaphore) + " || " + nameof(json)); } if (webSocket.State != WebSocketState.Open) { @@ -141,9 +139,7 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { private static async Task PostLoggedMessageUpdate(WebSocket webSocket, SemaphoreSlim sendSemaphore, string loggedMessage) { if ((webSocket == null) || (sendSemaphore == null) || string.IsNullOrEmpty(loggedMessage)) { - ASF.ArchiLogger.LogNullError(nameof(webSocket) + " || " + nameof(sendSemaphore) + " || " + nameof(loggedMessage)); - - return; + throw new ArgumentNullException(nameof(webSocket) + " || " + nameof(sendSemaphore) + " || " + nameof(loggedMessage)); } if (webSocket.State != WebSocketState.Open) { @@ -151,6 +147,7 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { } string response = JsonConvert.SerializeObject(new GenericResponse(loggedMessage)); + await PostLoggedJsonUpdate(webSocket, sendSemaphore, response).ConfigureAwait(false); } } diff --git a/ArchiSteamFarm/IPC/Controllers/Api/StructureController.cs b/ArchiSteamFarm/IPC/Controllers/Api/StructureController.cs index c57e4d1ad..b06c121b4 100644 --- a/ArchiSteamFarm/IPC/Controllers/Api/StructureController.cs +++ b/ArchiSteamFarm/IPC/Controllers/Api/StructureController.cs @@ -39,18 +39,16 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { [ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.BadRequest)] public ActionResult StructureGet(string structure) { if (string.IsNullOrEmpty(structure)) { - ASF.ArchiLogger.LogNullError(nameof(structure)); - - return BadRequest(new GenericResponse(false, string.Format(Strings.ErrorIsEmpty, nameof(structure)))); + throw new ArgumentNullException(nameof(structure)); } - Type targetType = WebUtilities.ParseType(structure); + Type? targetType = WebUtilities.ParseType(structure); if (targetType == null) { return BadRequest(new GenericResponse(false, string.Format(Strings.ErrorIsInvalid, structure))); } - object obj; + object? obj; try { obj = Activator.CreateInstance(targetType, true); diff --git a/ArchiSteamFarm/IPC/Controllers/Api/TypeController.cs b/ArchiSteamFarm/IPC/Controllers/Api/TypeController.cs index ef1a40475..d8a609879 100644 --- a/ArchiSteamFarm/IPC/Controllers/Api/TypeController.cs +++ b/ArchiSteamFarm/IPC/Controllers/Api/TypeController.cs @@ -43,45 +43,55 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { [ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.BadRequest)] public ActionResult TypeGet(string type) { if (string.IsNullOrEmpty(type)) { - ASF.ArchiLogger.LogNullError(nameof(type)); - - return BadRequest(new GenericResponse(false, string.Format(Strings.ErrorIsEmpty, nameof(type)))); + throw new ArgumentNullException(nameof(type)); } - Type targetType = WebUtilities.ParseType(type); + Type? targetType = WebUtilities.ParseType(type); if (targetType == null) { return BadRequest(new GenericResponse(false, string.Format(Strings.ErrorIsInvalid, type))); } - string baseType = targetType.BaseType?.GetUnifiedName(); - HashSet customAttributes = targetType.CustomAttributes.Select(attribute => attribute.AttributeType.GetUnifiedName()).ToHashSet(StringComparer.Ordinal); - string underlyingType = null; + string? baseType = targetType.BaseType?.GetUnifiedName(); + 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); if (targetType.IsClass) { foreach (FieldInfo field in targetType.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public).Where(field => !field.IsPrivate)) { - JsonPropertyAttribute jsonProperty = field.GetCustomAttribute(); + JsonPropertyAttribute? jsonProperty = field.GetCustomAttribute(); if (jsonProperty != null) { - body[jsonProperty.PropertyName ?? field.Name] = field.FieldType.GetUnifiedName(); + string? unifiedName = field.FieldType.GetUnifiedName(); + + if (!string.IsNullOrEmpty(unifiedName)) { + body[jsonProperty.PropertyName ?? field.Name] = unifiedName!; + } } } foreach (PropertyInfo property in targetType.GetProperties(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public).Where(property => property.CanRead && (property.GetMethod?.IsPrivate == false))) { - JsonPropertyAttribute jsonProperty = property.GetCustomAttribute(); + JsonPropertyAttribute? jsonProperty = property.GetCustomAttribute(); if (jsonProperty != null) { - body[jsonProperty.PropertyName ?? property.Name] = property.PropertyType.GetUnifiedName(); + string? unifiedName = property.PropertyType.GetUnifiedName(); + + if (!string.IsNullOrEmpty(unifiedName)) { + body[jsonProperty.PropertyName ?? property.Name] = unifiedName!; + } } } } else if (targetType.IsEnum) { Type enumType = Enum.GetUnderlyingType(targetType); underlyingType = enumType.GetUnifiedName(); - foreach (object value in Enum.GetValues(targetType)) { - string valueText = value.ToString(); + foreach (object? value in Enum.GetValues(targetType)) { + if (value == null) { + continue; + } + + string? valueText = value.ToString(); if (string.IsNullOrEmpty(valueText)) { ASF.ArchiLogger.LogNullError(nameof(valueText)); @@ -89,7 +99,13 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { return BadRequest(new GenericResponse(false, string.Format(Strings.ErrorObjectIsNull, nameof(valueText)))); } - body[valueText] = Convert.ChangeType(value, enumType).ToString(); + string? valueObjText = Convert.ChangeType(value, enumType)?.ToString(); + + if (string.IsNullOrEmpty(valueObjText)) { + continue; + } + + body[valueText] = valueObjText!; } } diff --git a/ArchiSteamFarm/IPC/Controllers/Api/WWWController.cs b/ArchiSteamFarm/IPC/Controllers/Api/WWWController.cs index 24dcb059f..abcdcd8ff 100644 --- a/ArchiSteamFarm/IPC/Controllers/Api/WWWController.cs +++ b/ArchiSteamFarm/IPC/Controllers/Api/WWWController.cs @@ -45,9 +45,7 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { [ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.InternalServerError)] public ActionResult DirectoryGet(string directory) { if (string.IsNullOrEmpty(directory)) { - ASF.ArchiLogger.LogNullError(nameof(directory)); - - return BadRequest(new GenericResponse(false, string.Format(Strings.ErrorIsEmpty, nameof(directory)))); + throw new ArgumentNullException(directory); } string directoryPath = Path.Combine(ArchiKestrel.WebsiteDirectory, directory); @@ -64,7 +62,7 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { return StatusCode((int) HttpStatusCode.InternalServerError, new GenericResponse(false, string.Format(Strings.ErrorParsingObject, nameof(files)) + Environment.NewLine + e)); } - HashSet result = files.Select(Path.GetFileName).ToHashSet(); + HashSet result = files.Select(Path.GetFileName).Where(fileName => !string.IsNullOrEmpty(fileName)).ToHashSet()!; return Ok(new GenericResponse>(result)); } @@ -79,7 +77,7 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { [ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.OK)] [ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.ServiceUnavailable)] public async Task> GitHubReleaseGet() { - GitHub.ReleaseResponse releaseResponse = await GitHub.GetLatestRelease(false).ConfigureAwait(false); + GitHub.ReleaseResponse? releaseResponse = await GitHub.GetLatestRelease(false).ConfigureAwait(false); return releaseResponse != null ? Ok(new GenericResponse(new GitHubReleaseResponse(releaseResponse))) : StatusCode((int) HttpStatusCode.ServiceUnavailable, new GenericResponse(false, string.Format(Strings.ErrorRequestFailedTooManyTimes, WebBrowser.MaxTries))); } @@ -96,10 +94,25 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { [ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.ServiceUnavailable)] public async Task> GitHubReleaseGet(string version) { if (string.IsNullOrEmpty(version)) { - return BadRequest(new GenericResponse(false, string.Format(Strings.ErrorIsEmpty, nameof(version)))); + throw new ArgumentNullException(nameof(version)); } - GitHub.ReleaseResponse releaseResponse = version.Equals("latest", StringComparison.OrdinalIgnoreCase) ? await GitHub.GetLatestRelease().ConfigureAwait(false) : await GitHub.GetRelease(version).ConfigureAwait(false); + GitHub.ReleaseResponse? releaseResponse; + + switch (version.ToUpperInvariant()) { + case "LATEST": + releaseResponse = await GitHub.GetLatestRelease().ConfigureAwait(false); + + break; + default: + if (!Version.TryParse(version, out Version? parsedVersion)) { + return BadRequest(new GenericResponse(false, string.Format(Strings.ErrorIsInvalid, nameof(version)))); + } + + releaseResponse = await GitHub.GetRelease(parsedVersion.ToString(4)).ConfigureAwait(false); + + break; + } return releaseResponse != null ? Ok(new GenericResponse(new GitHubReleaseResponse(releaseResponse))) : StatusCode((int) HttpStatusCode.ServiceUnavailable, new GenericResponse(false, string.Format(Strings.ErrorRequestFailedTooManyTimes, WebBrowser.MaxTries))); } @@ -116,17 +129,15 @@ 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) { - ASF.ArchiLogger.LogNullError(nameof(request)); - - return BadRequest(new GenericResponse(false, string.Format(Strings.ErrorIsEmpty, nameof(request)))); + if ((request == null) || (ASF.WebBrowser == null)) { + throw new ArgumentNullException(nameof(request) + " || " + nameof(ASF.WebBrowser)); } - if (string.IsNullOrEmpty(request.URL) || !Uri.TryCreate(request.URL, UriKind.Absolute, out Uri uri) || !uri.Scheme.Equals(Uri.UriSchemeHttps)) { + if (string.IsNullOrEmpty(request.URL) || !Uri.TryCreate(request.URL, UriKind.Absolute, out Uri? uri) || !uri.Scheme.Equals(Uri.UriSchemeHttps)) { return BadRequest(new GenericResponse(false, string.Format(Strings.ErrorIsInvalid, nameof(request.URL)))); } - WebBrowser.StringResponse urlResponse = await ASF.WebBrowser.UrlGetToString(request.URL).ConfigureAwait(false); + WebBrowser.StringResponse? urlResponse = await ASF.WebBrowser.UrlGetToString(request.URL!).ConfigureAwait(false); return urlResponse?.Content != null ? Ok(new GenericResponse(urlResponse.Content)) : StatusCode((int) HttpStatusCode.ServiceUnavailable, new GenericResponse(false, string.Format(Strings.ErrorRequestFailedTooManyTimes, WebBrowser.MaxTries))); } diff --git a/ArchiSteamFarm/IPC/Integration/ApiAuthenticationMiddleware.cs b/ArchiSteamFarm/IPC/Integration/ApiAuthenticationMiddleware.cs index 405b4cf44..fa4d9b304 100644 --- a/ArchiSteamFarm/IPC/Integration/ApiAuthenticationMiddleware.cs +++ b/ArchiSteamFarm/IPC/Integration/ApiAuthenticationMiddleware.cs @@ -41,11 +41,11 @@ namespace ArchiSteamFarm.IPC.Integration { private static readonly SemaphoreSlim AuthorizationSemaphore = new SemaphoreSlim(1, 1); private static readonly ConcurrentDictionary FailedAuthorizations = new ConcurrentDictionary(); - private static Timer ClearFailedAuthorizationsTimer; + private static Timer? ClearFailedAuthorizationsTimer; private readonly RequestDelegate Next; - public ApiAuthenticationMiddleware([JetBrains.Annotations.NotNull] RequestDelegate next) { + public ApiAuthenticationMiddleware(RequestDelegate next) { Next = next ?? throw new ArgumentNullException(nameof(next)); lock (FailedAuthorizations) { @@ -61,9 +61,7 @@ namespace ArchiSteamFarm.IPC.Integration { [PublicAPI] public async Task InvokeAsync(HttpContext context) { if (context == null) { - ASF.ArchiLogger.LogNullError(nameof(context)); - - return; + throw new ArgumentNullException(nameof(context)); } HttpStatusCode authenticationStatus = await GetAuthenticationStatus(context).ConfigureAwait(false); @@ -78,13 +76,13 @@ namespace ArchiSteamFarm.IPC.Integration { } private static async Task GetAuthenticationStatus(HttpContext context) { - if (context == null) { - ASF.ArchiLogger.LogNullError(nameof(context)); - - return HttpStatusCode.InternalServerError; + if ((context == null) || (ClearFailedAuthorizationsTimer == null)) { + throw new ArgumentNullException(nameof(context) + " || " + nameof(ClearFailedAuthorizationsTimer)); } - if (string.IsNullOrEmpty(ASF.GlobalConfig.IPCPassword)) { + string? ipcPassword = ASF.GlobalConfig?.IPCPassword ?? GlobalConfig.DefaultIPCPassword; + + if (string.IsNullOrEmpty(ipcPassword)) { return HttpStatusCode.OK; } @@ -100,13 +98,13 @@ namespace ArchiSteamFarm.IPC.Integration { return HttpStatusCode.Unauthorized; } - string inputPassword = passwords.FirstOrDefault(password => !string.IsNullOrEmpty(password)); + string? inputPassword = passwords.FirstOrDefault(password => !string.IsNullOrEmpty(password)); if (string.IsNullOrEmpty(inputPassword)) { return HttpStatusCode.Unauthorized; } - bool authorized = inputPassword == ASF.GlobalConfig.IPCPassword; + bool authorized = inputPassword == ipcPassword; await AuthorizationSemaphore.WaitAsync().ConfigureAwait(false); diff --git a/ArchiSteamFarm/IPC/Integration/EnumSchemaFilter.cs b/ArchiSteamFarm/IPC/Integration/EnumSchemaFilter.cs index 8be5a7701..cafe4da2c 100644 --- a/ArchiSteamFarm/IPC/Integration/EnumSchemaFilter.cs +++ b/ArchiSteamFarm/IPC/Integration/EnumSchemaFilter.cs @@ -29,9 +29,11 @@ using Swashbuckle.AspNetCore.SwaggerGen; namespace ArchiSteamFarm.IPC.Integration { [UsedImplicitly] internal sealed class EnumSchemaFilter : ISchemaFilter { - public void Apply([NotNull] OpenApiSchema schema, [NotNull] SchemaFilterContext context) { + public void Apply(OpenApiSchema schema, SchemaFilterContext context) { if ((schema == null) || (context == null)) { - throw new ArgumentNullException(nameof(schema) + " || " + nameof(context)); + ASF.ArchiLogger.LogNullError(nameof(schema) + " || " + nameof(context)); + + return; } if (!context.Type.IsEnum) { @@ -44,8 +46,12 @@ namespace ArchiSteamFarm.IPC.Integration { OpenApiObject definition = new OpenApiObject(); - foreach (object enumValue in context.Type.GetEnumValues()) { - string enumName = Enum.GetName(context.Type, enumValue); + foreach (object? enumValue in context.Type.GetEnumValues()) { + if (enumValue == null) { + continue; + } + + string? enumName = Enum.GetName(context.Type, enumValue); if (string.IsNullOrEmpty(enumName)) { enumName = enumValue.ToString(); @@ -74,7 +80,11 @@ namespace ArchiSteamFarm.IPC.Integration { schema.AddExtension("x-definition", definition); } - private static bool TryCast(object value, out T typedValue) { + private static bool TryCast(object value, out T typedValue) where T : struct { + if (value == null) { + throw new ArgumentNullException(nameof(value)); + } + try { typedValue = (T) Convert.ChangeType(value, typeof(T)); diff --git a/ArchiSteamFarm/IPC/Requests/ASFRequest.cs b/ArchiSteamFarm/IPC/Requests/ASFRequest.cs index d9f8a2ff4..abcb45254 100644 --- a/ArchiSteamFarm/IPC/Requests/ASFRequest.cs +++ b/ArchiSteamFarm/IPC/Requests/ASFRequest.cs @@ -31,7 +31,7 @@ namespace ArchiSteamFarm.IPC.Requests { /// [JsonProperty(Required = Required.Always)] [Required] - public readonly GlobalConfig GlobalConfig; + public readonly GlobalConfig? GlobalConfig; [JsonConstructor] private ASFRequest() { } diff --git a/ArchiSteamFarm/IPC/Requests/BotGamesToRedeemInBackgroundRequest.cs b/ArchiSteamFarm/IPC/Requests/BotGamesToRedeemInBackgroundRequest.cs index e6268e6a7..2e322e045 100644 --- a/ArchiSteamFarm/IPC/Requests/BotGamesToRedeemInBackgroundRequest.cs +++ b/ArchiSteamFarm/IPC/Requests/BotGamesToRedeemInBackgroundRequest.cs @@ -36,7 +36,7 @@ namespace ArchiSteamFarm.IPC.Requests { /// [JsonProperty(Required = Required.Always)] [Required] - public readonly OrderedDictionary GamesToRedeemInBackground; + public readonly OrderedDictionary? GamesToRedeemInBackground; [JsonConstructor] private BotGamesToRedeemInBackgroundRequest() { } diff --git a/ArchiSteamFarm/IPC/Requests/BotInputRequest.cs b/ArchiSteamFarm/IPC/Requests/BotInputRequest.cs index 70ce1525a..158798698 100644 --- a/ArchiSteamFarm/IPC/Requests/BotInputRequest.cs +++ b/ArchiSteamFarm/IPC/Requests/BotInputRequest.cs @@ -35,7 +35,7 @@ namespace ArchiSteamFarm.IPC.Requests { /// Specifies the value for given input type (declared in ) /// [JsonProperty(Required = Required.Always)] - public readonly string Value; + public readonly string? Value; [JsonConstructor] private BotInputRequest() { } diff --git a/ArchiSteamFarm/IPC/Requests/BotRedeemRequest.cs b/ArchiSteamFarm/IPC/Requests/BotRedeemRequest.cs index 037130d52..ca0ef8d85 100644 --- a/ArchiSteamFarm/IPC/Requests/BotRedeemRequest.cs +++ b/ArchiSteamFarm/IPC/Requests/BotRedeemRequest.cs @@ -32,7 +32,7 @@ namespace ArchiSteamFarm.IPC.Requests { /// [JsonProperty(Required = Required.Always)] [Required] - public readonly ImmutableHashSet KeysToRedeem; + public readonly ImmutableHashSet? KeysToRedeem; [JsonConstructor] private BotRedeemRequest() { } diff --git a/ArchiSteamFarm/IPC/Requests/BotRenameRequest.cs b/ArchiSteamFarm/IPC/Requests/BotRenameRequest.cs index 73594fe4f..3a105eb19 100644 --- a/ArchiSteamFarm/IPC/Requests/BotRenameRequest.cs +++ b/ArchiSteamFarm/IPC/Requests/BotRenameRequest.cs @@ -31,7 +31,7 @@ namespace ArchiSteamFarm.IPC.Requests { /// [JsonProperty(Required = Required.Always)] [Required] - public readonly string NewName; + public readonly string? NewName; [JsonConstructor] private BotRenameRequest() { } diff --git a/ArchiSteamFarm/IPC/Requests/BotRequest.cs b/ArchiSteamFarm/IPC/Requests/BotRequest.cs index 26473e660..3441d418c 100644 --- a/ArchiSteamFarm/IPC/Requests/BotRequest.cs +++ b/ArchiSteamFarm/IPC/Requests/BotRequest.cs @@ -31,7 +31,7 @@ namespace ArchiSteamFarm.IPC.Requests { /// [JsonProperty(Required = Required.Always)] [Required] - public readonly BotConfig BotConfig; + public readonly BotConfig? BotConfig; [JsonConstructor] private BotRequest() { } diff --git a/ArchiSteamFarm/IPC/Requests/CommandRequest.cs b/ArchiSteamFarm/IPC/Requests/CommandRequest.cs index 75cf86103..1e09d37a6 100644 --- a/ArchiSteamFarm/IPC/Requests/CommandRequest.cs +++ b/ArchiSteamFarm/IPC/Requests/CommandRequest.cs @@ -32,9 +32,9 @@ namespace ArchiSteamFarm.IPC.Requests { /// [JsonProperty(Required = Required.Always)] [Required] - public readonly string Command; + public readonly string? Command; - internal CommandRequest([JetBrains.Annotations.NotNull] string command) { + internal CommandRequest(string command) { if (string.IsNullOrEmpty(command)) { throw new ArgumentNullException(nameof(command)); } diff --git a/ArchiSteamFarm/IPC/Requests/TwoFactorAuthenticationConfirmationsRequest.cs b/ArchiSteamFarm/IPC/Requests/TwoFactorAuthenticationConfirmationsRequest.cs index fc63a4473..a0fe0158e 100644 --- a/ArchiSteamFarm/IPC/Requests/TwoFactorAuthenticationConfirmationsRequest.cs +++ b/ArchiSteamFarm/IPC/Requests/TwoFactorAuthenticationConfirmationsRequest.cs @@ -50,7 +50,7 @@ namespace ArchiSteamFarm.IPC.Requests { /// Specifies IDs of the confirmations that we're supposed to handle. CreatorID of the confirmation is equal to ID of the object that triggered it - e.g. ID of the trade offer, or ID of the market listing. If not provided, or empty array, all confirmation IDs are considered for an action. /// [JsonProperty(Required = Required.DisallowNull)] - public ImmutableHashSet AcceptedCreatorIDs { get; private set; } + public ImmutableHashSet? AcceptedCreatorIDs { get; private set; } /// /// A helper property which works the same as but with values written as strings - for javascript compatibility purposes. Use either this one, or , not both. diff --git a/ArchiSteamFarm/IPC/Requests/WWWSendRequest.cs b/ArchiSteamFarm/IPC/Requests/WWWSendRequest.cs index 81eaacb4d..20a14a488 100644 --- a/ArchiSteamFarm/IPC/Requests/WWWSendRequest.cs +++ b/ArchiSteamFarm/IPC/Requests/WWWSendRequest.cs @@ -34,7 +34,7 @@ namespace ArchiSteamFarm.IPC.Requests { /// [Required] [JsonProperty(Required = Required.Always)] - public readonly string URL; + public readonly string? URL; [JsonConstructor] private WWWSendRequest() { } diff --git a/ArchiSteamFarm/IPC/Responses/ASFResponse.cs b/ArchiSteamFarm/IPC/Responses/ASFResponse.cs index e633403be..b9e7b9446 100644 --- a/ArchiSteamFarm/IPC/Responses/ASFResponse.cs +++ b/ArchiSteamFarm/IPC/Responses/ASFResponse.cs @@ -21,7 +21,6 @@ using System; using System.ComponentModel.DataAnnotations; -using JetBrains.Annotations; using Newtonsoft.Json; namespace ArchiSteamFarm.IPC.Responses { @@ -61,7 +60,7 @@ namespace ArchiSteamFarm.IPC.Responses { [Required] public readonly Version Version; - internal ASFResponse([NotNull] string buildVariant, [NotNull] GlobalConfig globalConfig, uint memoryUsage, DateTime processStartTime, [NotNull] Version version) { + internal ASFResponse(string buildVariant, GlobalConfig globalConfig, uint memoryUsage, DateTime processStartTime, Version version) { if (string.IsNullOrEmpty(buildVariant) || (globalConfig == null) || (memoryUsage == 0) || (processStartTime == DateTime.MinValue) || (version == null)) { throw new ArgumentNullException(nameof(buildVariant) + " || " + nameof(globalConfig) + " || " + nameof(memoryUsage) + " || " + nameof(processStartTime) + " || " + nameof(version)); } diff --git a/ArchiSteamFarm/IPC/Responses/GamesToRedeemInBackgroundResponse.cs b/ArchiSteamFarm/IPC/Responses/GamesToRedeemInBackgroundResponse.cs index 6b28dcb02..98dc7c3de 100644 --- a/ArchiSteamFarm/IPC/Responses/GamesToRedeemInBackgroundResponse.cs +++ b/ArchiSteamFarm/IPC/Responses/GamesToRedeemInBackgroundResponse.cs @@ -28,15 +28,15 @@ namespace ArchiSteamFarm.IPC.Responses { /// Keys that were redeemed and not used during the process, if available. /// [JsonProperty] - public readonly Dictionary UnusedKeys; + public readonly Dictionary? UnusedKeys; /// /// Keys that were redeemed and used during the process, if available. /// [JsonProperty] - public readonly Dictionary UsedKeys; + public readonly Dictionary? UsedKeys; - internal GamesToRedeemInBackgroundResponse(Dictionary unusedKeys = null, Dictionary usedKeys = null) { + internal GamesToRedeemInBackgroundResponse(Dictionary? unusedKeys = null, Dictionary? usedKeys = null) { UnusedKeys = unusedKeys; UsedKeys = usedKeys; } diff --git a/ArchiSteamFarm/IPC/Responses/GenericResponse.cs b/ArchiSteamFarm/IPC/Responses/GenericResponse.cs index f7a91238a..eaf740b95 100644 --- a/ArchiSteamFarm/IPC/Responses/GenericResponse.cs +++ b/ArchiSteamFarm/IPC/Responses/GenericResponse.cs @@ -32,12 +32,12 @@ namespace ArchiSteamFarm.IPC.Responses { /// The type of the result depends on the API endpoint that you've called. /// [JsonProperty] - public readonly T Result; + public readonly T? Result; - public GenericResponse(T result) : base(result != null) => Result = result; + public GenericResponse(T? result) : base(result != null) => Result = result; public GenericResponse(bool success, string message) : base(success, message) { } - public GenericResponse(bool success, T result) : base(success) => Result = result; - public GenericResponse(bool success, string message, T result) : base(success, message) => Result = result; + public GenericResponse(bool success, T? result) : base(success) => Result = result; + public GenericResponse(bool success, string message, T? result) : base(success, message) => Result = result; } public class GenericResponse { @@ -57,16 +57,10 @@ namespace ArchiSteamFarm.IPC.Responses { [Required] public readonly bool Success; - public GenericResponse(bool success, string message = null) { + public GenericResponse(bool success, string? message = null) { Success = success; - if (!string.IsNullOrEmpty(message)) { - Message = message; - - return; - } - - Message = success ? "OK" : Strings.WarningFailed; + Message = !string.IsNullOrEmpty(message) ? message! : success ? "OK" : Strings.WarningFailed; } } } diff --git a/ArchiSteamFarm/IPC/Responses/GitHubReleaseResponse.cs b/ArchiSteamFarm/IPC/Responses/GitHubReleaseResponse.cs index 5d0778cb7..2ef664da4 100644 --- a/ArchiSteamFarm/IPC/Responses/GitHubReleaseResponse.cs +++ b/ArchiSteamFarm/IPC/Responses/GitHubReleaseResponse.cs @@ -21,7 +21,6 @@ using System; using System.ComponentModel.DataAnnotations; -using JetBrains.Annotations; using Newtonsoft.Json; namespace ArchiSteamFarm.IPC.Responses { @@ -31,7 +30,7 @@ namespace ArchiSteamFarm.IPC.Responses { /// [JsonProperty(Required = Required.Always)] [Required] - public readonly string ChangelogHTML; + public readonly string? ChangelogHTML; /// /// Date of the release. @@ -52,9 +51,9 @@ namespace ArchiSteamFarm.IPC.Responses { /// [JsonProperty(Required = Required.Always)] [Required] - public readonly string Version; + public readonly string? Version; - internal GitHubReleaseResponse([NotNull] GitHub.ReleaseResponse releaseResponse) { + internal GitHubReleaseResponse(GitHub.ReleaseResponse releaseResponse) { if (releaseResponse == null) { throw new ArgumentNullException(nameof(releaseResponse)); } diff --git a/ArchiSteamFarm/IPC/Responses/TypeResponse.cs b/ArchiSteamFarm/IPC/Responses/TypeResponse.cs index 013baabb8..b3fb5578d 100644 --- a/ArchiSteamFarm/IPC/Responses/TypeResponse.cs +++ b/ArchiSteamFarm/IPC/Responses/TypeResponse.cs @@ -22,7 +22,6 @@ using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; -using JetBrains.Annotations; using Newtonsoft.Json; namespace ArchiSteamFarm.IPC.Responses { @@ -46,7 +45,7 @@ namespace ArchiSteamFarm.IPC.Responses { [Required] public readonly TypeProperties Properties; - internal TypeResponse([NotNull] Dictionary body, [NotNull] TypeProperties properties) { + internal TypeResponse(Dictionary body, TypeProperties properties) { if ((body == null) || (properties == null)) { throw new ArgumentNullException(nameof(body) + " || " + nameof(properties)); } @@ -63,7 +62,7 @@ namespace ArchiSteamFarm.IPC.Responses { /// This can be used for determining how should be interpreted. /// [JsonProperty] - public readonly string BaseType; + public readonly string? BaseType; /// /// Custom attributes of given type, if available. @@ -72,7 +71,7 @@ namespace ArchiSteamFarm.IPC.Responses { /// This can be used for determining main enum type if is . /// [JsonProperty] - public readonly HashSet CustomAttributes; + public readonly HashSet? CustomAttributes; /// /// Underlying type of given type, if available. @@ -81,9 +80,9 @@ namespace ArchiSteamFarm.IPC.Responses { /// This can be used for determining underlying enum type if is . /// [JsonProperty] - public readonly string UnderlyingType; + public readonly string? UnderlyingType; - internal TypeProperties(string baseType = null, HashSet customAttributes = null, string underlyingType = null) { + internal TypeProperties(string? baseType = null, HashSet? customAttributes = null, string? underlyingType = null) { BaseType = baseType; CustomAttributes = customAttributes; UnderlyingType = underlyingType; diff --git a/ArchiSteamFarm/IPC/Startup.cs b/ArchiSteamFarm/IPC/Startup.cs index df066b6bc..530071241 100644 --- a/ArchiSteamFarm/IPC/Startup.cs +++ b/ArchiSteamFarm/IPC/Startup.cs @@ -39,13 +39,14 @@ using Newtonsoft.Json.Serialization; #if NETFRAMEWORK using Newtonsoft.Json.Converters; + #endif namespace ArchiSteamFarm.IPC { internal sealed class Startup { private readonly IConfiguration Configuration; - public Startup([NotNull] IConfiguration configuration) => Configuration = configuration ?? throw new ArgumentNullException(nameof(configuration)); + public Startup(IConfiguration configuration) => Configuration = configuration ?? throw new ArgumentNullException(nameof(configuration)); #if NETFRAMEWORK [UsedImplicitly] @@ -55,9 +56,7 @@ namespace ArchiSteamFarm.IPC { public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { #endif if ((app == null) || (env == null)) { - ASF.ArchiLogger.LogNullError(nameof(app) + " || " + nameof(env)); - - return; + throw new ArgumentNullException(nameof(app) + " || " + nameof(env)); } if (Debugging.IsUserDebugging) { @@ -93,7 +92,9 @@ namespace ArchiSteamFarm.IPC { app.UseRouting(); #endif - if (!string.IsNullOrEmpty(ASF.GlobalConfig.IPCPassword)) { + string? ipcPassword = ASF.GlobalConfig?.IPCPassword ?? GlobalConfig.DefaultIPCPassword; + + if (!string.IsNullOrEmpty(ipcPassword)) { // We need ApiAuthenticationMiddleware for IPCPassword app.UseWhen(context => context.Request.Path.StartsWithSegments("/Api", StringComparison.OrdinalIgnoreCase), appBuilder => appBuilder.UseMiddleware()); @@ -125,9 +126,7 @@ namespace ArchiSteamFarm.IPC { public void ConfigureServices(IServiceCollection services) { if (services == null) { - ASF.ArchiLogger.LogNullError(nameof(services)); - - return; + throw new ArgumentNullException(nameof(services)); } // The order of dependency injection matters, pay attention to it @@ -138,8 +137,10 @@ namespace ArchiSteamFarm.IPC { // Add support for response compression services.AddResponseCompression(); + string? ipcPassword = ASF.GlobalConfig?.IPCPassword ?? GlobalConfig.DefaultIPCPassword; + // Add CORS to allow userscripts and third-party apps - if (!string.IsNullOrEmpty(ASF.GlobalConfig.IPCPassword)) { + if (!string.IsNullOrEmpty(ipcPassword)) { services.AddCors(options => options.AddDefaultPolicy(policyBuilder => policyBuilder.AllowAnyOrigin())); } @@ -209,7 +210,7 @@ namespace ArchiSteamFarm.IPC { #endif // Add support for controllers declared in custom plugins - HashSet assemblies = PluginsCore.LoadAssemblies(); + HashSet? assemblies = PluginsCore.LoadAssemblies(); if (assemblies != null) { foreach (Assembly assembly in assemblies) { diff --git a/ArchiSteamFarm/IPC/WebUtilities.cs b/ArchiSteamFarm/IPC/WebUtilities.cs index c874a6f42..5360ff213 100644 --- a/ArchiSteamFarm/IPC/WebUtilities.cs +++ b/ArchiSteamFarm/IPC/WebUtilities.cs @@ -1,4 +1,4 @@ -// _ _ _ ____ _ _____ +// _ _ _ ____ _ _____ // / \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___ // / _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \ // / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | | @@ -29,9 +29,7 @@ namespace ArchiSteamFarm.IPC { internal static class WebUtilities { internal static async Task Generate(this HttpResponse httpResponse, HttpStatusCode statusCode) { if (httpResponse == null) { - ASF.ArchiLogger.LogNullError(nameof(httpResponse)); - - return; + throw new ArgumentNullException(nameof(httpResponse)); } ushort statusCodeNumber = (ushort) statusCode; @@ -40,24 +38,20 @@ namespace ArchiSteamFarm.IPC { await httpResponse.WriteAsync(statusCodeNumber + " - " + statusCode).ConfigureAwait(false); } - internal static string GetUnifiedName(this Type type) { + internal static string? GetUnifiedName(this Type type) { if (type == null) { - ASF.ArchiLogger.LogNullError(nameof(type)); - - return null; + throw new ArgumentNullException(nameof(type)); } return type.GenericTypeArguments.Length == 0 ? type.FullName : type.Namespace + "." + type.Name + string.Join("", type.GenericTypeArguments.Select(innerType => '[' + innerType.GetUnifiedName() + ']')); } - internal static Type ParseType(string typeText) { + internal static Type? ParseType(string typeText) { if (string.IsNullOrEmpty(typeText)) { - ASF.ArchiLogger.LogNullError(nameof(typeText)); - - return null; + throw new ArgumentNullException(nameof(typeText)); } - Type targetType = Type.GetType(typeText); + Type? targetType = Type.GetType(typeText); if (targetType != null) { return targetType; diff --git a/ArchiSteamFarm/Json/Steam.cs b/ArchiSteamFarm/Json/Steam.cs index 2d4f63c9e..61133aa0d 100644 --- a/ArchiSteamFarm/Json/Steam.cs +++ b/ArchiSteamFarm/Json/Steam.cs @@ -42,7 +42,7 @@ namespace ArchiSteamFarm.Json { [JsonIgnore] [PublicAPI] - public ImmutableDictionary AdditionalProperties { get; internal set; } + public ImmutableDictionary? AdditionalProperties { get; internal set; } [JsonIgnore] [PublicAPI] @@ -81,7 +81,7 @@ namespace ArchiSteamFarm.Json { [JsonIgnore] [PublicAPI] - public ImmutableHashSet Tags { get; internal set; } + public ImmutableHashSet? Tags { get; internal set; } [JsonIgnore] [PublicAPI] @@ -93,7 +93,6 @@ namespace ArchiSteamFarm.Json { #pragma warning disable IDE0051 [JsonProperty(PropertyName = "amount", Required = Required.Always)] - [JetBrains.Annotations.NotNull] private string AmountText { get => Amount.ToString(); @@ -117,7 +116,6 @@ namespace ArchiSteamFarm.Json { #pragma warning disable IDE0052 [JsonProperty(PropertyName = "assetid", Required = Required.DisallowNull)] - [JetBrains.Annotations.NotNull] private string AssetIDText { get => AssetID.ToString(); @@ -141,7 +139,6 @@ namespace ArchiSteamFarm.Json { #pragma warning disable IDE0051 [JsonProperty(PropertyName = "classid", Required = Required.DisallowNull)] - [JetBrains.Annotations.NotNull] private string ClassIDText { set { if (string.IsNullOrEmpty(value)) { @@ -161,7 +158,6 @@ namespace ArchiSteamFarm.Json { #pragma warning disable IDE0051 [JsonProperty(PropertyName = "contextid", Required = Required.DisallowNull)] - [JetBrains.Annotations.NotNull] private string ContextIDText { get => ContextID.ToString(); @@ -185,7 +181,6 @@ namespace ArchiSteamFarm.Json { #pragma warning disable IDE0051 [JsonProperty(PropertyName = "id", Required = Required.DisallowNull)] - [JetBrains.Annotations.NotNull] private string IDText { set => AssetIDText = value; } @@ -193,7 +188,6 @@ namespace ArchiSteamFarm.Json { #pragma warning disable IDE0051 [JsonProperty(PropertyName = "instanceid", Required = Required.DisallowNull)] - [JetBrains.Annotations.NotNull] private string InstanceIDText { set { if (string.IsNullOrEmpty(value)) { @@ -212,7 +206,7 @@ namespace ArchiSteamFarm.Json { #pragma warning restore IDE0051 // Constructed from trades being received or plugins - public Asset(uint appID, ulong contextID, ulong classID, uint amount, ulong instanceID = 0, ulong assetID = 0, bool marketable = true, bool tradable = true, ImmutableHashSet tags = null, uint realAppID = 0, EType type = EType.Unknown, ERarity rarity = ERarity.Unknown) { + public Asset(uint appID, ulong contextID, ulong classID, uint amount, ulong instanceID = 0, ulong assetID = 0, bool marketable = true, bool tradable = true, ImmutableHashSet? tags = null, uint realAppID = 0, EType type = EType.Unknown, ERarity rarity = ERarity.Unknown) { if ((appID == 0) || (contextID == 0) || (classID == 0) || (amount == 0)) { throw new ArgumentNullException(nameof(appID) + " || " + nameof(contextID) + " || " + nameof(classID) + " || " + nameof(amount)); } @@ -237,19 +231,18 @@ namespace ArchiSteamFarm.Json { [JsonConstructor] private Asset() { } - [JetBrains.Annotations.NotNull] internal Asset CreateShallowCopy() => (Asset) MemberwiseClone(); public sealed class Tag { [JsonProperty(PropertyName = "category", Required = Required.Always)] [PublicAPI] - public readonly string Identifier; + public readonly string? Identifier; [JsonProperty(PropertyName = "internal_name", Required = Required.Always)] [PublicAPI] - public readonly string Value; + public readonly string? Value; - internal Tag([JetBrains.Annotations.NotNull] string identifier, [JetBrains.Annotations.NotNull] string value) { + internal Tag(string identifier, string value) { if (string.IsNullOrEmpty(identifier) || string.IsNullOrEmpty(value)) { throw new ArgumentNullException(nameof(identifier) + " || " + nameof(value)); } @@ -354,13 +347,13 @@ namespace ArchiSteamFarm.Json { [SuppressMessage("ReSharper", "ClassCannotBeInstantiated")] internal sealed class InventoryResponse : EResultResponse { [JsonProperty(PropertyName = "assets", Required = Required.DisallowNull)] - internal readonly ImmutableHashSet Assets; + internal readonly ImmutableHashSet? Assets; [JsonProperty(PropertyName = "descriptions", Required = Required.DisallowNull)] - internal readonly ImmutableHashSet Descriptions; + internal readonly ImmutableHashSet? Descriptions; [JsonProperty(PropertyName = "error", Required = Required.DisallowNull)] - internal readonly string Error; + internal readonly string? Error; [JsonProperty(PropertyName = "total_inventory_count", Required = Required.DisallowNull)] internal readonly uint TotalInventoryCount; @@ -439,7 +432,7 @@ namespace ArchiSteamFarm.Json { foreach (Asset.Tag tag in Tags) { switch (tag.Identifier) { case "Game": - if ((tag.Value.Length <= 4) || !tag.Value.StartsWith("app_", StringComparison.Ordinal)) { + if (string.IsNullOrEmpty(tag.Value) || (tag.Value!.Length <= 4) || !tag.Value.StartsWith("app_", StringComparison.Ordinal)) { ASF.ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(tag.Value), tag.Value)); break; @@ -528,7 +521,7 @@ namespace ArchiSteamFarm.Json { } [JsonExtensionData] - internal Dictionary AdditionalProperties { + internal Dictionary? AdditionalProperties { get; [UsedImplicitly] set; @@ -542,7 +535,7 @@ namespace ArchiSteamFarm.Json { internal bool Marketable { get; set; } [JsonProperty(PropertyName = "tags", Required = Required.DisallowNull)] - internal ImmutableHashSet Tags { get; set; } + internal ImmutableHashSet? Tags { get; set; } internal bool Tradable { get; set; } @@ -608,7 +601,7 @@ namespace ArchiSteamFarm.Json { [SuppressMessage("ReSharper", "ClassCannotBeInstantiated")] internal sealed class NewDiscoveryQueueResponse { [JsonProperty(PropertyName = "queue", Required = Required.Always)] - internal readonly ImmutableHashSet Queue; + internal readonly ImmutableHashSet? Queue; [JsonConstructor] private NewDiscoveryQueueResponse() { } @@ -617,7 +610,7 @@ namespace ArchiSteamFarm.Json { [SuppressMessage("ReSharper", "ClassCannotBeInstantiated")] internal sealed class RedeemWalletResponse : EResultResponse { [JsonProperty(PropertyName = "wallet", Required = Required.DisallowNull)] - internal readonly InternalKeyDetails KeyDetails; + internal readonly InternalKeyDetails? KeyDetails; [JsonProperty(PropertyName = "detail", Required = Required.DisallowNull)] internal readonly EPurchaseResultDetail? PurchaseResultDetail; @@ -697,10 +690,10 @@ namespace ArchiSteamFarm.Json { internal readonly ECommentPermission CommentPermission; [JsonProperty(PropertyName = "PrivacySettings", Required = Required.Always)] - internal readonly PrivacySettings Settings; + internal readonly PrivacySettings? Settings; // Constructed from privacy change request - internal UserPrivacy([JetBrains.Annotations.NotNull] PrivacySettings settings, ECommentPermission commentPermission) { + internal UserPrivacy(PrivacySettings settings, ECommentPermission commentPermission) { Settings = settings ?? throw new ArgumentNullException(nameof(settings)); CommentPermission = commentPermission; } diff --git a/ArchiSteamFarm/MobileAuthenticator.cs b/ArchiSteamFarm/MobileAuthenticator.cs index 1c5aec024..bb8cab7a8 100644 --- a/ArchiSteamFarm/MobileAuthenticator.cs +++ b/ArchiSteamFarm/MobileAuthenticator.cs @@ -53,36 +53,41 @@ namespace ArchiSteamFarm { #pragma warning disable 649 [JsonProperty(PropertyName = "identity_secret", Required = Required.Always)] - private readonly string IdentitySecret; + private readonly string? IdentitySecret; #pragma warning restore 649 #pragma warning disable 649 [JsonProperty(PropertyName = "shared_secret", Required = Required.Always)] - private readonly string SharedSecret; + private readonly string? SharedSecret; #pragma warning restore 649 - private Bot Bot; + private Bot? Bot; [JsonConstructor] private MobileAuthenticator() => CachedDeviceID = new ArchiCacheable(ResolveDeviceID); public void Dispose() => CachedDeviceID.Dispose(); - internal async Task GenerateToken() { + internal async Task GenerateToken() { + if (Bot == null) { + throw new ArgumentNullException(nameof(Bot)); + } + uint time = await GetSteamTime().ConfigureAwait(false); if (time == 0) { - Bot.ArchiLogger.LogNullError(nameof(time)); - - return null; + throw new ArgumentNullException(nameof(time)); } return GenerateTokenForTime(time); } - [ItemCanBeNull] - internal async Task> GetConfirmations() { - (bool success, string deviceID) = await CachedDeviceID.GetValue().ConfigureAwait(false); + internal async Task?> GetConfirmations() { + if (Bot == null) { + throw new ArgumentNullException(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))); @@ -93,12 +98,10 @@ namespace ArchiSteamFarm { uint time = await GetSteamTime().ConfigureAwait(false); if (time == 0) { - Bot.ArchiLogger.LogNullError(nameof(time)); - - return null; + throw new ArgumentNullException(nameof(time)); } - string confirmationHash = GenerateConfirmationHash(time, "conf"); + string? confirmationHash = GenerateConfirmationHash(time, "conf"); if (string.IsNullOrEmpty(confirmationHash)) { Bot.ArchiLogger.LogNullError(nameof(confirmationHash)); @@ -108,7 +111,7 @@ namespace ArchiSteamFarm { await LimitConfirmationsRequestsAsync().ConfigureAwait(false); - using IDocument htmlDocument = await Bot.ArchiWebHandler.GetConfirmations(deviceID, confirmationHash, time).ConfigureAwait(false); + using IDocument? htmlDocument = await Bot.ArchiWebHandler.GetConfirmations(deviceID!, confirmationHash!, time).ConfigureAwait(false); if (htmlDocument == null) { return null; @@ -123,7 +126,7 @@ namespace ArchiSteamFarm { } foreach (IElement confirmationNode in confirmationNodes) { - string idText = confirmationNode.GetAttributeValue("data-confid"); + string? idText = confirmationNode.GetAttribute("data-confid"); if (string.IsNullOrEmpty(idText)) { Bot.ArchiLogger.LogNullError(nameof(idText)); @@ -137,7 +140,7 @@ namespace ArchiSteamFarm { return null; } - string keyText = confirmationNode.GetAttributeValue("data-key"); + string? keyText = confirmationNode.GetAttribute("data-key"); if (string.IsNullOrEmpty(keyText)) { Bot.ArchiLogger.LogNullError(nameof(keyText)); @@ -151,7 +154,7 @@ namespace ArchiSteamFarm { return null; } - string creatorText = confirmationNode.GetAttributeValue("data-creator"); + string? creatorText = confirmationNode.GetAttribute("data-creator"); if (string.IsNullOrEmpty(creatorText)) { Bot.ArchiLogger.LogNullError(nameof(creatorText)); @@ -165,7 +168,7 @@ namespace ArchiSteamFarm { return null; } - string typeText = confirmationNode.GetAttributeValue("data-type"); + string? typeText = confirmationNode.GetAttribute("data-type"); if (string.IsNullOrEmpty(typeText)) { Bot.ArchiLogger.LogNullError(nameof(typeText)); @@ -192,13 +195,11 @@ namespace ArchiSteamFarm { } internal async Task HandleConfirmations(IReadOnlyCollection confirmations, bool accept) { - if ((confirmations == null) || (confirmations.Count == 0)) { - Bot.ArchiLogger.LogNullError(nameof(confirmations)); - - return false; + if ((confirmations == null) || (confirmations.Count == 0) || (Bot == null)) { + throw new ArgumentNullException(nameof(confirmations) + " || " + nameof(Bot)); } - (bool success, string deviceID) = await CachedDeviceID.GetValue().ConfigureAwait(false); + (bool success, string? deviceID) = await CachedDeviceID.GetValue().ConfigureAwait(false); if (!success || string.IsNullOrEmpty(deviceID)) { Bot.ArchiLogger.LogGenericError(string.Format(Strings.WarningFailedWithError, nameof(deviceID))); @@ -209,12 +210,10 @@ namespace ArchiSteamFarm { uint time = await GetSteamTime().ConfigureAwait(false); if (time == 0) { - Bot.ArchiLogger.LogNullError(nameof(time)); - - return false; + throw new ArgumentNullException(nameof(time)); } - string confirmationHash = GenerateConfirmationHash(time, "conf"); + string? confirmationHash = GenerateConfirmationHash(time, "conf"); if (string.IsNullOrEmpty(confirmationHash)) { Bot.ArchiLogger.LogNullError(nameof(confirmationHash)); @@ -222,7 +221,7 @@ namespace ArchiSteamFarm { return false; } - bool? result = await Bot.ArchiWebHandler.HandleConfirmations(deviceID, confirmationHash, time, confirmations, accept).ConfigureAwait(false); + bool? result = await Bot.ArchiWebHandler.HandleConfirmations(deviceID!, confirmationHash!, time, confirmations, accept).ConfigureAwait(false); if (!result.HasValue) { // Request timed out @@ -238,7 +237,7 @@ namespace ArchiSteamFarm { // In this case, we'll accept all pending confirmations one-by-one, synchronously (as Steam can't handle them in parallel) // We totally ignore actual result returned by those calls, abort only if request timed out foreach (Confirmation confirmation in confirmations) { - bool? confirmationResult = await Bot.ArchiWebHandler.HandleConfirmation(deviceID, confirmationHash, time, confirmation.ID, confirmation.Key, accept).ConfigureAwait(false); + bool? confirmationResult = await Bot.ArchiWebHandler.HandleConfirmation(deviceID!, confirmationHash!, time, confirmation.ID, confirmation.Key, accept).ConfigureAwait(false); if (!confirmationResult.HasValue) { return false; @@ -248,13 +247,11 @@ namespace ArchiSteamFarm { return true; } - internal void Init([JetBrains.Annotations.NotNull] Bot bot) => Bot = bot ?? throw new ArgumentNullException(nameof(bot)); + internal void Init(Bot bot) => Bot = bot ?? throw new ArgumentNullException(nameof(bot)); - private string GenerateConfirmationHash(uint time, string tag = null) { - if (time == 0) { - Bot.ArchiLogger.LogNullError(nameof(time)); - - return null; + 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)); } byte[] identitySecret; @@ -271,7 +268,7 @@ namespace ArchiSteamFarm { byte bufferSize = 8; if (!string.IsNullOrEmpty(tag)) { - bufferSize += (byte) Math.Min(32, tag.Length); + bufferSize += (byte) Math.Min(32, tag!.Length); } byte[] timeArray = BitConverter.GetBytes((ulong) time); @@ -295,11 +292,9 @@ namespace ArchiSteamFarm { return Convert.ToBase64String(hash); } - private string GenerateTokenForTime(uint time) { - if (time == 0) { - Bot.ArchiLogger.LogNullError(nameof(time)); - - return null; + private string? GenerateTokenForTime(uint time) { + if ((time == 0) || (Bot == null) || string.IsNullOrEmpty(SharedSecret)) { + throw new ArgumentNullException(nameof(time) + " || " + nameof(Bot) + " || " + nameof(SharedSecret)); } byte[] sharedSecret; @@ -351,6 +346,10 @@ namespace ArchiSteamFarm { } private async Task GetSteamTime() { + if (Bot == null) { + throw new ArgumentNullException(nameof(Bot)); + } + if (SteamTimeDifference.HasValue && (DateTime.UtcNow.Subtract(LastSteamTimeCheck).TotalHours < SteamTimeTTL)) { return (uint) (Utilities.GetUnixTime() + SteamTimeDifference.Value); } @@ -379,12 +378,12 @@ namespace ArchiSteamFarm { private static async Task LimitConfirmationsRequestsAsync() { if (ASF.ConfirmationsSemaphore == null) { - ASF.ArchiLogger.LogNullError(nameof(ASF.ConfirmationsSemaphore)); - - return; + throw new ArgumentNullException(nameof(ASF.ConfirmationsSemaphore)); } - if (ASF.GlobalConfig.ConfirmationsLimiterDelay == 0) { + byte confirmationsLimiterDelay = ASF.GlobalConfig?.ConfirmationsLimiterDelay ?? GlobalConfig.DefaultConfirmationsLimiterDelay; + + if (confirmationsLimiterDelay == 0) { return; } @@ -392,14 +391,18 @@ namespace ArchiSteamFarm { Utilities.InBackground( async () => { - await Task.Delay(ASF.GlobalConfig.ConfirmationsLimiterDelay * 1000).ConfigureAwait(false); + await Task.Delay(confirmationsLimiterDelay * 1000).ConfigureAwait(false); ASF.ConfirmationsSemaphore.Release(); } ); } - private async Task<(bool Success, string Result)> ResolveDeviceID() { - string deviceID = await Bot.ArchiHandler.GetTwoFactorDeviceIdentifier(Bot.SteamID).ConfigureAwait(false); + private async Task<(bool Success, string? Result)> ResolveDeviceID() { + if (Bot == null) { + throw new ArgumentNullException(nameof(Bot)); + } + + string? deviceID = await Bot.ArchiHandler.GetTwoFactorDeviceIdentifier(Bot.SteamID).ConfigureAwait(false); if (string.IsNullOrEmpty(deviceID)) { Bot.ArchiLogger.LogGenericWarning(Strings.WarningFailed); diff --git a/ArchiSteamFarm/NLog/ArchiLogger.cs b/ArchiSteamFarm/NLog/ArchiLogger.cs index 82f6eb074..f9a48af6a 100644 --- a/ArchiSteamFarm/NLog/ArchiLogger.cs +++ b/ArchiSteamFarm/NLog/ArchiLogger.cs @@ -31,7 +31,7 @@ namespace ArchiSteamFarm.NLog { public sealed class ArchiLogger { private readonly Logger Logger; - public ArchiLogger([NotNull] string name) { + public ArchiLogger(string name) { if (string.IsNullOrEmpty(name)) { throw new ArgumentNullException(nameof(name)); } @@ -40,22 +40,18 @@ namespace ArchiSteamFarm.NLog { } [PublicAPI] - public void LogGenericDebug(string message, [CallerMemberName] string previousMethodName = null) { + public void LogGenericDebug(string message, [CallerMemberName] string? previousMethodName = null) { if (string.IsNullOrEmpty(message)) { - LogNullError(nameof(message)); - - return; + throw new ArgumentNullException(nameof(message)); } Logger.Debug($"{previousMethodName}() {message}"); } [PublicAPI] - public void LogGenericDebuggingException(Exception exception, [CallerMemberName] string previousMethodName = null) { + public void LogGenericDebuggingException(Exception exception, [CallerMemberName] string? previousMethodName = null) { if (exception == null) { - LogNullError(nameof(exception)); - - return; + throw new ArgumentNullException(nameof(exception)); } if (!Debugging.IsUserDebugging) { @@ -66,85 +62,71 @@ namespace ArchiSteamFarm.NLog { } [PublicAPI] - public void LogGenericError(string message, [CallerMemberName] string previousMethodName = null) { + public void LogGenericError(string message, [CallerMemberName] string? previousMethodName = null) { if (string.IsNullOrEmpty(message)) { - LogNullError(nameof(message)); - - return; + throw new ArgumentNullException(nameof(message)); } Logger.Error($"{previousMethodName}() {message}"); } [PublicAPI] - public void LogGenericException(Exception exception, [CallerMemberName] string previousMethodName = null) { + public void LogGenericException(Exception exception, [CallerMemberName] string? previousMethodName = null) { if (exception == null) { - LogNullError(nameof(exception)); - - return; + throw new ArgumentNullException(nameof(exception)); } Logger.Error(exception, $"{previousMethodName}()"); } [PublicAPI] - public void LogGenericInfo(string message, [CallerMemberName] string previousMethodName = null) { + public void LogGenericInfo(string message, [CallerMemberName] string? previousMethodName = null) { if (string.IsNullOrEmpty(message)) { - LogNullError(nameof(message)); - - return; + throw new ArgumentNullException(nameof(message)); } Logger.Info($"{previousMethodName}() {message}"); } [PublicAPI] - public void LogGenericTrace(string message, [CallerMemberName] string previousMethodName = null) { + public void LogGenericTrace(string message, [CallerMemberName] string? previousMethodName = null) { if (string.IsNullOrEmpty(message)) { - LogNullError(nameof(message)); - - return; + throw new ArgumentNullException(nameof(message)); } Logger.Trace($"{previousMethodName}() {message}"); } [PublicAPI] - public void LogGenericWarning(string message, [CallerMemberName] string previousMethodName = null) { + public void LogGenericWarning(string message, [CallerMemberName] string? previousMethodName = null) { if (string.IsNullOrEmpty(message)) { - LogNullError(nameof(message)); - - return; + throw new ArgumentNullException(nameof(message)); } Logger.Warn($"{previousMethodName}() {message}"); } [PublicAPI] - public void LogGenericWarningException(Exception exception, [CallerMemberName] string previousMethodName = null) { + public void LogGenericWarningException(Exception exception, [CallerMemberName] string? previousMethodName = null) { if (exception == null) { - LogNullError(nameof(exception)); - - return; + throw new ArgumentNullException(nameof(exception)); } Logger.Warn(exception, $"{previousMethodName}()"); } [PublicAPI] - public void LogNullError(string nullObjectName, [CallerMemberName] string previousMethodName = null) { + public void LogNullError(string nullObjectName, [CallerMemberName] string? previousMethodName = null) { if (string.IsNullOrEmpty(nullObjectName)) { - return; + throw new ArgumentNullException(nameof(nullObjectName)); } LogGenericError(string.Format(Strings.ErrorObjectIsNull, nullObjectName), previousMethodName); } - internal void LogChatMessage(bool echo, string message, ulong chatGroupID = 0, ulong chatID = 0, ulong steamID = 0, [CallerMemberName] string previousMethodName = null) { + internal void LogChatMessage(bool echo, string message, ulong chatGroupID = 0, ulong chatID = 0, ulong steamID = 0, [CallerMemberName] string? previousMethodName = null) { if (string.IsNullOrEmpty(message) || (((chatGroupID == 0) || (chatID == 0)) && (steamID == 0))) { - LogNullError(nameof(message) + " || " + "((" + nameof(chatGroupID) + " || " + nameof(chatID) + ") && " + nameof(steamID) + ")"); - - return; + throw new ArgumentNullException(nameof(message) + " || " + "((" + nameof(chatGroupID) + " || " + nameof(chatID) + ") && " + nameof(steamID) + ")"); } StringBuilder loggedMessage = new StringBuilder(previousMethodName + "() " + message + " " + (echo ? "->" : "<-") + " "); @@ -169,11 +151,9 @@ namespace ArchiSteamFarm.NLog { Logger.Log(logEventInfo); } - internal async Task LogFatalException(Exception exception, [CallerMemberName] string previousMethodName = null) { + internal async Task LogFatalException(Exception exception, [CallerMemberName] string? previousMethodName = null) { if (exception == null) { - LogNullError(nameof(exception)); - - return; + throw new ArgumentNullException(nameof(exception)); } Logger.Fatal(exception, $"{previousMethodName}()"); diff --git a/ArchiSteamFarm/NLog/HistoryTarget.cs b/ArchiSteamFarm/NLog/HistoryTarget.cs index ae32100b6..5ea2b15c7 100644 --- a/ArchiSteamFarm/NLog/HistoryTarget.cs +++ b/ArchiSteamFarm/NLog/HistoryTarget.cs @@ -75,12 +75,12 @@ namespace ArchiSteamFarm.NLog { NewHistoryEntry?.Invoke(this, new NewHistoryEntryArgs(message)); } - internal event EventHandler NewHistoryEntry; + internal event EventHandler? NewHistoryEntry; internal sealed class NewHistoryEntryArgs : EventArgs { internal readonly string Message; - internal NewHistoryEntryArgs([NotNull] string message) => Message = message ?? throw new ArgumentNullException(nameof(message)); + internal NewHistoryEntryArgs(string message) => Message = message ?? throw new ArgumentNullException(nameof(message)); } } } diff --git a/ArchiSteamFarm/NLog/Logging.cs b/ArchiSteamFarm/NLog/Logging.cs index 8be36d5a3..b0359b6ca 100644 --- a/ArchiSteamFarm/NLog/Logging.cs +++ b/ArchiSteamFarm/NLog/Logging.cs @@ -28,7 +28,6 @@ using System.Threading.Tasks; using ArchiSteamFarm.Collections; using ArchiSteamFarm.IPC; using ArchiSteamFarm.Localization; -using JetBrains.Annotations; using NLog; using NLog.Config; using NLog.Targets; @@ -64,14 +63,12 @@ namespace ArchiSteamFarm.NLog { } } - internal static async Task GetUserInput(ASF.EUserInputType userInputType, string botName = SharedInfo.ASF) { + internal static async Task GetUserInput(ASF.EUserInputType userInputType, string botName = SharedInfo.ASF) { if ((userInputType == ASF.EUserInputType.None) || !Enum.IsDefined(typeof(ASF.EUserInputType), userInputType) || string.IsNullOrEmpty(botName)) { - ASF.ArchiLogger.LogNullError(nameof(userInputType) + " || " + nameof(botName)); - - return null; + throw new ArgumentNullException(nameof(userInputType) + " || " + nameof(botName)); } - if (ASF.GlobalConfig.Headless) { + if (ASF.GlobalConfig?.Headless ?? GlobalConfig.DefaultHeadless) { ASF.ArchiLogger.LogGenericWarning(Strings.ErrorUserInputRunningInHeadlessMode); return null; @@ -196,7 +193,7 @@ namespace ArchiSteamFarm.NLog { return; } - HistoryTarget historyTarget = LogManager.Configuration.AllTargets.OfType().FirstOrDefault(); + HistoryTarget? historyTarget = LogManager.Configuration.AllTargets.OfType().FirstOrDefault(); if ((historyTarget == null) && !IsUsingCustomConfiguration) { historyTarget = new HistoryTarget("History") { @@ -214,7 +211,7 @@ namespace ArchiSteamFarm.NLog { } internal static void StartInteractiveConsole() { - if (ASF.GlobalConfig.SteamOwnerID == 0) { + if ((ASF.GlobalConfig?.SteamOwnerID ?? GlobalConfig.DefaultSteamOwnerID) == 0) { ASF.ArchiLogger.LogGenericWarning(string.Format(Strings.InteractiveConsoleNotAvailable, nameof(ASF.GlobalConfig.SteamOwnerID))); return; @@ -230,7 +227,6 @@ namespace ArchiSteamFarm.NLog { return Console.ReadLine(); } - [NotNull] private static string ConsoleReadLineMasked(char mask = '*') { StringBuilder result = new StringBuilder(); @@ -287,15 +283,17 @@ namespace ArchiSteamFarm.NLog { continue; } - if (!string.IsNullOrEmpty(ASF.GlobalConfig.CommandPrefix) && command.StartsWith(ASF.GlobalConfig.CommandPrefix, StringComparison.Ordinal)) { - command = command.Substring(ASF.GlobalConfig.CommandPrefix.Length); + string commandPrefix = ASF.GlobalConfig?.CommandPrefix ?? GlobalConfig.DefaultCommandPrefix; + + if (!string.IsNullOrEmpty(commandPrefix) && command.StartsWith(commandPrefix, StringComparison.Ordinal)) { + command = command.Substring(commandPrefix.Length); if (string.IsNullOrEmpty(command)) { continue; } } - Bot targetBot = Bot.Bots.OrderBy(bot => bot.Key, Bot.BotsComparer).Select(bot => bot.Value).FirstOrDefault(); + Bot? targetBot = Bot.Bots.OrderBy(bot => bot.Key, Bot.BotsComparer).Select(bot => bot.Value).FirstOrDefault(); if (targetBot == null) { Console.WriteLine(@"<< " + Strings.ErrorNoBotsDefined); @@ -305,7 +303,9 @@ namespace ArchiSteamFarm.NLog { Console.WriteLine(@"<> " + Strings.Executing); - string response = await targetBot.Commands.Response(ASF.GlobalConfig.SteamOwnerID, command).ConfigureAwait(false); + ulong steamOwnerID = ASF.GlobalConfig?.SteamOwnerID ?? GlobalConfig.DefaultSteamOwnerID; + + string? response = await targetBot.Commands.Response(steamOwnerID, command).ConfigureAwait(false); if (string.IsNullOrEmpty(response)) { ASF.ArchiLogger.LogNullError(nameof(response)); @@ -339,9 +339,9 @@ namespace ArchiSteamFarm.NLog { } } - private static void OnConfigurationChanged(object sender, LoggingConfigurationChangedEventArgs e) { - if ((sender == null) || (e == null)) { - ASF.ArchiLogger.LogNullError(nameof(sender) + " || " + nameof(e)); + private static void OnConfigurationChanged(object? sender, LoggingConfigurationChangedEventArgs e) { + if (e == null) { + ASF.ArchiLogger.LogNullError(nameof(e)); return; } @@ -352,7 +352,7 @@ namespace ArchiSteamFarm.NLog { OnUserInputStart(); } - HistoryTarget historyTarget = LogManager.Configuration.AllTargets.OfType().FirstOrDefault(); + HistoryTarget? historyTarget = LogManager.Configuration.AllTargets.OfType().FirstOrDefault(); ArchiKestrel.OnNewHistoryTarget(historyTarget); } diff --git a/ArchiSteamFarm/NLog/SteamTarget.cs b/ArchiSteamFarm/NLog/SteamTarget.cs index 07a6a46a8..d2c499644 100644 --- a/ArchiSteamFarm/NLog/SteamTarget.cs +++ b/ArchiSteamFarm/NLog/SteamTarget.cs @@ -19,6 +19,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +using System; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading.Tasks; @@ -36,7 +37,7 @@ namespace ArchiSteamFarm.NLog { // This is NLog config property, it must have public get() and set() capabilities [PublicAPI] - public string BotName { get; set; } + public string? BotName { get; set; } // This is NLog config property, it must have public get() and set() capabilities [PublicAPI] @@ -71,10 +72,10 @@ namespace ArchiSteamFarm.NLog { return; } - Bot bot = null; + Bot? bot = null; if (!string.IsNullOrEmpty(BotName)) { - bot = Bot.GetBot(BotName); + bot = Bot.GetBot(BotName!); if (bot?.IsConnectedAndLoggedOn != true) { return; @@ -88,15 +89,13 @@ namespace ArchiSteamFarm.NLog { } } - private async Task SendGroupMessage(string message, Bot bot = null) { + private async Task SendGroupMessage(string message, Bot? bot = null) { if (string.IsNullOrEmpty(message)) { - ASF.ArchiLogger.LogNullError(nameof(message)); - - return; + throw new ArgumentNullException(nameof(message)); } if (bot == null) { - bot = Bot.Bots.Values.FirstOrDefault(targetBot => targetBot.IsConnectedAndLoggedOn); + bot = Bot.Bots?.Values.FirstOrDefault(targetBot => targetBot.IsConnectedAndLoggedOn); if (bot == null) { return; @@ -108,15 +107,13 @@ namespace ArchiSteamFarm.NLog { } } - private async Task SendPrivateMessage(string message, Bot bot = null) { + private async Task SendPrivateMessage(string message, Bot? bot = null) { if (string.IsNullOrEmpty(message)) { - ASF.ArchiLogger.LogNullError(nameof(message)); - - return; + throw new ArgumentNullException(nameof(message)); } if (bot == null) { - bot = Bot.Bots.Values.FirstOrDefault(targetBot => targetBot.IsConnectedAndLoggedOn && (targetBot.SteamID != SteamID)); + bot = Bot.Bots?.Values.FirstOrDefault(targetBot => targetBot.IsConnectedAndLoggedOn && (targetBot.SteamID != SteamID)); if (bot == null) { return; diff --git a/ArchiSteamFarm/OS.cs b/ArchiSteamFarm/OS.cs index 376a9a762..e2f557dfc 100644 --- a/ArchiSteamFarm/OS.cs +++ b/ArchiSteamFarm/OS.cs @@ -29,7 +29,6 @@ using System.Text.RegularExpressions; using System.Threading; using ArchiSteamFarm.Helpers; using ArchiSteamFarm.Localization; -using JetBrains.Annotations; namespace ArchiSteamFarm { internal static class OS { @@ -37,13 +36,11 @@ namespace ArchiSteamFarm { internal static readonly string ProcessFileName = Process.GetCurrentProcess().MainModule?.FileName ?? throw new ArgumentNullException(nameof(ProcessFileName)); internal static bool IsUnix => RuntimeInformation.IsOSPlatform(OSPlatform.Linux) || RuntimeInformation.IsOSPlatform(OSPlatform.OSX); - - [NotNull] internal static string Variant => RuntimeInformation.OSDescription.Trim(); private static bool IsWindows => RuntimeInformation.IsOSPlatform(OSPlatform.Windows); - private static Mutex SingleInstance; + private static Mutex? SingleInstance; internal static void CoreInit() { if (IsWindows && !Console.IsOutputRedirected) { @@ -64,9 +61,7 @@ namespace ArchiSteamFarm { internal static ICrossProcessSemaphore CreateCrossProcessSemaphore(string objectName) { if (string.IsNullOrEmpty(objectName)) { - ASF.ArchiLogger.LogNullError(nameof(objectName)); - - return null; + throw new ArgumentNullException(nameof(objectName)); } string resourceName = GetOsResourceName(objectName); @@ -76,9 +71,7 @@ namespace ArchiSteamFarm { internal static void Init(bool systemRequired, GlobalConfig.EOptimizationMode optimizationMode) { if (!Enum.IsDefined(typeof(GlobalConfig.EOptimizationMode), optimizationMode)) { - ASF.ArchiLogger.LogNullError(nameof(optimizationMode)); - - return; + throw new ArgumentNullException(nameof(optimizationMode)); } if (IsWindows) { @@ -97,9 +90,7 @@ namespace ArchiSteamFarm { break; default: - ASF.ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(optimizationMode), optimizationMode)); - - return; + throw new ArgumentOutOfRangeException(nameof(optimizationMode)); } } @@ -132,9 +123,7 @@ namespace ArchiSteamFarm { internal static void UnixSetFileAccess(string path, EUnixPermission permission) { if (string.IsNullOrEmpty(path)) { - ASF.ArchiLogger.LogNullError(nameof(path)); - - return; + throw new ArgumentNullException(nameof(path)); } if (!IsUnix) { @@ -166,9 +155,7 @@ namespace ArchiSteamFarm { private static string GetOsResourceName(string objectName) { if (string.IsNullOrEmpty(objectName)) { - ASF.ArchiLogger.LogNullError(nameof(objectName)); - - return null; + throw new ArgumentNullException(nameof(objectName)); } return SharedInfo.AssemblyName + "-" + objectName; diff --git a/ArchiSteamFarm/Plugins/IASF.cs b/ArchiSteamFarm/Plugins/IASF.cs index 76b11adf8..2e2972afb 100644 --- a/ArchiSteamFarm/Plugins/IASF.cs +++ b/ArchiSteamFarm/Plugins/IASF.cs @@ -31,6 +31,6 @@ namespace ArchiSteamFarm.Plugins { /// ASF will call this method right after global config initialization. /// /// Extra config properties made out of . Can be null if no extra properties are found. - void OnASFInit([CanBeNull] IReadOnlyDictionary additionalConfigProperties = null); + void OnASFInit(IReadOnlyDictionary? additionalConfigProperties = null); } } diff --git a/ArchiSteamFarm/Plugins/IBot.cs b/ArchiSteamFarm/Plugins/IBot.cs index 2a4f2272c..d74f01342 100644 --- a/ArchiSteamFarm/Plugins/IBot.cs +++ b/ArchiSteamFarm/Plugins/IBot.cs @@ -30,13 +30,13 @@ namespace ArchiSteamFarm.Plugins { /// Doing so will allow the garbage collector to dispose the bot afterwards, refraining from doing so will create a "memory leak" by keeping the reference alive. /// /// Bot object related to this callback. - void OnBotDestroy([NotNull] Bot bot); + void OnBotDestroy(Bot bot); /// /// ASF will call this method after creating the bot object, e.g. after config creation. /// Bot config is not yet available at this stage. This function will execute only once for every bot object. /// /// Bot object related to this callback. - void OnBotInit([NotNull] Bot bot); + void OnBotInit(Bot bot); } } diff --git a/ArchiSteamFarm/Plugins/IBotCardsFarmerInfo.cs b/ArchiSteamFarm/Plugins/IBotCardsFarmerInfo.cs index 8e2b08bf0..a217a3235 100644 --- a/ArchiSteamFarm/Plugins/IBotCardsFarmerInfo.cs +++ b/ArchiSteamFarm/Plugins/IBotCardsFarmerInfo.cs @@ -29,18 +29,18 @@ namespace ArchiSteamFarm.Plugins { /// /// Bot object related to this callback. /// Bool value indicating whether the module has finished successfully, so when there was at least one card to drop, and nothing has interrupted us in the meantime. - void OnBotFarmingFinished([NotNull] Bot bot, bool farmedSomething); + void OnBotFarmingFinished(Bot bot, bool farmedSomething); /// /// ASF will call this method when cards farming module is started on given bot instance. The module is started only when there are valid cards to drop, so this method won't be called when there is nothing to idle. /// /// Bot object related to this callback. - void OnBotFarmingStarted([NotNull] Bot bot); + void OnBotFarmingStarted(Bot bot); /// /// ASF will call this method when cards farming module is stopped on given bot instance. The stop could be a result of a natural finish, or other situations (e.g. Steam networking issues, user commands). /// /// Bot object related to this callback. - void OnBotFarmingStopped([NotNull] Bot bot); + void OnBotFarmingStopped(Bot bot); } } diff --git a/ArchiSteamFarm/Plugins/IBotCommand.cs b/ArchiSteamFarm/Plugins/IBotCommand.cs index bd041f445..89e808f13 100644 --- a/ArchiSteamFarm/Plugins/IBotCommand.cs +++ b/ArchiSteamFarm/Plugins/IBotCommand.cs @@ -33,8 +33,6 @@ namespace ArchiSteamFarm.Plugins { /// Command message in its raw format, stripped of . /// Pre-parsed message using standard ASF delimiters. /// Response to the command, or null/empty (as the task value) if the command isn't handled by this plugin. - [ItemCanBeNull] - [NotNull] - Task OnBotCommand([NotNull] Bot bot, ulong steamID, [NotNull] string message, [ItemNotNull] [NotNull] string[] args); + Task OnBotCommand(Bot bot, ulong steamID, string message, string[] args); } } diff --git a/ArchiSteamFarm/Plugins/IBotConnection.cs b/ArchiSteamFarm/Plugins/IBotConnection.cs index de38bad68..867229142 100644 --- a/ArchiSteamFarm/Plugins/IBotConnection.cs +++ b/ArchiSteamFarm/Plugins/IBotConnection.cs @@ -30,12 +30,12 @@ namespace ArchiSteamFarm.Plugins { /// /// Bot object related to this callback. /// Reason for disconnection, or if the disconnection was initiated by ASF (e.g. as a result of a command). - void OnBotDisconnected([NotNull] Bot bot, EResult reason); + void OnBotDisconnected(Bot bot, EResult reason); /// /// ASF will call this method when bot successfully connects to Steam network. /// /// Bot object related to this callback. - void OnBotLoggedOn([NotNull] Bot bot); + void OnBotLoggedOn(Bot bot); } } diff --git a/ArchiSteamFarm/Plugins/IBotFriendRequest.cs b/ArchiSteamFarm/Plugins/IBotFriendRequest.cs index 09398515d..bb1d5eaf4 100644 --- a/ArchiSteamFarm/Plugins/IBotFriendRequest.cs +++ b/ArchiSteamFarm/Plugins/IBotFriendRequest.cs @@ -31,7 +31,6 @@ namespace ArchiSteamFarm.Plugins { /// Bot object related to this callback. /// 64-bit Steam identificator of the user that sent a friend request, or a group that the bot has been invited to. /// True if the request should be accepted as part of this plugin, false otherwise. - [NotNull] - Task OnBotFriendRequest([NotNull] Bot bot, ulong steamID); + Task OnBotFriendRequest(Bot bot, ulong steamID); } } diff --git a/ArchiSteamFarm/Plugins/IBotMessage.cs b/ArchiSteamFarm/Plugins/IBotMessage.cs index d5e70d843..d0438da7b 100644 --- a/ArchiSteamFarm/Plugins/IBotMessage.cs +++ b/ArchiSteamFarm/Plugins/IBotMessage.cs @@ -32,8 +32,6 @@ namespace ArchiSteamFarm.Plugins { /// 64-bit long unsigned integer of steamID executing the command. /// Message in its raw format. /// Response to the message, or null/empty (as the task value) for silence. - [ItemCanBeNull] - [NotNull] - Task OnBotMessage([NotNull] Bot bot, ulong steamID, [NotNull] string message); + Task OnBotMessage(Bot bot, ulong steamID, string message); } } diff --git a/ArchiSteamFarm/Plugins/IBotModules.cs b/ArchiSteamFarm/Plugins/IBotModules.cs index d2a184e86..4d1c4a79f 100644 --- a/ArchiSteamFarm/Plugins/IBotModules.cs +++ b/ArchiSteamFarm/Plugins/IBotModules.cs @@ -32,6 +32,6 @@ namespace ArchiSteamFarm.Plugins { /// /// Bot object related to this callback. /// Extra config properties made out of . Can be null if no extra properties are found. - void OnBotInitModules([NotNull] Bot bot, [CanBeNull] IReadOnlyDictionary additionalConfigProperties = null); + void OnBotInitModules(Bot bot, IReadOnlyDictionary? additionalConfigProperties = null); } } diff --git a/ArchiSteamFarm/Plugins/IBotSteamClient.cs b/ArchiSteamFarm/Plugins/IBotSteamClient.cs index 4b34d2336..9d7d3cb8f 100644 --- a/ArchiSteamFarm/Plugins/IBotSteamClient.cs +++ b/ArchiSteamFarm/Plugins/IBotSteamClient.cs @@ -31,14 +31,13 @@ namespace ArchiSteamFarm.Plugins { /// /// Bot object related to this callback. /// Callback manager object which can be used for establishing subscriptions to standard and custom callbacks. - void OnBotSteamCallbacksInit([NotNull] Bot bot, [NotNull] CallbackManager callbackManager); + void OnBotSteamCallbacksInit(Bot bot, CallbackManager callbackManager); /// /// ASF will call this method right after bot initialization in order to allow you hooking custom SK2 client handlers into the SteamClient. /// /// Bot object related to this callback. - /// Collection of custom client handlers that are supposed to be hooked into the SteamClient by ASF. If you do not require any, just return null. - [CanBeNull] - IReadOnlyCollection OnBotSteamHandlersInit([NotNull] Bot bot); + /// Collection of custom client handlers that are supposed to be hooked into the SteamClient by ASF. If you do not require any, just return null or empty collection. + IReadOnlyCollection? OnBotSteamHandlersInit(Bot bot); } } diff --git a/ArchiSteamFarm/Plugins/IBotTradeOffer.cs b/ArchiSteamFarm/Plugins/IBotTradeOffer.cs index bee70e7fc..309c32dbf 100644 --- a/ArchiSteamFarm/Plugins/IBotTradeOffer.cs +++ b/ArchiSteamFarm/Plugins/IBotTradeOffer.cs @@ -32,7 +32,6 @@ namespace ArchiSteamFarm.Plugins { /// Bot object related to this callback. /// Trade offer related to this callback. /// True if the trade offer should be accepted as part of this plugin, false otherwise. - [NotNull] - Task OnBotTradeOffer([NotNull] Bot bot, [NotNull] Steam.TradeOffer tradeOffer); + Task OnBotTradeOffer(Bot bot, Steam.TradeOffer tradeOffer); } } diff --git a/ArchiSteamFarm/Plugins/IBotTradeOfferResults.cs b/ArchiSteamFarm/Plugins/IBotTradeOfferResults.cs index 7b8c6b0ed..cc5385647 100644 --- a/ArchiSteamFarm/Plugins/IBotTradeOfferResults.cs +++ b/ArchiSteamFarm/Plugins/IBotTradeOfferResults.cs @@ -30,6 +30,6 @@ namespace ArchiSteamFarm.Plugins { /// /// Bot object related to this callback. /// Trade results related to this callback. - void OnBotTradeOfferResults([NotNull] Bot bot, [NotNull] IReadOnlyCollection tradeResults); + void OnBotTradeOfferResults(Bot bot, IReadOnlyCollection tradeResults); } } diff --git a/ArchiSteamFarm/Plugins/IBotUserNotifications.cs b/ArchiSteamFarm/Plugins/IBotUserNotifications.cs index a7c228fb6..352331f93 100644 --- a/ArchiSteamFarm/Plugins/IBotUserNotifications.cs +++ b/ArchiSteamFarm/Plugins/IBotUserNotifications.cs @@ -30,6 +30,6 @@ namespace ArchiSteamFarm.Plugins { /// /// Bot object related to this callback. /// Collection containing those notification types that are new (that is, when new count > previous count of that notification type). - void OnBotUserNotifications([NotNull] Bot bot, [NotNull] IReadOnlyCollection newNotifications); + void OnBotUserNotifications(Bot bot, IReadOnlyCollection newNotifications); } } diff --git a/ArchiSteamFarm/Plugins/IBotsComparer.cs b/ArchiSteamFarm/Plugins/IBotsComparer.cs index bfaa158bf..90640981b 100644 --- a/ArchiSteamFarm/Plugins/IBotsComparer.cs +++ b/ArchiSteamFarm/Plugins/IBotsComparer.cs @@ -30,7 +30,6 @@ namespace ArchiSteamFarm.Plugins { /// Unless you know what you're doing, you should not implement this property yourself and let ASF decide. /// /// Comparer that will be used for the bots, as well as bot regexes. - [NotNull] StringComparer BotsComparer { get; } } } diff --git a/ArchiSteamFarm/Plugins/IPlugin.cs b/ArchiSteamFarm/Plugins/IPlugin.cs index 2b83f5476..db2f8e966 100644 --- a/ArchiSteamFarm/Plugins/IPlugin.cs +++ b/ArchiSteamFarm/Plugins/IPlugin.cs @@ -31,7 +31,6 @@ namespace ArchiSteamFarm.Plugins { /// /// String that will be used as the name of this plugin. [JsonProperty] - [NotNull] string Name { get; } /// @@ -40,7 +39,6 @@ namespace ArchiSteamFarm.Plugins { /// /// Version that will be shown to the user when plugin is loaded. [JsonProperty] - [NotNull] Version Version { get; } /// diff --git a/ArchiSteamFarm/Plugins/ISteamPICSChanges.cs b/ArchiSteamFarm/Plugins/ISteamPICSChanges.cs index b51557cd0..33d0b6bd1 100644 --- a/ArchiSteamFarm/Plugins/ISteamPICSChanges.cs +++ b/ArchiSteamFarm/Plugins/ISteamPICSChanges.cs @@ -31,7 +31,6 @@ namespace ArchiSteamFarm.Plugins { /// ASF uses this method for determining the point in time from which it should keep history going upon a restart. The actual point in time that will be used is calculated as the lowest change number from all loaded plugins, to guarantee that no plugin will miss any changes, while allowing possible duplicates for those plugins that were already synchronized with newer changes. If you don't care about persistent state and just want to receive the ongoing history, you should return 0 (which is equal to "I'm fine with any"). If there won't be any plugin asking for a specific point in time, ASF will start returning entries since the start of the program. /// /// The most recent change number from which you're fine to receive - [NotNull] Task GetPreferredChangeNumberToStartFrom(); /// @@ -40,7 +39,7 @@ namespace ArchiSteamFarm.Plugins { /// The change number of current callback. /// App changes that happened since the previous call of this method. Can be empty. /// Package changes that happened since the previous call of this method. Can be empty. - void OnPICSChanges(uint currentChangeNumber, [NotNull] IReadOnlyDictionary appChanges, [NotNull] IReadOnlyDictionary packageChanges); + void OnPICSChanges(uint currentChangeNumber, IReadOnlyDictionary appChanges, IReadOnlyDictionary packageChanges); /// /// ASF will call this method when it'll be necessary to restart the history of PICS changes. This can happen due to Steam limitation in which we're unable to keep history going if we're too far behind (approx 5k changeNumbers). If you're relying on continuous history of app/package PICS changes sent by , ASF can no longer guarantee that upon calling this method, therefore you should start clean. diff --git a/ArchiSteamFarm/Plugins/PluginsCore.cs b/ArchiSteamFarm/Plugins/PluginsCore.cs index 1869a7b76..f567c7b4b 100644 --- a/ArchiSteamFarm/Plugins/PluginsCore.cs +++ b/ArchiSteamFarm/Plugins/PluginsCore.cs @@ -31,7 +31,6 @@ using System.Reflection; using System.Threading.Tasks; using ArchiSteamFarm.Json; using ArchiSteamFarm.Localization; -using JetBrains.Annotations; using Newtonsoft.Json.Linq; using SteamKit2; @@ -40,11 +39,10 @@ namespace ArchiSteamFarm.Plugins { internal static bool HasCustomPluginsLoaded => HasActivePluginsLoaded && ActivePlugins.Any(plugin => !(plugin is OfficialPlugin officialPlugin) || !officialPlugin.HasSameVersion()); [ImportMany] - internal static ImmutableHashSet ActivePlugins { get; private set; } + internal static ImmutableHashSet? ActivePlugins { get; private set; } private static bool HasActivePluginsLoaded => ActivePlugins?.Count > 0; - [ItemNotNull] internal static async Task GetBotsComparer() { if (!HasActivePluginsLoaded) { return StringComparer.Ordinal; @@ -60,7 +58,7 @@ namespace ArchiSteamFarm.Plugins { return StringComparer.Ordinal; } - StringComparer result = results.FirstOrDefault(comparer => comparer != null); + StringComparer? result = results.FirstOrDefault(comparer => comparer != null); return result ?? StringComparer.Ordinal; } @@ -94,7 +92,7 @@ namespace ArchiSteamFarm.Plugins { return false; } - HashSet assemblies = LoadAssemblies(); + HashSet? assemblies = LoadAssemblies(); if ((assemblies == null) || (assemblies.Count == 0)) { ASF.ArchiLogger.LogGenericTrace(Strings.NothingFound); @@ -160,13 +158,13 @@ namespace ArchiSteamFarm.Plugins { return invalidPlugins.Count == 0; } - internal static HashSet LoadAssemblies() { - HashSet assemblies = null; + internal static HashSet? LoadAssemblies() { + HashSet? assemblies = null; string pluginsPath = Path.Combine(SharedInfo.HomeDirectory, SharedInfo.PluginsDirectory); if (Directory.Exists(pluginsPath)) { - HashSet loadedAssemblies = LoadAssembliesFrom(pluginsPath); + HashSet? loadedAssemblies = LoadAssembliesFrom(pluginsPath); if ((loadedAssemblies != null) && (loadedAssemblies.Count > 0)) { assemblies = loadedAssemblies; @@ -176,7 +174,7 @@ namespace ArchiSteamFarm.Plugins { string customPluginsPath = Path.Combine(Directory.GetCurrentDirectory(), SharedInfo.PluginsDirectory); if (Directory.Exists(customPluginsPath)) { - HashSet loadedAssemblies = LoadAssembliesFrom(customPluginsPath); + HashSet? loadedAssemblies = LoadAssembliesFrom(customPluginsPath); if ((loadedAssemblies != null) && (loadedAssemblies.Count > 0)) { if ((assemblies != null) && (assemblies.Count > 0)) { @@ -190,7 +188,7 @@ namespace ArchiSteamFarm.Plugins { return assemblies; } - internal static async Task OnASFInitModules(IReadOnlyDictionary additionalConfigProperties = null) { + internal static async Task OnASFInitModules(IReadOnlyDictionary? additionalConfigProperties = null) { if (!HasActivePluginsLoaded) { return; } @@ -202,19 +200,16 @@ namespace ArchiSteamFarm.Plugins { } } - [ItemCanBeNull] - internal static async Task OnBotCommand(Bot bot, ulong steamID, string message, string[] args) { + internal static async Task OnBotCommand(Bot bot, ulong steamID, string message, string[] args) { if ((bot == null) || (steamID == 0) || !new SteamID(steamID).IsIndividualAccount || string.IsNullOrEmpty(message) || (args == null) || (args.Length == 0)) { - ASF.ArchiLogger.LogNullError(nameof(bot) + " || " + nameof(steamID) + " || " + nameof(message) + " || " + nameof(args)); - - return null; + throw new ArgumentNullException(nameof(bot) + " || " + nameof(steamID) + " || " + nameof(message) + " || " + nameof(args)); } if (!HasActivePluginsLoaded) { return null; } - IList responses; + IList responses; try { responses = await Utilities.InParallel(ActivePlugins.OfType().Select(plugin => plugin.OnBotCommand(bot, steamID, message, args))).ConfigureAwait(false); @@ -229,9 +224,7 @@ namespace ArchiSteamFarm.Plugins { internal static async Task OnBotDestroy(Bot bot) { if (bot == null) { - ASF.ArchiLogger.LogNullError(nameof(bot)); - - return; + throw new ArgumentNullException(nameof(bot)); } if (!HasActivePluginsLoaded) { @@ -247,9 +240,7 @@ namespace ArchiSteamFarm.Plugins { internal static async Task OnBotDisconnected(Bot bot, EResult reason) { if (bot == null) { - ASF.ArchiLogger.LogNullError(nameof(bot)); - - return; + throw new ArgumentNullException(nameof(bot)); } if (!HasActivePluginsLoaded) { @@ -265,9 +256,7 @@ namespace ArchiSteamFarm.Plugins { internal static async Task OnBotFarmingFinished(Bot bot, bool farmedSomething) { if (bot == null) { - ASF.ArchiLogger.LogNullError(nameof(bot)); - - return; + throw new ArgumentNullException(nameof(bot)); } if (!HasActivePluginsLoaded) { @@ -283,9 +272,7 @@ namespace ArchiSteamFarm.Plugins { internal static async Task OnBotFarmingStarted(Bot bot) { if (bot == null) { - ASF.ArchiLogger.LogNullError(nameof(bot)); - - return; + throw new ArgumentNullException(nameof(bot)); } if (!HasActivePluginsLoaded) { @@ -301,9 +288,7 @@ namespace ArchiSteamFarm.Plugins { internal static async Task OnBotFarmingStopped(Bot bot) { if (bot == null) { - ASF.ArchiLogger.LogNullError(nameof(bot)); - - return; + throw new ArgumentNullException(nameof(bot)); } if (!HasActivePluginsLoaded) { @@ -319,9 +304,7 @@ namespace ArchiSteamFarm.Plugins { internal static async Task OnBotFriendRequest(Bot bot, ulong steamID) { if ((bot == null) || (steamID == 0) || !new SteamID(steamID).IsValid) { - ASF.ArchiLogger.LogNullError(nameof(bot) + " || " + nameof(steamID)); - - return false; + throw new ArgumentNullException(nameof(bot) + " || " + nameof(steamID)); } if (!HasActivePluginsLoaded) { @@ -343,9 +326,7 @@ namespace ArchiSteamFarm.Plugins { internal static async Task OnBotInit(Bot bot) { if (bot == null) { - ASF.ArchiLogger.LogNullError(nameof(bot)); - - return; + throw new ArgumentNullException(nameof(bot)); } if (!HasActivePluginsLoaded) { @@ -359,11 +340,9 @@ namespace ArchiSteamFarm.Plugins { } } - internal static async Task OnBotInitModules(Bot bot, IReadOnlyDictionary additionalConfigProperties = null) { + internal static async Task OnBotInitModules(Bot bot, IReadOnlyDictionary? additionalConfigProperties = null) { if (bot == null) { - ASF.ArchiLogger.LogNullError(nameof(bot)); - - return; + throw new ArgumentNullException(nameof(bot)); } if (!HasActivePluginsLoaded) { @@ -379,9 +358,7 @@ namespace ArchiSteamFarm.Plugins { internal static async Task OnBotLoggedOn(Bot bot) { if (bot == null) { - ASF.ArchiLogger.LogNullError(nameof(bot)); - - return; + throw new ArgumentNullException(nameof(bot)); } if (!HasActivePluginsLoaded) { @@ -395,19 +372,16 @@ namespace ArchiSteamFarm.Plugins { } } - [ItemCanBeNull] - internal static async Task OnBotMessage(Bot bot, ulong steamID, string message) { + internal static async Task OnBotMessage(Bot bot, ulong steamID, string message) { if ((bot == null) || (steamID == 0) || !new SteamID(steamID).IsIndividualAccount || string.IsNullOrEmpty(message)) { - ASF.ArchiLogger.LogNullError(nameof(bot) + " || " + nameof(steamID) + " || " + nameof(message)); - - return null; + throw new ArgumentNullException(nameof(bot) + " || " + nameof(steamID) + " || " + nameof(message)); } if (!HasActivePluginsLoaded) { return null; } - IList responses; + IList responses; try { responses = await Utilities.InParallel(ActivePlugins.OfType().Select(plugin => plugin.OnBotMessage(bot, steamID, message))).ConfigureAwait(false); @@ -422,9 +396,7 @@ namespace ArchiSteamFarm.Plugins { internal static async Task OnBotSteamCallbacksInit(Bot bot, CallbackManager callbackManager) { if ((bot == null) || (callbackManager == null)) { - ASF.ArchiLogger.LogNullError(nameof(bot) + " || " + nameof(callbackManager)); - - return; + throw new ArgumentNullException(nameof(bot) + " || " + nameof(callbackManager)); } if (!HasActivePluginsLoaded) { @@ -438,18 +410,16 @@ namespace ArchiSteamFarm.Plugins { } } - internal static async Task> OnBotSteamHandlersInit(Bot bot) { + internal static async Task?> OnBotSteamHandlersInit(Bot bot) { if (bot == null) { - ASF.ArchiLogger.LogNullError(nameof(bot)); - - return null; + throw new ArgumentNullException(nameof(bot)); } if (!HasActivePluginsLoaded) { return null; } - IList> responses; + IList?> responses; try { responses = await Utilities.InParallel(ActivePlugins.OfType().Select(plugin => Task.Run(() => plugin.OnBotSteamHandlersInit(bot)))).ConfigureAwait(false); @@ -464,9 +434,7 @@ namespace ArchiSteamFarm.Plugins { internal static async Task OnBotTradeOffer(Bot bot, Steam.TradeOffer tradeOffer) { if ((bot == null) || (tradeOffer == null)) { - ASF.ArchiLogger.LogNullError(nameof(bot) + " || " + nameof(tradeOffer)); - - return false; + throw new ArgumentNullException(nameof(bot) + " || " + nameof(tradeOffer)); } if (!HasActivePluginsLoaded) { @@ -488,9 +456,7 @@ namespace ArchiSteamFarm.Plugins { internal static async Task OnBotTradeOfferResults(Bot bot, IReadOnlyCollection tradeResults) { if ((bot == null) || (tradeResults == null) || (tradeResults.Count == 0)) { - ASF.ArchiLogger.LogNullError(nameof(bot) + " || " + nameof(tradeResults)); - - return; + throw new ArgumentNullException(nameof(bot) + " || " + nameof(tradeResults)); } if (!HasActivePluginsLoaded) { @@ -506,9 +472,7 @@ namespace ArchiSteamFarm.Plugins { internal static async Task OnBotUserNotifications(Bot bot, IReadOnlyCollection newNotifications) { if ((bot == null) || (newNotifications == null) || (newNotifications.Count == 0)) { - ASF.ArchiLogger.LogNullError(nameof(bot) + " || " + nameof(newNotifications)); - - return; + throw new ArgumentNullException(nameof(bot) + " || " + nameof(newNotifications)); } if (!HasActivePluginsLoaded) { @@ -524,9 +488,7 @@ namespace ArchiSteamFarm.Plugins { internal static async Task OnPICSChanges(uint currentChangeNumber, IReadOnlyDictionary appChanges, IReadOnlyDictionary packageChanges) { if ((currentChangeNumber == 0) || (appChanges == null) || (packageChanges == null)) { - ASF.ArchiLogger.LogNullError(nameof(currentChangeNumber) + " || " + nameof(appChanges) + " || " + nameof(packageChanges)); - - return; + throw new ArgumentNullException(nameof(currentChangeNumber) + " || " + nameof(appChanges) + " || " + nameof(packageChanges)); } if (!HasActivePluginsLoaded) { @@ -542,9 +504,7 @@ namespace ArchiSteamFarm.Plugins { internal static async Task OnPICSChangesRestart(uint currentChangeNumber) { if (currentChangeNumber == 0) { - ASF.ArchiLogger.LogNullError(nameof(currentChangeNumber)); - - return; + throw new ArgumentNullException(nameof(currentChangeNumber)); } if (!HasActivePluginsLoaded) { @@ -558,11 +518,9 @@ namespace ArchiSteamFarm.Plugins { } } - private static HashSet LoadAssembliesFrom(string path) { + private static HashSet? LoadAssembliesFrom(string path) { if (string.IsNullOrEmpty(path)) { - ASF.ArchiLogger.LogNullError(nameof(path)); - - return null; + throw new ArgumentNullException(nameof(path)); } if (!Directory.Exists(path)) { diff --git a/ArchiSteamFarm/Program.cs b/ArchiSteamFarm/Program.cs index 678c8f439..fea3d899b 100644 --- a/ArchiSteamFarm/Program.cs +++ b/ArchiSteamFarm/Program.cs @@ -38,12 +38,13 @@ using SteamKit2; namespace ArchiSteamFarm { internal static class Program { - internal static string NetworkGroup { get; private set; } + internal static string? NetworkGroup { get; private set; } internal static bool ProcessRequired { get; private set; } internal static bool RestartAllowed { get; private set; } = true; internal static bool ShutdownSequenceInitialized { get; private set; } private static readonly TaskCompletionSource ShutdownResetEvent = new TaskCompletionSource(); + private static bool SystemRequired; internal static async Task Exit(byte exitCode = 0) { @@ -60,12 +61,10 @@ namespace ArchiSteamFarm { return; } - string executableName = Path.GetFileNameWithoutExtension(OS.ProcessFileName); + string? executableName = Path.GetFileNameWithoutExtension(OS.ProcessFileName); if (string.IsNullOrEmpty(executableName)) { - ASF.ArchiLogger.LogNullError(nameof(executableName)); - - return; + throw new ArgumentNullException(nameof(executableName)); } IEnumerable arguments = Environment.GetCommandLineArgs().Skip(executableName.Equals(SharedInfo.AssemblyName) ? 1 : 0); @@ -85,9 +84,7 @@ namespace ArchiSteamFarm { private static void HandleCryptKeyArgument(string cryptKey) { if (string.IsNullOrEmpty(cryptKey)) { - ASF.ArchiLogger.LogNullError(nameof(cryptKey)); - - return; + throw new ArgumentNullException(nameof(cryptKey)); } ArchiCryptoHelper.SetEncryptionKey(cryptKey); @@ -95,9 +92,7 @@ namespace ArchiSteamFarm { private static void HandleNetworkGroupArgument(string networkGroup) { if (string.IsNullOrEmpty(networkGroup)) { - ASF.ArchiLogger.LogNullError(nameof(networkGroup)); - - return; + throw new ArgumentNullException(nameof(networkGroup)); } NetworkGroup = networkGroup; @@ -105,9 +100,7 @@ namespace ArchiSteamFarm { private static void HandlePathArgument(string path) { if (string.IsNullOrEmpty(path)) { - ASF.ArchiLogger.LogNullError(nameof(path)); - - return; + throw new ArgumentNullException(nameof(path)); } try { @@ -117,7 +110,7 @@ namespace ArchiSteamFarm { } } - private static async Task Init(IReadOnlyCollection args) { + private static async Task Init(IReadOnlyCollection? args) { AppDomain.CurrentDomain.ProcessExit += OnProcessExit; AppDomain.CurrentDomain.UnhandledException += OnUnhandledException; TaskScheduler.UnobservedTaskException += OnUnobservedTaskException; @@ -135,13 +128,19 @@ namespace ArchiSteamFarm { await InitASF(args).ConfigureAwait(false); } - private static async Task InitASF(IReadOnlyCollection args) { + private static async Task InitASF(IReadOnlyCollection? args) { OS.CoreInit(); Console.Title = SharedInfo.ProgramIdentifier; ASF.ArchiLogger.LogGenericInfo(SharedInfo.ProgramIdentifier); - await InitGlobalConfigAndLanguage().ConfigureAwait(false); + if (!await InitGlobalConfigAndLanguage().ConfigureAwait(false)) { + return; + } + + if (ASF.GlobalConfig == null) { + throw new ArgumentNullException(nameof(ASF.GlobalConfig)); + } // Parse post-init args if (args != null) { @@ -154,7 +153,7 @@ namespace ArchiSteamFarm { await ASF.Init().ConfigureAwait(false); } - private static async Task InitCore(IReadOnlyCollection args) { + private static async Task InitCore(IReadOnlyCollection? args) { Directory.SetCurrentDirectory(SharedInfo.HomeDirectory); // Allow loading configs from source tree if it's a debug build @@ -192,16 +191,14 @@ namespace ArchiSteamFarm { return true; } - private static async Task InitGlobalConfigAndLanguage() { + private static async Task InitGlobalConfigAndLanguage() { string globalConfigFile = ASF.GetFilePath(ASF.EFileType.Config); if (string.IsNullOrEmpty(globalConfigFile)) { - ASF.ArchiLogger.LogNullError(nameof(globalConfigFile)); - - return; + throw new ArgumentNullException(nameof(globalConfigFile)); } - GlobalConfig globalConfig; + GlobalConfig? globalConfig; if (File.Exists(globalConfigFile)) { globalConfig = await GlobalConfig.Load(globalConfigFile).ConfigureAwait(false); @@ -211,7 +208,7 @@ namespace ArchiSteamFarm { await Task.Delay(5 * 1000).ConfigureAwait(false); await Exit(1).ConfigureAwait(false); - return; + return false; } } else { globalConfig = new GlobalConfig(); @@ -223,10 +220,10 @@ namespace ArchiSteamFarm { ASF.ArchiLogger.LogGenericDebug(globalConfigFile + ": " + JsonConvert.SerializeObject(ASF.GlobalConfig, Formatting.Indented)); } - if (!string.IsNullOrEmpty(ASF.GlobalConfig.CurrentCulture)) { + if (!string.IsNullOrEmpty(ASF.GlobalConfig?.CurrentCulture)) { try { // GetCultureInfo() would be better but we can't use it for specifying neutral cultures such as "en" - CultureInfo culture = CultureInfo.CreateSpecificCulture(ASF.GlobalConfig.CurrentCulture); + CultureInfo culture = CultureInfo.CreateSpecificCulture(ASF.GlobalConfig!.CurrentCulture); CultureInfo.DefaultThreadCurrentCulture = CultureInfo.DefaultThreadCurrentUICulture = culture; } catch (Exception e) { ASF.ArchiLogger.LogGenericWarningException(e); @@ -239,16 +236,16 @@ namespace ArchiSteamFarm { switch (CultureInfo.CurrentUICulture.TwoLetterISOLanguageName) { case "en": case "iv": - return; + return true; } // We can't dispose this resource set, as we can't be sure if it isn't used somewhere else, rely on GC in this case - ResourceSet defaultResourceSet = Strings.ResourceManager.GetResourceSet(CultureInfo.GetCultureInfo("en-US"), true, true); + ResourceSet? defaultResourceSet = Strings.ResourceManager.GetResourceSet(CultureInfo.GetCultureInfo("en-US"), true, true); if (defaultResourceSet == null) { ASF.ArchiLogger.LogNullError(nameof(defaultResourceSet)); - return; + return true; } HashSet defaultStringObjects = defaultResourceSet.Cast().ToHashSet(); @@ -256,16 +253,16 @@ namespace ArchiSteamFarm { if (defaultStringObjects.Count == 0) { ASF.ArchiLogger.LogNullError(nameof(defaultStringObjects)); - return; + return true; } // We can't dispose this resource set, as we can't be sure if it isn't used somewhere else, rely on GC in this case - ResourceSet currentResourceSet = Strings.ResourceManager.GetResourceSet(CultureInfo.CurrentUICulture, true, true); + ResourceSet? currentResourceSet = Strings.ResourceManager.GetResourceSet(CultureInfo.CurrentUICulture, true, true); if (currentResourceSet == null) { ASF.ArchiLogger.LogNullError(nameof(currentResourceSet)); - return; + return true; } HashSet currentStringObjects = currentResourceSet.Cast().ToHashSet(); @@ -286,15 +283,15 @@ namespace ArchiSteamFarm { float translationCompleteness = currentStringObjects.Count / (float) defaultStringObjects.Count; ASF.ArchiLogger.LogGenericInfo(string.Format(Strings.TranslationIncomplete, CultureInfo.CurrentUICulture.Name, translationCompleteness.ToString("P1"))); } + + return true; } private static async Task InitGlobalDatabaseAndServices() { string globalDatabaseFile = ASF.GetFilePath(ASF.EFileType.Database); if (string.IsNullOrEmpty(globalDatabaseFile)) { - ASF.ArchiLogger.LogNullError(nameof(globalDatabaseFile)); - - return; + throw new ArgumentNullException(nameof(globalDatabaseFile)); } if (!File.Exists(globalDatabaseFile)) { @@ -304,7 +301,7 @@ namespace ArchiSteamFarm { await Task.Delay(5 * 1000).ConfigureAwait(false); } - GlobalDatabase globalDatabase = await GlobalDatabase.CreateOrLoad(globalDatabaseFile).ConfigureAwait(false); + GlobalDatabase? globalDatabase = await GlobalDatabase.CreateOrLoad(globalDatabaseFile).ConfigureAwait(false); if (globalDatabase == null) { ASF.ArchiLogger.LogGenericError(string.Format(Strings.ErrorDatabaseInvalid, globalDatabaseFile)); @@ -377,7 +374,7 @@ namespace ArchiSteamFarm { return true; } - private static async Task Main(string[] args) { + private static async Task Main(string[]? args) { // Initialize await Init(args).ConfigureAwait(false); @@ -385,24 +382,20 @@ namespace ArchiSteamFarm { return await ShutdownResetEvent.Task.ConfigureAwait(false); } - private static async void OnProcessExit(object sender, EventArgs e) => await Shutdown().ConfigureAwait(false); + private static async void OnProcessExit(object? sender, EventArgs e) => await Shutdown().ConfigureAwait(false); - private static async void OnUnhandledException(object sender, UnhandledExceptionEventArgs e) { + private static async void OnUnhandledException(object? sender, UnhandledExceptionEventArgs e) { if (e?.ExceptionObject == null) { - ASF.ArchiLogger.LogNullError(nameof(e) + " || " + nameof(e.ExceptionObject)); - - return; + throw new ArgumentNullException(nameof(e) + " || " + nameof(e.ExceptionObject)); } await ASF.ArchiLogger.LogFatalException((Exception) e.ExceptionObject).ConfigureAwait(false); await Exit(1).ConfigureAwait(false); } - private static async void OnUnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e) { + private static async void OnUnobservedTaskException(object? sender, UnobservedTaskExceptionEventArgs e) { if (e?.Exception == null) { - ASF.ArchiLogger.LogNullError(nameof(e) + " || " + nameof(e.Exception)); - - return; + throw new ArgumentNullException(nameof(e) + " || " + nameof(e.Exception)); } await ASF.ArchiLogger.LogFatalException(e.Exception).ConfigureAwait(false); @@ -414,13 +407,11 @@ namespace ArchiSteamFarm { private static void ParsePostInitArgs(IReadOnlyCollection args) { if (args == null) { - ASF.ArchiLogger.LogNullError(nameof(args)); - - return; + throw new ArgumentNullException(nameof(args)); } try { - string envCryptKey = Environment.GetEnvironmentVariable(SharedInfo.EnvironmentVariableCryptKey); + string? envCryptKey = Environment.GetEnvironmentVariable(SharedInfo.EnvironmentVariableCryptKey); if (!string.IsNullOrEmpty(envCryptKey)) { HandleCryptKeyArgument(envCryptKey); @@ -464,19 +455,17 @@ namespace ArchiSteamFarm { private static void ParsePreInitArgs(IReadOnlyCollection args) { if (args == null) { - ASF.ArchiLogger.LogNullError(nameof(args)); - - return; + throw new ArgumentNullException(nameof(args)); } try { - string envNetworkGroup = Environment.GetEnvironmentVariable(SharedInfo.EnvironmentVariableNetworkGroup); + string? envNetworkGroup = Environment.GetEnvironmentVariable(SharedInfo.EnvironmentVariableNetworkGroup); if (!string.IsNullOrEmpty(envNetworkGroup)) { HandleNetworkGroupArgument(envNetworkGroup); } - string envPath = Environment.GetEnvironmentVariable(SharedInfo.EnvironmentVariablePath); + string? envPath = Environment.GetEnvironmentVariable(SharedInfo.EnvironmentVariablePath); if (!string.IsNullOrEmpty(envPath)) { HandlePathArgument(envPath); diff --git a/ArchiSteamFarm/RuntimeCompatibility.cs b/ArchiSteamFarm/RuntimeCompatibility.cs index 147ca8ccc..f8b7209f6 100644 --- a/ArchiSteamFarm/RuntimeCompatibility.cs +++ b/ArchiSteamFarm/RuntimeCompatibility.cs @@ -29,6 +29,7 @@ using Microsoft.AspNetCore.Hosting; using System.Collections.Generic; using System.Net.WebSockets; using System.Threading; + #endif namespace ArchiSteamFarm { @@ -38,7 +39,11 @@ namespace ArchiSteamFarm { private static readonly DateTime SavedProcessStartTime = DateTime.UtcNow; #endif +#if NETFRAMEWORK public static bool IsRunningOnMono => Type.GetType("Mono.Runtime") != null; +#else + public static bool IsRunningOnMono => false; +#endif public static DateTime ProcessStartTime { get { @@ -57,7 +62,7 @@ namespace ArchiSteamFarm { #pragma warning disable 1998 [PublicAPI] public static class File { - public static async Task AppendAllTextAsync([NotNull] string path, string contents) => + public static async Task AppendAllTextAsync(string path, string contents) => #if NETFRAMEWORK System.IO.File.AppendAllText(path, contents); #else @@ -65,7 +70,7 @@ namespace ArchiSteamFarm { #endif #pragma warning disable IDE0022 - public static void Move([NotNull] string sourceFileName, [NotNull] string destFileName, bool overwrite) { + public static void Move(string sourceFileName, string destFileName, bool overwrite) { #if NETFRAMEWORK if (overwrite && System.IO.File.Exists(destFileName)) { System.IO.File.Delete(destFileName); @@ -78,23 +83,21 @@ namespace ArchiSteamFarm { } #pragma warning restore IDE0022 - [ItemNotNull] - public static async Task ReadAllBytesAsync([NotNull] string path) => + public static async Task ReadAllBytesAsync(string path) => #if NETFRAMEWORK System.IO.File.ReadAllBytes(path); #else await System.IO.File.ReadAllBytesAsync(path).ConfigureAwait(false); #endif - [ItemNotNull] - public static async Task ReadAllTextAsync([NotNull] string path) => + public static async Task ReadAllTextAsync(string path) => #if NETFRAMEWORK System.IO.File.ReadAllText(path); #else await System.IO.File.ReadAllTextAsync(path).ConfigureAwait(false); #endif - public static async Task WriteAllTextAsync([NotNull] string path, string contents) => + public static async Task WriteAllTextAsync(string path, string contents) => #if NETFRAMEWORK System.IO.File.WriteAllText(path, contents); #else @@ -115,8 +118,7 @@ namespace ArchiSteamFarm { [PublicAPI] public static class Path { - [NotNull] - public static string GetRelativePath([NotNull] string relativeTo, [NotNull] string path) { + public static string GetRelativePath(string relativeTo, string path) { #if NETFRAMEWORK if (!path.StartsWith(relativeTo, StringComparison.Ordinal)) { throw new NotImplementedException(); @@ -134,8 +136,7 @@ namespace ArchiSteamFarm { } #if NETFRAMEWORK - [NotNull] - internal static IWebHostBuilder ConfigureWebHostDefaults([NotNull] this IWebHostBuilder builder, [NotNull] Action configure) { + internal static IWebHostBuilder ConfigureWebHostDefaults(this IWebHostBuilder builder, Action configure) { configure(builder); return builder; @@ -147,17 +148,16 @@ namespace ArchiSteamFarm { value = kv.Value; } - public static ValueTask DisposeAsync([NotNull] this IDisposable disposable) { + public static ValueTask DisposeAsync(this IDisposable disposable) { disposable.Dispose(); return default; } - public static async Task ReceiveAsync([NotNull] this WebSocket webSocket, [NotNull] byte[] buffer, CancellationToken cancellationToken) => await webSocket.ReceiveAsync(new ArraySegment(buffer), cancellationToken).ConfigureAwait(false); - public static async Task SendAsync([NotNull] this WebSocket webSocket, [NotNull] byte[] buffer, WebSocketMessageType messageType, bool endOfMessage, CancellationToken cancellationToken) => await webSocket.SendAsync(new ArraySegment(buffer), messageType, endOfMessage, cancellationToken).ConfigureAwait(false); + public static async Task ReceiveAsync(this WebSocket webSocket, byte[] buffer, CancellationToken cancellationToken) => await webSocket.ReceiveAsync(new ArraySegment(buffer), cancellationToken).ConfigureAwait(false); + public static async Task SendAsync(this WebSocket webSocket, byte[] buffer, WebSocketMessageType messageType, bool endOfMessage, CancellationToken cancellationToken) => await webSocket.SendAsync(new ArraySegment(buffer), messageType, endOfMessage, cancellationToken).ConfigureAwait(false); - [NotNull] - public static string[] Split([NotNull] this string text, char separator, StringSplitOptions options = StringSplitOptions.None) => text.Split(new[] { separator }, options); + public static string[] Split(this string text, char separator, StringSplitOptions options = StringSplitOptions.None) => text.Split(new[] { separator }, options); public static void TrimExcess(this Dictionary _) { } // no-op #endif diff --git a/ArchiSteamFarm/SharedInfo.cs b/ArchiSteamFarm/SharedInfo.cs index 12285f57f..cb9ab6432 100644 --- a/ArchiSteamFarm/SharedInfo.cs +++ b/ArchiSteamFarm/SharedInfo.cs @@ -67,7 +67,7 @@ namespace ArchiSteamFarm { internal static string HomeDirectory { get { if (!string.IsNullOrEmpty(CachedHomeDirectory)) { - return CachedHomeDirectory; + return CachedHomeDirectory!; } // We're aiming to handle two possible cases here, classic publish and single-file publish which is possible with OS-specific builds @@ -75,70 +75,47 @@ namespace ArchiSteamFarm { // We can't just return our base directory since it could lead to the (wrong) temporary directory of extracted files in a single-publish scenario // If the path goes to our own binary, the user is using OS-specific build, single-file or not, we'll use path to location of that binary then // Otherwise, this path goes to some third-party binary, likely dotnet/mono, the user is using our generic build or other custom binary, we need to trust our base directory then - CachedHomeDirectory = Path.GetFileNameWithoutExtension(OS.ProcessFileName) == AssemblyName ? Path.GetDirectoryName(OS.ProcessFileName) : AppContext.BaseDirectory; + CachedHomeDirectory = Path.GetFileNameWithoutExtension(OS.ProcessFileName) == AssemblyName ? Path.GetDirectoryName(OS.ProcessFileName) ?? AppContext.BaseDirectory : AppContext.BaseDirectory; return CachedHomeDirectory; } } - [NotNull] internal static string ProgramIdentifier => PublicIdentifier + " V" + Version + " (" + BuildInfo.Variant + "/" + ModuleVersion + " | " + OS.Variant + ")"; - - [NotNull] internal static string PublicIdentifier => AssemblyName + (BuildInfo.IsCustomBuild ? "-custom" : PluginsCore.HasCustomPluginsLoaded ? "-modded" : ""); - - [NotNull] internal static Version Version => Assembly.GetEntryAssembly()?.GetName().Version ?? throw new ArgumentNullException(nameof(Version)); private static Guid ModuleVersion => Assembly.GetEntryAssembly()?.ManifestModule.ModuleVersionId ?? throw new ArgumentNullException(nameof(ModuleVersion)); - private static string CachedHomeDirectory; + private static string? CachedHomeDirectory; internal static class BuildInfo { #if ASF_VARIANT_DOCKER internal static bool CanUpdate => false; - - [NotNull] internal static string Variant => "docker"; #elif ASF_VARIANT_GENERIC internal static bool CanUpdate => true; - - [NotNull] internal static string Variant => "generic"; #elif ASF_VARIANT_GENERIC_NETF internal static bool CanUpdate => true; - - [NotNull] internal static string Variant => "generic-netf"; #elif ASF_VARIANT_LINUX_ARM internal static bool CanUpdate => true; - - [NotNull] internal static string Variant => "linux-arm"; #elif ASF_VARIANT_LINUX_ARM64 internal static bool CanUpdate => true; - - [NotNull] internal static string Variant => "linux-arm64"; #elif ASF_VARIANT_LINUX_X64 internal static bool CanUpdate => true; - - [NotNull] internal static string Variant => "linux-x64"; #elif ASF_VARIANT_OSX_X64 internal static bool CanUpdate => true; - - [NotNull] internal static string Variant => "osx-x64"; #elif ASF_VARIANT_WIN_X64 internal static bool CanUpdate => true; - - [NotNull] internal static string Variant => "win-x64"; #else internal static bool CanUpdate => false; - - [NotNull] internal static string Variant => SourceVariant; #endif diff --git a/ArchiSteamFarm/Statistics.cs b/ArchiSteamFarm/Statistics.cs index b8f6f45c2..a9784abc5 100644 --- a/ArchiSteamFarm/Statistics.cs +++ b/ArchiSteamFarm/Statistics.cs @@ -29,7 +29,6 @@ using System.Threading; using System.Threading.Tasks; using ArchiSteamFarm.Json; using ArchiSteamFarm.Localization; -using JetBrains.Annotations; using Newtonsoft.Json; namespace ArchiSteamFarm { @@ -60,13 +59,13 @@ namespace ArchiSteamFarm { private DateTime LastPersonaStateRequest; private bool ShouldSendHeartBeats; - internal Statistics([JetBrains.Annotations.NotNull] Bot bot) { + internal Statistics(Bot bot) { Bot = bot ?? throw new ArgumentNullException(nameof(bot)); MatchActivelyTimer = new Timer( async e => await MatchActively().ConfigureAwait(false), null, - TimeSpan.FromHours(1) + TimeSpan.FromSeconds(ASF.LoadBalancingDelay * Bot.Bots.Count), // Delay + TimeSpan.FromHours(1) + TimeSpan.FromSeconds(ASF.LoadBalancingDelay * Bot.Bots?.Count ?? 0), // Delay TimeSpan.FromHours(8) // Period ); } @@ -99,11 +98,11 @@ namespace ArchiSteamFarm { const string request = URL + "/Api/HeartBeat"; Dictionary data = new Dictionary(2, StringComparer.Ordinal) { - { "Guid", ASF.GlobalDatabase.Guid.ToString("N") }, + { "Guid", (ASF.GlobalDatabase?.Guid ?? Guid.NewGuid()).ToString("N") }, { "SteamID", Bot.SteamID.ToString() } }; - WebBrowser.BasicResponse response = await Bot.ArchiWebHandler.WebBrowser.UrlPost(request, data, requestOptions: WebBrowser.ERequestOptions.ReturnClientErrors).ConfigureAwait(false); + WebBrowser.BasicResponse? response = await Bot.ArchiWebHandler.WebBrowser.UrlPost(request, data, requestOptions: WebBrowser.ERequestOptions.ReturnClientErrors).ConfigureAwait(false); if (response == null) { return; @@ -128,7 +127,7 @@ namespace ArchiSteamFarm { } } - internal async Task OnPersonaState(string nickname = null, string avatarHash = null) { + internal async Task OnPersonaState(string? nickname = null, string? avatarHash = null) { if ((DateTime.UtcNow < LastAnnouncementCheck.AddHours(MinAnnouncementCheckTTL)) && (ShouldSendHeartBeats || (LastHeartBeat == DateTime.MinValue))) { return; } @@ -157,7 +156,7 @@ namespace ArchiSteamFarm { return; } - string tradeToken = await Bot.ArchiHandler.GetTradeToken().ConfigureAwait(false); + string? tradeToken = await Bot.ArchiHandler.GetTradeToken().ConfigureAwait(false); if (string.IsNullOrEmpty(tradeToken)) { // This is actually network failure, so we'll stop sending heartbeats but not record it as valid check @@ -210,16 +209,16 @@ namespace ArchiSteamFarm { Dictionary data = new Dictionary(9, StringComparer.Ordinal) { { "AvatarHash", avatarHash ?? "" }, { "GamesCount", inventory.Select(item => item.RealAppID).Distinct().Count().ToString() }, - { "Guid", ASF.GlobalDatabase.Guid.ToString("N") }, + { "Guid", (ASF.GlobalDatabase?.Guid ?? Guid.NewGuid()).ToString("N") }, { "ItemsCount", inventory.Count.ToString() }, { "MatchableTypes", JsonConvert.SerializeObject(acceptedMatchableTypes) }, { "MatchEverything", Bot.BotConfig.TradingPreferences.HasFlag(BotConfig.ETradingPreferences.MatchEverything) ? "1" : "0" }, { "Nickname", nickname ?? "" }, { "SteamID", Bot.SteamID.ToString() }, - { "TradeToken", tradeToken } + { "TradeToken", tradeToken! } }; - WebBrowser.BasicResponse response = await Bot.ArchiWebHandler.WebBrowser.UrlPost(request, data, requestOptions: WebBrowser.ERequestOptions.ReturnClientErrors).ConfigureAwait(false); + WebBrowser.BasicResponse? response = await Bot.ArchiWebHandler.WebBrowser.UrlPost(request, data, requestOptions: WebBrowser.ERequestOptions.ReturnClientErrors).ConfigureAwait(false); if (response == null) { return; @@ -239,11 +238,10 @@ namespace ArchiSteamFarm { } } - [ItemCanBeNull] - private async Task> GetListedUsers() { + private async Task?> GetListedUsers() { const string request = URL + "/Api/Bots"; - WebBrowser.ObjectResponse> objectResponse = await Bot.ArchiWebHandler.WebBrowser.UrlGetToJsonObject>(request).ConfigureAwait(false); + WebBrowser.ObjectResponse>? objectResponse = await Bot.ArchiWebHandler.WebBrowser.UrlGetToJsonObject>(request).ConfigureAwait(false); return objectResponse?.Content; } @@ -325,7 +323,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 Dictionary? GivenAssetIDs, ISet? ReceivedAssetIDs)>(); bool shouldContinueMatching = true; bool tradedSomething = false; @@ -355,7 +353,7 @@ namespace ArchiSteamFarm { } } - private async Task<(bool ShouldContinueMatching, bool TradedSomething)> MatchActivelyRound(IReadOnlyCollection acceptedMatchableTypes, IDictionary GivenAssetIDs, ISet ReceivedAssetIDs)> triedSteamIDs) { + private async Task<(bool ShouldContinueMatching, bool TradedSomething)> MatchActivelyRound(IReadOnlyCollection acceptedMatchableTypes, IDictionary? GivenAssetIDs, ISet? ReceivedAssetIDs)> triedSteamIDs) { if ((acceptedMatchableTypes == null) || (acceptedMatchableTypes.Count == 0) || (triedSteamIDs == null)) { Bot.ArchiLogger.LogNullError(nameof(acceptedMatchableTypes) + " || " + nameof(triedSteamIDs)); @@ -391,7 +389,7 @@ namespace ArchiSteamFarm { return (false, false); } - ImmutableHashSet listedUsers = await GetListedUsers().ConfigureAwait(false); + ImmutableHashSet? listedUsers = await GetListedUsers().ConfigureAwait(false); if ((listedUsers == null) || (listedUsers.Count == 0)) { Bot.ArchiLogger.LogGenericTrace(string.Format(Strings.ErrorIsEmpty, nameof(listedUsers))); @@ -399,11 +397,12 @@ namespace ArchiSteamFarm { return (false, false); } + 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)>(); - 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)) { + 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(); if (wantedSets.Count == 0) { @@ -425,8 +424,8 @@ namespace ArchiSteamFarm { } if (holdDuration.Value > 0) { - if (holdDuration.Value > ASF.GlobalConfig.MaxTradeHoldDuration) { - Bot.ArchiLogger.LogGenericTrace(holdDuration.Value + " > " + ASF.GlobalConfig.MaxTradeHoldDuration); + if (holdDuration.Value > maxTradeHoldDuration) { + Bot.ArchiLogger.LogGenericTrace(holdDuration.Value + " > " + maxTradeHoldDuration); continue; } @@ -467,11 +466,11 @@ namespace ArchiSteamFarm { Dictionary fairClassIDsToReceive = new Dictionary(); 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)) { + if (!ourTradableState.TryGetValue(set, out Dictionary? ourTradableItems) || (ourTradableItems.Count == 0)) { continue; } - if (!theirTradableState.TryGetValue(set, out Dictionary theirTradableItems) || (theirTradableItems.Count == 0)) { + if (!theirTradableState.TryGetValue(set, out Dictionary? theirTradableItems) || (theirTradableItems.Count == 0)) { continue; } @@ -480,7 +479,7 @@ namespace ArchiSteamFarm { Dictionary ourTradableSet = new Dictionary(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)) { + if (inventoryStateChanges.TryGetValue(set, out Dictionary? pastChanges) && (pastChanges.Count > 0)) { foreach ((ulong classID, uint amount) in pastChanges) { if (!ourFullSet.TryGetValue(classID, out uint fullAmount) || (fullAmount == 0) || (fullAmount < amount)) { Bot.ArchiLogger.LogNullError(nameof(fullAmount)); @@ -566,7 +565,7 @@ namespace ArchiSteamFarm { classIDsToGive[ourItem] = classIDsToGive.TryGetValue(ourItem, out uint ourGivenAmount) ? ourGivenAmount + 1 : 1; ourFullSet[ourItem] = ourFullAmount - 1; // We don't need to remove anything here because we can guarantee that ourItem.Value is at least 2 - if (inventoryStateChanges.TryGetValue(set, out Dictionary currentChanges)) { + if (inventoryStateChanges.TryGetValue(set, out Dictionary? currentChanges)) { currentChanges[ourItem] = currentChanges.TryGetValue(ourItem, out uint amount) ? amount + 1 : 1; } else { inventoryStateChanges[set] = new Dictionary { @@ -626,8 +625,8 @@ namespace ArchiSteamFarm { return (false, skippedSetsThisRound.Count > 0); } - if (triedSteamIDs.TryGetValue(listedUser.SteamID, out (byte Tries, ISet GivenAssetIDs, ISet ReceivedAssetIDs) previousAttempt)) { - if (itemsToGive.Select(item => item.AssetID).All(previousAttempt.GivenAssetIDs.Contains) && itemsToReceive.Select(item => item.AssetID).All(previousAttempt.ReceivedAssetIDs.Contains)) { + if (triedSteamIDs.TryGetValue(listedUser.SteamID, out (byte Tries, ISet? GivenAssetIDs, ISet? ReceivedAssetIDs) previousAttempt)) { + if ((previousAttempt.GivenAssetIDs == null) || (previousAttempt.ReceivedAssetIDs == null) || (itemsToGive.Select(item => item.AssetID).All(previousAttempt.GivenAssetIDs.Contains) && itemsToReceive.Select(item => item.AssetID).All(previousAttempt.ReceivedAssetIDs.Contains))) { // This user didn't respond in our previous round, avoid him for remaining ones triedSteamIDs[listedUser.SteamID] = (byte.MaxValue, previousAttempt.GivenAssetIDs, previousAttempt.ReceivedAssetIDs); @@ -645,7 +644,7 @@ namespace ArchiSteamFarm { Bot.ArchiLogger.LogGenericTrace(Bot.SteamID + " <- " + string.Join(", ", itemsToReceive.Select(item => item.RealAppID + "/" + item.Type + "-" + item.ClassID + " #" + item.Amount)) + " | " + string.Join(", ", itemsToGive.Select(item => item.RealAppID + "/" + item.Type + "-" + item.ClassID + " #" + item.Amount)) + " -> " + listedUser.SteamID); - (bool success, HashSet mobileTradeOfferIDs) = await Bot.ArchiWebHandler.SendTradeOffer(listedUser.SteamID, itemsToGive, itemsToReceive, listedUser.TradeToken, true).ConfigureAwait(false); + (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) { (bool twoFactorSuccess, _) = await Bot.Actions.HandleTwoFactorAuthenticationConfirmations(true, MobileAuthenticator.Confirmation.EType.Trade, mobileTradeOfferIDs, true).ConfigureAwait(false); @@ -717,7 +716,7 @@ namespace ArchiSteamFarm { #pragma warning disable 649 [JsonProperty(PropertyName = "trade_token", Required = Required.Always)] - internal readonly string TradeToken; + internal readonly string? TradeToken; #pragma warning restore 649 internal float Score => GamesCount / (float) ItemsCount; diff --git a/ArchiSteamFarm/SteamKit2/InMemoryServerListProvider.cs b/ArchiSteamFarm/SteamKit2/InMemoryServerListProvider.cs index 2d7d618ca..31164c50d 100644 --- a/ArchiSteamFarm/SteamKit2/InMemoryServerListProvider.cs +++ b/ArchiSteamFarm/SteamKit2/InMemoryServerListProvider.cs @@ -24,7 +24,6 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using ArchiSteamFarm.Collections; -using JetBrains.Annotations; using Newtonsoft.Json; using SteamKit2.Discovery; @@ -33,10 +32,8 @@ namespace ArchiSteamFarm.SteamKit2 { [JsonProperty(Required = Required.DisallowNull)] private readonly ConcurrentHashSet ServerRecords = new ConcurrentHashSet(); - [NotNull] - public Task> FetchServerListAsync() => Task.FromResult(ServerRecords.Select(server => ServerRecord.CreateServer(server.Host, server.Port, server.ProtocolTypes))); + 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))); - [NotNull] public Task UpdateServerListAsync(IEnumerable endpoints) { if (endpoints == null) { ASF.ArchiLogger.LogNullError(nameof(endpoints)); @@ -55,6 +52,6 @@ namespace ArchiSteamFarm.SteamKit2 { public bool ShouldSerializeServerRecords() => ServerRecords.Count > 0; - internal event EventHandler ServerListUpdated; + internal event EventHandler? ServerListUpdated; } } diff --git a/ArchiSteamFarm/SteamKit2/ServerRecordEndPoint.cs b/ArchiSteamFarm/SteamKit2/ServerRecordEndPoint.cs index 761ec30cd..6733e7f89 100644 --- a/ArchiSteamFarm/SteamKit2/ServerRecordEndPoint.cs +++ b/ArchiSteamFarm/SteamKit2/ServerRecordEndPoint.cs @@ -20,14 +20,13 @@ // limitations under the License. using System; -using JetBrains.Annotations; using Newtonsoft.Json; using SteamKit2; namespace ArchiSteamFarm.SteamKit2 { internal sealed class ServerRecordEndPoint : IEquatable { [JsonProperty(Required = Required.Always)] - internal readonly string Host; + internal readonly string? Host; [JsonProperty(Required = Required.Always)] internal readonly ushort Port; @@ -35,7 +34,7 @@ namespace ArchiSteamFarm.SteamKit2 { [JsonProperty(Required = Required.Always)] internal readonly ProtocolTypes ProtocolTypes; - internal ServerRecordEndPoint([NotNull] string host, ushort port, ProtocolTypes protocolTypes) { + internal ServerRecordEndPoint(string host, ushort port, ProtocolTypes protocolTypes) { if (string.IsNullOrEmpty(host) || (port == 0) || (protocolTypes == 0)) { throw new ArgumentNullException(nameof(host) + " || " + nameof(port) + " || " + nameof(protocolTypes)); } @@ -48,8 +47,8 @@ namespace ArchiSteamFarm.SteamKit2 { [JsonConstructor] private ServerRecordEndPoint() { } - public bool Equals(ServerRecordEndPoint other) => (other != null) && (ReferenceEquals(other, this) || ((Host == other.Host) && (Port == other.Port) && (ProtocolTypes == other.ProtocolTypes))); - public override bool Equals(object obj) => (obj != null) && ((obj == this) || (obj is ServerRecordEndPoint serverRecord && Equals(serverRecord))); + public bool Equals(ServerRecordEndPoint? other) => (other != null) && (ReferenceEquals(other, this) || ((Host == other.Host) && (Port == other.Port) && (ProtocolTypes == other.ProtocolTypes))); + public override bool Equals(object? obj) => (obj != null) && ((obj == this) || (obj is ServerRecordEndPoint serverRecord && Equals(serverRecord))); public override int GetHashCode() => RuntimeCompatibility.HashCode.Combine(Host, Port, ProtocolTypes); } } diff --git a/ArchiSteamFarm/SteamPICSChanges.cs b/ArchiSteamFarm/SteamPICSChanges.cs index f0af21381..d8a80bad8 100644 --- a/ArchiSteamFarm/SteamPICSChanges.cs +++ b/ArchiSteamFarm/SteamPICSChanges.cs @@ -60,11 +60,11 @@ namespace ArchiSteamFarm { } try { - Bot refreshBot = null; - SteamApps.PICSChangesCallback picsChanges = null; + Bot? refreshBot = null; + SteamApps.PICSChangesCallback? picsChanges = null; for (byte i = 0; (i < WebBrowser.MaxTries) && (picsChanges == null); i++) { - refreshBot = Bot.Bots.Values.FirstOrDefault(bot => bot.IsConnectedAndLoggedOn); + refreshBot = Bot.Bots?.Values.FirstOrDefault(bot => bot.IsConnectedAndLoggedOn); if (refreshBot == null) { return; @@ -95,7 +95,7 @@ namespace ArchiSteamFarm { return; } - if (picsChanges.PackageChanges.Count > 0) { + if ((picsChanges.PackageChanges.Count > 0) && (ASF.GlobalDatabase != null)) { await ASF.GlobalDatabase.RefreshPackages(refreshBot, picsChanges.PackageChanges.ToDictionary(package => package.Key, package => package.Value.ChangeNumber)).ConfigureAwait(false); } diff --git a/ArchiSteamFarm/SteamSaleEvent.cs b/ArchiSteamFarm/SteamSaleEvent.cs index 098a00039..575f71eca 100644 --- a/ArchiSteamFarm/SteamSaleEvent.cs +++ b/ArchiSteamFarm/SteamSaleEvent.cs @@ -25,7 +25,6 @@ using System.Threading; using System.Threading.Tasks; using AngleSharp.Dom; using ArchiSteamFarm.Localization; -using JetBrains.Annotations; namespace ArchiSteamFarm { internal sealed class SteamSaleEvent : IAsyncDisposable { @@ -34,13 +33,13 @@ namespace ArchiSteamFarm { private readonly Bot Bot; private readonly Timer SaleEventTimer; - internal SteamSaleEvent([NotNull] Bot bot) { + internal SteamSaleEvent(Bot bot) { Bot = bot ?? throw new ArgumentNullException(nameof(bot)); SaleEventTimer = new Timer( async e => await ExploreDiscoveryQueue().ConfigureAwait(false), null, - TimeSpan.FromHours(1.1) + TimeSpan.FromSeconds(ASF.LoadBalancingDelay * Bot.Bots.Count), // Delay + TimeSpan.FromHours(1.1) + TimeSpan.FromSeconds(ASF.LoadBalancingDelay * Bot.Bots?.Count ?? 0), // Delay TimeSpan.FromHours(8.1) // Period ); } @@ -55,7 +54,7 @@ namespace ArchiSteamFarm { Bot.ArchiLogger.LogGenericTrace(Strings.Starting); for (byte i = 0; (i < MaxSingleQueuesDaily) && (await IsDiscoveryQueueAvailable().ConfigureAwait(false)).GetValueOrDefault(); i++) { - ImmutableHashSet queue = await Bot.ArchiWebHandler.GenerateNewDiscoveryQueue().ConfigureAwait(false); + ImmutableHashSet? queue = await Bot.ArchiWebHandler.GenerateNewDiscoveryQueue().ConfigureAwait(false); if ((queue == null) || (queue.Count == 0)) { Bot.ArchiLogger.LogGenericTrace(string.Format(Strings.ErrorIsEmpty, nameof(queue))); @@ -83,13 +82,13 @@ namespace ArchiSteamFarm { } private async Task IsDiscoveryQueueAvailable() { - using IDocument htmlDocument = await Bot.ArchiWebHandler.GetDiscoveryQueuePage().ConfigureAwait(false); + using IDocument? htmlDocument = await Bot.ArchiWebHandler.GetDiscoveryQueuePage().ConfigureAwait(false); if (htmlDocument == null) { return null; } - IElement htmlNode = htmlDocument.SelectSingleNode("//div[@class='subtext']"); + IElement? htmlNode = htmlDocument.SelectSingleNode("//div[@class='subtext']"); if (htmlNode == null) { // Valid, no cards for exploring the queue available diff --git a/ArchiSteamFarm/Trading.cs b/ArchiSteamFarm/Trading.cs index f976380a1..991550197 100644 --- a/ArchiSteamFarm/Trading.cs +++ b/ArchiSteamFarm/Trading.cs @@ -44,16 +44,14 @@ namespace ArchiSteamFarm { private bool ParsingScheduled; - internal Trading([NotNull] Bot bot) => Bot = bot ?? throw new ArgumentNullException(nameof(bot)); + internal Trading(Bot bot) => Bot = bot ?? throw new ArgumentNullException(nameof(bot)); public void Dispose() => TradesSemaphore.Dispose(); [PublicAPI] public static bool IsFairExchange(IReadOnlyCollection itemsToGive, IReadOnlyCollection itemsToReceive) { if ((itemsToGive == null) || (itemsToGive.Count == 0) || (itemsToReceive == null) || (itemsToReceive.Count == 0)) { - ASF.ArchiLogger.LogNullError(nameof(itemsToGive) + " || " + nameof(itemsToReceive)); - - return false; + throw new ArgumentNullException(nameof(itemsToGive) + " || " + nameof(itemsToReceive)); } 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>(); @@ -83,9 +81,7 @@ 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)) { - ASF.ArchiLogger.LogNullError(nameof(inventory) + " || " + nameof(itemsToGive) + " || " + nameof(itemsToReceive)); - - return false; + throw new ArgumentNullException(nameof(inventory) + " || " + nameof(itemsToGive) + " || " + nameof(itemsToReceive)); } // Input of this function is items we're expected to give/receive and our inventory (limited to realAppIDs of itemsToGive/itemsToReceive) @@ -118,9 +114,7 @@ namespace ArchiSteamFarm { } if (amountToGive > 0) { - ASF.ArchiLogger.LogNullError(nameof(amountToGive)); - - return false; + throw new ArgumentNullException(nameof(amountToGive)); } if (itemsToRemove.Count > 0) { @@ -138,7 +132,7 @@ namespace ArchiSteamFarm { // Once we have both states, we can check overall fairness foreach (((uint RealAppID, Steam.Asset.EType Type, Steam.Asset.ERarity Rarity) set, List beforeAmounts) in initialSets) { - if (!finalSets.TryGetValue(set, out List afterAmounts)) { + if (!finalSets.TryGetValue(set, out List? afterAmounts)) { // If we have no info about this set, then it has to be a bad one return false; } @@ -188,9 +182,7 @@ namespace ArchiSteamFarm { internal static (Dictionary<(uint RealAppID, Steam.Asset.EType Type, Steam.Asset.ERarity Rarity), Dictionary> FullState, Dictionary<(uint RealAppID, Steam.Asset.EType Type, Steam.Asset.ERarity Rarity), Dictionary> TradableState) GetDividedInventoryState(IReadOnlyCollection inventory) { if ((inventory == null) || (inventory.Count == 0)) { - ASF.ArchiLogger.LogNullError(nameof(inventory)); - - return (null, null); + 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>(); @@ -199,7 +191,7 @@ namespace ArchiSteamFarm { foreach (Steam.Asset item in inventory) { (uint RealAppID, Steam.Asset.EType Type, Steam.Asset.ERarity Rarity) key = (item.RealAppID, item.Type, item.Rarity); - if (fullState.TryGetValue(key, out Dictionary fullSet)) { + if (fullState.TryGetValue(key, out Dictionary? fullSet)) { fullSet[item.ClassID] = fullSet.TryGetValue(item.ClassID, out uint amount) ? amount + item.Amount : item.Amount; } else { fullState[key] = new Dictionary { { item.ClassID, item.Amount } }; @@ -209,7 +201,7 @@ namespace ArchiSteamFarm { continue; } - if (tradableState.TryGetValue(key, out Dictionary tradableSet)) { + if (tradableState.TryGetValue(key, out Dictionary? tradableSet)) { tradableSet[item.ClassID] = tradableSet.TryGetValue(item.ClassID, out uint amount) ? amount + item.Amount : item.Amount; } else { tradableState[key] = new Dictionary { { item.ClassID, item.Amount } }; @@ -221,9 +213,7 @@ namespace ArchiSteamFarm { internal static Dictionary<(uint RealAppID, Steam.Asset.EType Type, Steam.Asset.ERarity Rarity), Dictionary> GetTradableInventoryState(IReadOnlyCollection inventory) { if ((inventory == null) || (inventory.Count == 0)) { - ASF.ArchiLogger.LogNullError(nameof(inventory)); - - return null; + 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>(); @@ -231,7 +221,7 @@ namespace ArchiSteamFarm { 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); - if (tradableState.TryGetValue(key, out Dictionary tradableSet)) { + if (tradableState.TryGetValue(key, out Dictionary? tradableSet)) { tradableSet[item.ClassID] = tradableSet.TryGetValue(item.ClassID, out uint amount) ? amount + item.Amount : item.Amount; } else { tradableState[key] = new Dictionary { { item.ClassID, item.Amount } }; @@ -243,9 +233,7 @@ namespace ArchiSteamFarm { internal static HashSet GetTradableItemsFromInventory(ISet inventory, IDictionary classIDs) { if ((inventory == null) || (inventory.Count == 0) || (classIDs == null) || (classIDs.Count == 0)) { - ASF.ArchiLogger.LogNullError(nameof(inventory) + " || " + nameof(classIDs)); - - return null; + throw new ArgumentNullException(nameof(inventory) + " || " + nameof(classIDs)); } HashSet result = new HashSet(); @@ -273,16 +261,12 @@ 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)) { - ASF.ArchiLogger.LogNullError(nameof(fullState) + " || " + nameof(tradableState)); - - return false; + throw new ArgumentNullException(nameof(fullState) + " || " + nameof(tradableState)); } foreach (((uint RealAppID, Steam.Asset.EType Type, Steam.Asset.ERarity Rarity) set, Dictionary state) in tradableState) { - if (!fullState.TryGetValue(set, out Dictionary fullSet) || (fullSet == null) || (fullSet.Count == 0)) { - ASF.ArchiLogger.LogNullError(nameof(fullSet)); - - return false; + if (!fullState.TryGetValue(set, out Dictionary? fullSet) || (fullSet == null) || (fullSet.Count == 0)) { + throw new ArgumentNullException(nameof(fullSet)); } if (!IsEmptyForMatching(fullSet, state)) { @@ -296,24 +280,18 @@ namespace ArchiSteamFarm { internal static bool IsEmptyForMatching(IReadOnlyDictionary fullSet, IReadOnlyDictionary tradableSet) { if ((fullSet == null) || (tradableSet == null)) { - ASF.ArchiLogger.LogNullError(nameof(fullSet) + " || " + nameof(tradableSet)); - - return false; + throw new ArgumentNullException(nameof(fullSet) + " || " + 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 - ASF.ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(amount), amount)); - - return false; + throw new ArgumentOutOfRangeException(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)) { - ASF.ArchiLogger.LogNullError(nameof(fullAmount)); - - return false; + throw new ArgumentNullException(nameof(fullAmount)); } if (fullAmount > 1) { @@ -369,9 +347,7 @@ namespace ArchiSteamFarm { private static Dictionary<(uint RealAppID, Steam.Asset.EType Type, Steam.Asset.ERarity Rarity), List> GetInventorySets(IReadOnlyCollection inventory) { if ((inventory == null) || (inventory.Count == 0)) { - ASF.ArchiLogger.LogNullError(nameof(inventory)); - - return null; + throw new ArgumentNullException(nameof(inventory)); } Dictionary<(uint RealAppID, Steam.Asset.EType Type, Steam.Asset.ERarity Rarity), Dictionary> sets = GetInventoryState(inventory); @@ -381,9 +357,7 @@ namespace ArchiSteamFarm { private static Dictionary<(uint RealAppID, Steam.Asset.EType Type, Steam.Asset.ERarity Rarity), Dictionary> GetInventoryState(IReadOnlyCollection inventory) { if ((inventory == null) || (inventory.Count == 0)) { - ASF.ArchiLogger.LogNullError(nameof(inventory)); - - return null; + 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>(); @@ -391,7 +365,7 @@ namespace ArchiSteamFarm { foreach (Steam.Asset item in inventory) { (uint RealAppID, Steam.Asset.EType Type, Steam.Asset.ERarity Rarity) key = (item.RealAppID, item.Type, item.Rarity); - if (state.TryGetValue(key, out Dictionary set)) { + if (state.TryGetValue(key, out Dictionary? set)) { set[item.ClassID] = set.TryGetValue(item.ClassID, out uint amount) ? amount + item.Amount : item.Amount; } else { state[key] = new Dictionary { { item.ClassID, item.Amount } }; @@ -402,7 +376,7 @@ namespace ArchiSteamFarm { } private async Task ParseActiveTrades() { - HashSet tradeOffers = await Bot.ArchiWebHandler.GetActiveTradeOffers().ConfigureAwait(false); + HashSet? tradeOffers = await Bot.ArchiWebHandler.GetActiveTradeOffers().ConfigureAwait(false); if ((tradeOffers == null) || (tradeOffers.Count == 0)) { return false; @@ -412,11 +386,11 @@ namespace ArchiSteamFarm { HandledTradeOfferIDs.IntersectWith(tradeOffers.Select(tradeOffer => tradeOffer.TradeOfferID)); } - IEnumerable> tasks = tradeOffers.Where(tradeOffer => !HandledTradeOfferIDs.Contains(tradeOffer.TradeOfferID)).Select(ParseTrade); - IList<(ParseTradeResult TradeResult, bool RequiresMobileConfirmation)> results = await Utilities.InParallel(tasks).ConfigureAwait(false); + IEnumerable> tasks = tradeOffers.Where(tradeOffer => !HandledTradeOfferIDs.Contains(tradeOffer.TradeOfferID)).Select(ParseTrade); + 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 != null) && (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); @@ -429,16 +403,18 @@ namespace ArchiSteamFarm { } } - await PluginsCore.OnBotTradeOfferResults(Bot, results.Select(result => result.TradeResult).ToHashSet()).ConfigureAwait(false); + HashSet validTradeResults = results.Where(result => result.TradeResult != null).Select(result => result.TradeResult!).ToHashSet(); + + if (validTradeResults.Count > 0) { + 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)); } - private async Task<(ParseTradeResult TradeResult, bool RequiresMobileConfirmation)> ParseTrade(Steam.TradeOffer tradeOffer) { + private async Task<(ParseTradeResult? TradeResult, bool RequiresMobileConfirmation)> ParseTrade(Steam.TradeOffer tradeOffer) { if (tradeOffer == null) { - Bot.ArchiLogger.LogNullError(nameof(tradeOffer)); - - return (null, false); + throw new ArgumentNullException(nameof(tradeOffer)); } if (tradeOffer.State != ETradeOfferState.Active) { @@ -518,10 +494,12 @@ namespace ArchiSteamFarm { } private async Task ShouldAcceptTrade(Steam.TradeOffer tradeOffer) { - if (tradeOffer == null) { - Bot.ArchiLogger.LogNullError(nameof(tradeOffer)); + if (Bot.Bots == null) { + throw new ArgumentNullException(nameof(Bot.Bots)); + } - return ParseTradeResult.EResult.Unknown; + if ((tradeOffer == null) || (ASF.GlobalConfig == null)) { + throw new ArgumentNullException(nameof(tradeOffer) + " || " + nameof(ASF.GlobalConfig)); } if (tradeOffer.OtherSteamID64 != 0) { @@ -677,9 +655,9 @@ namespace ArchiSteamFarm { [PublicAPI] public readonly ulong TradeOfferID; - internal readonly ImmutableHashSet ReceivedItemTypes; + internal readonly ImmutableHashSet? ReceivedItemTypes; - internal ParseTradeResult(ulong tradeOfferID, EResult result, IReadOnlyCollection itemsToReceive = null) { + internal ParseTradeResult(ulong tradeOfferID, EResult result, IReadOnlyCollection? itemsToReceive = null) { if ((tradeOfferID == 0) || (result == EResult.Unknown)) { throw new ArgumentNullException(nameof(tradeOfferID) + " || " + nameof(result)); } diff --git a/ArchiSteamFarm/Utilities.cs b/ArchiSteamFarm/Utilities.cs index 1cd9ade3d..4b2909a1a 100644 --- a/ArchiSteamFarm/Utilities.cs +++ b/ArchiSteamFarm/Utilities.cs @@ -44,9 +44,7 @@ namespace ArchiSteamFarm { [PublicAPI] public static string GetArgsAsText(string[] args, byte argsToSkip, string delimiter) { if ((args == null) || (args.Length <= argsToSkip) || string.IsNullOrEmpty(delimiter)) { - ASF.ArchiLogger.LogNullError(nameof(args) + " || " + nameof(argsToSkip) + " || " + nameof(delimiter)); - - return null; + throw new ArgumentNullException(nameof(args) + " || " + nameof(argsToSkip) + " || " + nameof(delimiter)); } return string.Join(delimiter, args.Skip(argsToSkip)); @@ -55,36 +53,21 @@ namespace ArchiSteamFarm { [PublicAPI] public static string GetArgsAsText(string text, byte argsToSkip) { if (string.IsNullOrEmpty(text)) { - ASF.ArchiLogger.LogNullError(nameof(text)); - - return null; + throw new ArgumentNullException(nameof(text)); } - string[] args = text.Split((char[]) null, argsToSkip + 1, StringSplitOptions.RemoveEmptyEntries); + string[] args = text.Split(new char[0], argsToSkip + 1, StringSplitOptions.RemoveEmptyEntries); return args[^1]; } - [PublicAPI] - public static string GetAttributeValue(this INode node, string attributeName) { - if ((node == null) || string.IsNullOrEmpty(attributeName)) { - ASF.ArchiLogger.LogNullError(nameof(node) + " || " + nameof(attributeName)); - - return null; - } - - return node is IElement element ? element.GetAttribute(attributeName) : null; - } - [PublicAPI] public static uint GetUnixTime() => (uint) DateTimeOffset.UtcNow.ToUnixTimeSeconds(); [PublicAPI] public static async void InBackground(Action action, bool longRunning = false) { if (action == null) { - ASF.ArchiLogger.LogNullError(nameof(action)); - - return; + throw new ArgumentNullException(nameof(action)); } TaskCreationOptions options = TaskCreationOptions.DenyChildAttach; @@ -99,9 +82,7 @@ namespace ArchiSteamFarm { [PublicAPI] public static async void InBackground(Func function, bool longRunning = false) { if (function == null) { - ASF.ArchiLogger.LogNullError(nameof(function)); - - return; + throw new ArgumentNullException(nameof(function)); } TaskCreationOptions options = TaskCreationOptions.DenyChildAttach; @@ -116,14 +97,12 @@ namespace ArchiSteamFarm { [PublicAPI] public static async Task> InParallel(IEnumerable> tasks) { if (tasks == null) { - ASF.ArchiLogger.LogNullError(nameof(tasks)); - - return null; + throw new ArgumentNullException(nameof(tasks)); } IList results; - switch (ASF.GlobalConfig.OptimizationMode) { + switch (ASF.GlobalConfig?.OptimizationMode) { case GlobalConfig.EOptimizationMode.MinMemoryUsage: results = new List(); @@ -144,12 +123,10 @@ namespace ArchiSteamFarm { [PublicAPI] public static async Task InParallel(IEnumerable tasks) { if (tasks == null) { - ASF.ArchiLogger.LogNullError(nameof(tasks)); - - return; + throw new ArgumentNullException(nameof(tasks)); } - switch (ASF.GlobalConfig.OptimizationMode) { + switch (ASF.GlobalConfig?.OptimizationMode) { case GlobalConfig.EOptimizationMode.MinMemoryUsage: foreach (Task task in tasks) { await task.ConfigureAwait(false); @@ -169,9 +146,7 @@ namespace ArchiSteamFarm { [PublicAPI] public static bool IsValidCdKey(string key) { if (string.IsNullOrEmpty(key)) { - ASF.ArchiLogger.LogNullError(nameof(key)); - - return false; + throw new ArgumentNullException(nameof(key)); } return Regex.IsMatch(key, @"^[0-9A-Z]{4,7}-[0-9A-Z]{4,7}-[0-9A-Z]{4,7}(?:(?:-[0-9A-Z]{4,7})?(?:-[0-9A-Z]{4,7}))?$", RegexOptions.CultureInvariant | RegexOptions.IgnoreCase); @@ -180,9 +155,7 @@ namespace ArchiSteamFarm { [PublicAPI] public static bool IsValidHexadecimalText(string text) { if (string.IsNullOrEmpty(text)) { - ASF.ArchiLogger.LogNullError(nameof(text)); - - return false; + throw new ArgumentNullException(nameof(text)); } return (text.Length % 2 == 0) && text.All(Uri.IsHexDigit); @@ -225,23 +198,17 @@ namespace ArchiSteamFarm { } } - [ItemNotNull] - [NotNull] [PublicAPI] - public static List SelectElementNodes([NotNull] this IElement element, string xpath) => element.SelectNodes(xpath).Cast().ToList(); + public static List SelectElementNodes(this IElement element, string xpath) => element.SelectNodes(xpath).Cast().ToList(); - [ItemNotNull] - [NotNull] [PublicAPI] - public static List SelectNodes([NotNull] this IDocument document, string xpath) => document.Body.SelectNodes(xpath).Cast().ToList(); + public static List SelectNodes(this IDocument document, string xpath) => document.Body.SelectNodes(xpath).Cast().ToList(); - [CanBeNull] [PublicAPI] - public static IElement SelectSingleElementNode([NotNull] this IElement element, string xpath) => (IElement) element.SelectSingleNode(xpath); + public static IElement? SelectSingleElementNode(this IElement element, string xpath) => (IElement?) element.SelectSingleNode(xpath); - [CanBeNull] [PublicAPI] - public static IElement SelectSingleNode([NotNull] this IDocument document, string xpath) => (IElement) document.Body.SelectSingleNode(xpath); + public static IElement? SelectSingleNode(this IDocument document, string xpath) => (IElement?) document.Body.SelectSingleNode(xpath); [PublicAPI] public static IEnumerable ToEnumerable(this T item) { @@ -251,9 +218,8 @@ namespace ArchiSteamFarm { [PublicAPI] public static string ToHumanReadable(this TimeSpan timeSpan) => timeSpan.Humanize(3, maxUnit: TimeUnit.Year, minUnit: TimeUnit.Second); - [NotNull] [PublicAPI] - public static Task ToLongRunningTask([NotNull] this AsyncJob job) where T : CallbackMsg { + public static Task ToLongRunningTask(this AsyncJob job) where T : CallbackMsg { if (job == null) { throw new ArgumentNullException(nameof(job)); } @@ -263,9 +229,8 @@ namespace ArchiSteamFarm { return job.ToTask(); } - [NotNull] [PublicAPI] - public static Task.ResultSet> ToLongRunningTask([NotNull] this AsyncJobMultiple job) where T : CallbackMsg { + public static Task.ResultSet> ToLongRunningTask(this AsyncJobMultiple job) where T : CallbackMsg { if (job == null) { throw new ArgumentNullException(nameof(job)); } @@ -277,9 +242,7 @@ namespace ArchiSteamFarm { internal static void DeleteEmptyDirectoriesRecursively(string directory) { if (string.IsNullOrEmpty(directory)) { - ASF.ArchiLogger.LogNullError(nameof(directory)); - - return; + throw new ArgumentNullException(nameof(directory)); } if (!Directory.Exists(directory)) { @@ -299,33 +262,19 @@ namespace ArchiSteamFarm { } } - internal static string GetCookieValue(this CookieContainer cookieContainer, string url, string name) { + internal static string? GetCookieValue(this CookieContainer cookieContainer, string url, string name) { if ((cookieContainer == null) || string.IsNullOrEmpty(url) || string.IsNullOrEmpty(name)) { - ASF.ArchiLogger.LogNullError(nameof(cookieContainer) + " || " + nameof(url) + " || " + nameof(name)); - - return null; + throw new ArgumentNullException(nameof(cookieContainer) + " || " + nameof(url) + " || " + nameof(name)); } - Uri uri; - - try { - uri = new Uri(url); - } catch (UriFormatException e) { - ASF.ArchiLogger.LogGenericException(e); - - return null; - } - - CookieCollection cookies = cookieContainer.GetCookies(uri); + CookieCollection cookies = cookieContainer.GetCookies(new Uri(url)); return cookies.Count > 0 ? (from Cookie cookie in cookies where cookie.Name.Equals(name) select cookie.Value).FirstOrDefault() : null; } internal static bool RelativeDirectoryStartsWith(string directory, params string[] prefixes) { if (string.IsNullOrEmpty(directory) || (prefixes == null) || (prefixes.Length == 0)) { - ASF.ArchiLogger.LogNullError(nameof(directory) + " || " + nameof(prefixes)); - - return false; + throw new ArgumentNullException(nameof(directory) + " || " + 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 951a07066..6ab13b8c1 100644 --- a/ArchiSteamFarm/WebBrowser.cs +++ b/ArchiSteamFarm/WebBrowser.cs @@ -54,16 +54,16 @@ namespace ArchiSteamFarm { private readonly HttpClient HttpClient; private readonly HttpClientHandler HttpClientHandler; - internal WebBrowser([NotNull] ArchiLogger archiLogger, IWebProxy webProxy = null, bool extendedTimeout = false) { + internal WebBrowser(ArchiLogger archiLogger, IWebProxy? webProxy = null, bool extendedTimeout = false) { ArchiLogger = archiLogger ?? throw new ArgumentNullException(nameof(archiLogger)); HttpClientHandler = new HttpClientHandler { AllowAutoRedirect = false, // This must be false if we want to handle custom redirection schemes such as "steammobile://" -#if !NETFRAMEWORK - AutomaticDecompression = DecompressionMethods.All, -#else +#if NETFRAMEWORK AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip, +#else + AutomaticDecompression = DecompressionMethods.All, #endif CookieContainer = CookieContainer @@ -86,9 +86,12 @@ namespace ArchiSteamFarm { HttpClientHandler.Dispose(); } - [NotNull] [PublicAPI] public HttpClient GenerateDisposableHttpClient(bool extendedTimeout = false) { + if (ASF.GlobalConfig == null) { + throw new ArgumentNullException(nameof(ASF.GlobalConfig)); + } + HttpClient result = new HttpClient(HttpClientHandler, false) { #if !NETFRAMEWORK DefaultRequestVersion = HttpVersion.Version20, @@ -103,19 +106,16 @@ namespace ArchiSteamFarm { return result; } - [ItemCanBeNull] [PublicAPI] - public async Task UrlGetToHtmlDocument(string request, string referer = null, ERequestOptions requestOptions = ERequestOptions.None, byte maxTries = MaxTries) { + public async Task UrlGetToHtmlDocument(string request, string? referer = null, ERequestOptions requestOptions = ERequestOptions.None, byte maxTries = MaxTries) { if (string.IsNullOrEmpty(request) || (maxTries == 0)) { - ArchiLogger.LogNullError(nameof(request) + " || " + nameof(maxTries)); - - return null; + throw new ArgumentNullException(nameof(request) + " || " + nameof(maxTries)); } - HtmlDocumentResponse result = null; + HtmlDocumentResponse? result = null; for (byte i = 0; i < maxTries; i++) { - await using StreamResponse response = await UrlGetToStream(request, referer, requestOptions | ERequestOptions.ReturnClientErrors, 1).ConfigureAwait(false); + await using StreamResponse? response = await UrlGetToStream(request, referer, requestOptions | ERequestOptions.ReturnClientErrors, 1).ConfigureAwait(false); if (response?.StatusCode.IsClientErrorCode() == true) { if (requestOptions.HasFlag(ERequestOptions.ReturnClientErrors)) { @@ -148,19 +148,16 @@ namespace ArchiSteamFarm { return result; } - [ItemCanBeNull] [PublicAPI] - public async Task> UrlGetToJsonObject(string request, string referer = null, ERequestOptions requestOptions = ERequestOptions.None, byte maxTries = MaxTries) where T : class { + public async Task?> UrlGetToJsonObject(string request, string? referer = null, ERequestOptions requestOptions = ERequestOptions.None, byte maxTries = MaxTries) where T : class { if (string.IsNullOrEmpty(request) || (maxTries == 0)) { - ArchiLogger.LogNullError(nameof(request) + " || " + nameof(maxTries)); - - return null; + throw new ArgumentNullException(nameof(request) + " || " + nameof(maxTries)); } - ObjectResponse result = null; + ObjectResponse? result = null; for (byte i = 0; i < maxTries; i++) { - await using StreamResponse response = await UrlGetToStream(request, referer, requestOptions | ERequestOptions.ReturnClientErrors, 1).ConfigureAwait(false); + await using StreamResponse? response = await UrlGetToStream(request, referer, requestOptions | ERequestOptions.ReturnClientErrors, 1).ConfigureAwait(false); if (response?.StatusCode.IsClientErrorCode() == true) { if (requestOptions.HasFlag(ERequestOptions.ReturnClientErrors)) { @@ -174,7 +171,7 @@ namespace ArchiSteamFarm { continue; } - T obj; + T? obj; try { using StreamReader streamReader = new StreamReader(response.Content); @@ -182,6 +179,12 @@ namespace ArchiSteamFarm { JsonSerializer serializer = new JsonSerializer(); obj = serializer.Deserialize(jsonReader); + + if (obj == null) { + ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorIsEmpty, nameof(obj))); + + continue; + } } catch (Exception e) { ArchiLogger.LogGenericWarningException(e); @@ -199,19 +202,16 @@ namespace ArchiSteamFarm { return result; } - [ItemCanBeNull] [PublicAPI] - public async Task UrlGetToXmlDocument(string request, string referer = null, ERequestOptions requestOptions = ERequestOptions.None, byte maxTries = MaxTries) { + public async Task UrlGetToXmlDocument(string request, string? referer = null, ERequestOptions requestOptions = ERequestOptions.None, byte maxTries = MaxTries) { if (string.IsNullOrEmpty(request) || (maxTries == 0)) { - ArchiLogger.LogNullError(nameof(request) + " || " + nameof(maxTries)); - - return null; + throw new ArgumentNullException(nameof(request) + " || " + nameof(maxTries)); } - XmlDocumentResponse result = null; + XmlDocumentResponse? result = null; for (byte i = 0; i < maxTries; i++) { - await using StreamResponse response = await UrlGetToStream(request, referer, requestOptions | ERequestOptions.ReturnClientErrors, 1).ConfigureAwait(false); + await using StreamResponse? response = await UrlGetToStream(request, referer, requestOptions | ERequestOptions.ReturnClientErrors, 1).ConfigureAwait(false); if (response?.StatusCode.IsClientErrorCode() == true) { if (requestOptions.HasFlag(ERequestOptions.ReturnClientErrors)) { @@ -246,19 +246,16 @@ namespace ArchiSteamFarm { return result; } - [ItemCanBeNull] [PublicAPI] - public async Task UrlHead(string request, string referer = null, ERequestOptions requestOptions = ERequestOptions.None, byte maxTries = MaxTries) { + public async Task UrlHead(string request, string? referer = null, ERequestOptions requestOptions = ERequestOptions.None, byte maxTries = MaxTries) { if (string.IsNullOrEmpty(request) || (maxTries == 0)) { - ArchiLogger.LogNullError(nameof(request) + " || " + nameof(maxTries)); - - return null; + throw new ArgumentNullException(nameof(request) + " || " + nameof(maxTries)); } - BasicResponse result = null; + BasicResponse? result = null; for (byte i = 0; i < maxTries; i++) { - using HttpResponseMessage response = await InternalHead(request, referer).ConfigureAwait(false); + using HttpResponseMessage? response = await InternalHead(request, referer).ConfigureAwait(false); if (response == null) { continue; @@ -283,19 +280,16 @@ namespace ArchiSteamFarm { return result; } - [ItemCanBeNull] [PublicAPI] - public async Task UrlPost(string request, T data = null, string referer = null, ERequestOptions requestOptions = ERequestOptions.None, byte maxTries = MaxTries) where T : class { + public async Task UrlPost(string request, T? data = null, string? referer = null, ERequestOptions requestOptions = ERequestOptions.None, byte maxTries = MaxTries) where T : class { if (string.IsNullOrEmpty(request) || (maxTries == 0)) { - ArchiLogger.LogNullError(nameof(request) + " || " + nameof(maxTries)); - - return null; + throw new ArgumentNullException(nameof(request) + " || " + nameof(maxTries)); } - BasicResponse result = null; + BasicResponse? result = null; for (byte i = 0; i < maxTries; i++) { - using HttpResponseMessage response = await InternalPost(request, data, referer).ConfigureAwait(false); + using HttpResponseMessage? response = await InternalPost(request, data, referer).ConfigureAwait(false); if (response == null) { continue; @@ -320,19 +314,16 @@ namespace ArchiSteamFarm { return result; } - [ItemCanBeNull] [PublicAPI] - public async Task UrlPostToHtmlDocument(string request, T data = null, string referer = null, ERequestOptions requestOptions = ERequestOptions.None, byte maxTries = MaxTries) where T : class { + public async Task UrlPostToHtmlDocument(string request, T? data = null, string? referer = null, ERequestOptions requestOptions = ERequestOptions.None, byte maxTries = MaxTries) where T : class { if (string.IsNullOrEmpty(request) || (maxTries == 0)) { - ArchiLogger.LogNullError(nameof(request) + " || " + nameof(maxTries)); - - return null; + throw new ArgumentNullException(nameof(request) + " || " + nameof(maxTries)); } - HtmlDocumentResponse result = null; + HtmlDocumentResponse? result = null; for (byte i = 0; i < maxTries; i++) { - await using StreamResponse response = await UrlPostToStream(request, data, referer, requestOptions | ERequestOptions.ReturnClientErrors, 1).ConfigureAwait(false); + await using StreamResponse? response = await UrlPostToStream(request, data, referer, requestOptions | ERequestOptions.ReturnClientErrors, 1).ConfigureAwait(false); if (response?.StatusCode.IsClientErrorCode() == true) { if (requestOptions.HasFlag(ERequestOptions.ReturnClientErrors)) { @@ -365,19 +356,16 @@ namespace ArchiSteamFarm { return result; } - [ItemCanBeNull] [PublicAPI] - public async Task> UrlPostToJsonObject(string request, TData data = null, string referer = null, ERequestOptions requestOptions = ERequestOptions.None, byte maxTries = MaxTries) where TResult : class where TData : class { + public async Task?> UrlPostToJsonObject(string request, 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)) { - ArchiLogger.LogNullError(nameof(request) + " || " + nameof(maxTries)); - - return null; + throw new ArgumentNullException(nameof(request) + " || " + nameof(maxTries)); } - ObjectResponse result = null; + ObjectResponse? result = null; for (byte i = 0; i < maxTries; i++) { - await using StreamResponse response = await UrlPostToStream(request, data, referer, requestOptions | ERequestOptions.ReturnClientErrors, 1).ConfigureAwait(false); + await using StreamResponse? response = await UrlPostToStream(request, data, referer, requestOptions | ERequestOptions.ReturnClientErrors, 1).ConfigureAwait(false); if (response?.StatusCode.IsClientErrorCode() == true) { if (requestOptions.HasFlag(ERequestOptions.ReturnClientErrors)) { @@ -391,7 +379,7 @@ namespace ArchiSteamFarm { continue; } - TResult obj; + TResult? obj; try { using StreamReader steamReader = new StreamReader(response.Content); @@ -399,6 +387,12 @@ namespace ArchiSteamFarm { JsonSerializer serializer = new JsonSerializer(); obj = serializer.Deserialize(jsonReader); + + if (obj == null) { + ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorIsEmpty, nameof(obj))); + + continue; + } } catch (Exception e) { ArchiLogger.LogGenericWarningException(e); @@ -432,33 +426,18 @@ namespace ArchiSteamFarm { } } - internal static async Task StringToHtmlDocument(string html) { - if (string.IsNullOrEmpty(html)) { - ASF.ArchiLogger.LogNullError(nameof(html)); - - return null; - } - - IBrowsingContext context = BrowsingContext.New(); - - return await context.OpenAsync(req => req.Content(html)).ConfigureAwait(false); - } - - [ItemCanBeNull] - internal async Task UrlGetToBinaryWithProgress(string request, string referer = null, ERequestOptions requestOptions = ERequestOptions.None, byte maxTries = MaxTries) { + internal async Task UrlGetToBinaryWithProgress(string request, string? referer = null, ERequestOptions requestOptions = ERequestOptions.None, byte maxTries = MaxTries) { if (string.IsNullOrEmpty(request) || (maxTries == 0)) { - ArchiLogger.LogNullError(nameof(request) + " || " + nameof(maxTries)); - - return null; + throw new ArgumentNullException(nameof(request) + " || " + nameof(maxTries)); } - BinaryResponse result = null; + BinaryResponse? result = null; for (byte i = 0; i < maxTries; i++) { const byte printPercentage = 10; const byte maxBatches = 99 / printPercentage; - await using StreamResponse response = await UrlGetToStream(request, referer, requestOptions | ERequestOptions.ReturnClientErrors, 1).ConfigureAwait(false); + await using StreamResponse? response = await UrlGetToStream(request, referer, requestOptions | ERequestOptions.ReturnClientErrors, 1).ConfigureAwait(false); if (response?.StatusCode.IsClientErrorCode() == true) { if (requestOptions.HasFlag(ERequestOptions.ReturnClientErrors)) { @@ -474,10 +453,11 @@ namespace ArchiSteamFarm { ArchiLogger.LogGenericDebug("0%..."); -#if !NETFRAMEWORK - await +#if NETFRAMEWORK + using MemoryStream ms = new MemoryStream((int) response.Length); +#else + await using MemoryStream ms = new MemoryStream((int) response.Length); #endif - using MemoryStream ms = new MemoryStream((int) response.Length); try { byte batch = 0; @@ -525,18 +505,15 @@ namespace ArchiSteamFarm { return result; } - [ItemCanBeNull] - internal async Task UrlGetToString(string request, string referer = null, ERequestOptions requestOptions = ERequestOptions.None, byte maxTries = MaxTries) { + internal async Task UrlGetToString(string request, string? referer = null, ERequestOptions requestOptions = ERequestOptions.None, byte maxTries = MaxTries) { if (string.IsNullOrEmpty(request) || (maxTries == 0)) { - ArchiLogger.LogNullError(nameof(request) + " || " + nameof(maxTries)); - - return null; + throw new ArgumentNullException(nameof(request) + " || " + nameof(maxTries)); } - StringResponse result = null; + StringResponse? result = null; for (byte i = 0; i < maxTries; i++) { - using HttpResponseMessage response = await InternalGet(request, referer).ConfigureAwait(false); + using HttpResponseMessage? response = await InternalGet(request, referer).ConfigureAwait(false); if (response?.StatusCode.IsClientErrorCode() == true) { if (requestOptions.HasFlag(ERequestOptions.ReturnClientErrors)) { @@ -561,41 +538,33 @@ namespace ArchiSteamFarm { return result; } - private async Task InternalGet(string request, string referer = null, HttpCompletionOption httpCompletionOption = HttpCompletionOption.ResponseContentRead) { + private async Task InternalGet(string request, string? referer = null, HttpCompletionOption httpCompletionOption = HttpCompletionOption.ResponseContentRead) { if (string.IsNullOrEmpty(request)) { - ArchiLogger.LogNullError(nameof(request)); - - return null; + throw new ArgumentNullException(nameof(request)); } return await InternalRequest(new Uri(request), HttpMethod.Get, null, referer, httpCompletionOption).ConfigureAwait(false); } - private async Task InternalHead(string request, string referer = null) { + private async Task InternalHead(string request, string? referer = null) { if (string.IsNullOrEmpty(request)) { - ArchiLogger.LogNullError(nameof(request)); - - return null; + throw new ArgumentNullException(nameof(request)); } return await InternalRequest(new Uri(request), HttpMethod.Head, null, referer).ConfigureAwait(false); } - private async Task InternalPost(string request, T data = null, string referer = null, HttpCompletionOption httpCompletionOption = HttpCompletionOption.ResponseContentRead) where T : class { + private async Task InternalPost(string request, T? data = null, string? referer = null, HttpCompletionOption httpCompletionOption = HttpCompletionOption.ResponseContentRead) where T : class { if (string.IsNullOrEmpty(request)) { - ArchiLogger.LogNullError(nameof(request)); - - return null; + throw new ArgumentNullException(nameof(request)); } return await InternalRequest(new Uri(request), HttpMethod.Post, data, referer, httpCompletionOption).ConfigureAwait(false); } - private async Task InternalRequest(Uri requestUri, HttpMethod httpMethod, T data = null, string referer = null, HttpCompletionOption httpCompletionOption = HttpCompletionOption.ResponseContentRead, byte maxRedirections = MaxTries) where T : class { + private async Task InternalRequest(Uri requestUri, HttpMethod httpMethod, T? data = null, string? referer = null, HttpCompletionOption httpCompletionOption = HttpCompletionOption.ResponseContentRead, byte maxRedirections = MaxTries) where T : class { if ((requestUri == null) || (httpMethod == null)) { - ArchiLogger.LogNullError(nameof(requestUri) + " || " + nameof(httpMethod)); - - return null; + throw new ArgumentNullException(nameof(requestUri) + " || " + nameof(httpMethod)); } HttpResponseMessage response; @@ -677,7 +646,7 @@ namespace ArchiSteamFarm { return response; default: // We have no clue about those, but maybe HttpClient can handle them for us - ASF.ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(redirectUri.Scheme), redirectUri.Scheme)); + ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(redirectUri.Scheme), redirectUri.Scheme)); break; } @@ -728,18 +697,15 @@ namespace ArchiSteamFarm { } } - [ItemCanBeNull] - private async Task UrlGetToStream(string request, string referer = null, ERequestOptions requestOptions = ERequestOptions.None, byte maxTries = MaxTries) { + private async Task UrlGetToStream(string request, string? referer = null, ERequestOptions requestOptions = ERequestOptions.None, byte maxTries = MaxTries) { if (string.IsNullOrEmpty(request) || (maxTries == 0)) { - ArchiLogger.LogNullError(nameof(request) + " || " + nameof(maxTries)); - - return null; + throw new ArgumentNullException(nameof(request) + " || " + nameof(maxTries)); } - StreamResponse result = null; + StreamResponse? result = null; for (byte i = 0; i < maxTries; i++) { - HttpResponseMessage response = await InternalGet(request, referer, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false); + HttpResponseMessage? response = await InternalGet(request, referer, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false); if (response?.StatusCode.IsClientErrorCode() == true) { if (requestOptions.HasFlag(ERequestOptions.ReturnClientErrors)) { @@ -764,18 +730,15 @@ namespace ArchiSteamFarm { return result; } - [ItemCanBeNull] - private async Task UrlPostToStream(string request, T data = null, string referer = null, ERequestOptions requestOptions = ERequestOptions.None, byte maxTries = MaxTries) where T : class { + private async Task UrlPostToStream(string request, T? data = null, string? referer = null, ERequestOptions requestOptions = ERequestOptions.None, byte maxTries = MaxTries) where T : class { if (string.IsNullOrEmpty(request) || (maxTries == 0)) { - ArchiLogger.LogNullError(nameof(request) + " || " + nameof(maxTries)); - - return null; + throw new ArgumentNullException(nameof(request) + " || " + nameof(maxTries)); } - StreamResponse result = null; + StreamResponse? result = null; for (byte i = 0; i < maxTries; i++) { - HttpResponseMessage response = await InternalPost(request, data, referer, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false); + HttpResponseMessage? response = await InternalPost(request, data, referer, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false); if (response?.StatusCode.IsClientErrorCode() == true) { if (requestOptions.HasFlag(ERequestOptions.ReturnClientErrors)) { @@ -806,7 +769,7 @@ namespace ArchiSteamFarm { internal readonly Uri FinalUri; - internal BasicResponse([NotNull] HttpResponseMessage httpResponseMessage) { + internal BasicResponse(HttpResponseMessage httpResponseMessage) { if (httpResponseMessage == null) { throw new ArgumentNullException(nameof(httpResponseMessage)); } @@ -815,7 +778,7 @@ namespace ArchiSteamFarm { StatusCode = httpResponseMessage.StatusCode; } - internal BasicResponse([NotNull] BasicResponse basicResponse) { + internal BasicResponse(BasicResponse basicResponse) { if (basicResponse == null) { throw new ArgumentNullException(nameof(basicResponse)); } @@ -827,15 +790,15 @@ namespace ArchiSteamFarm { public sealed class HtmlDocumentResponse : BasicResponse, IDisposable { [PublicAPI] - public readonly IDocument Content; + public readonly IDocument? Content; - internal HtmlDocumentResponse([NotNull] BasicResponse basicResponse) : base(basicResponse) { + internal HtmlDocumentResponse(BasicResponse basicResponse) : base(basicResponse) { if (basicResponse == null) { throw new ArgumentNullException(nameof(basicResponse)); } } - private HtmlDocumentResponse([NotNull] StreamResponse streamResponse, [NotNull] IDocument document) : this(streamResponse) { + private HtmlDocumentResponse(StreamResponse streamResponse, IDocument document) : this(streamResponse) { if ((streamResponse == null) || (document == null)) { throw new ArgumentNullException(nameof(streamResponse) + " || " + nameof(document)); } @@ -845,8 +808,7 @@ namespace ArchiSteamFarm { public void Dispose() => Content?.Dispose(); - [ItemCanBeNull] - internal static async Task Create([NotNull] StreamResponse streamResponse) { + internal static async Task Create(StreamResponse streamResponse) { if (streamResponse == null) { throw new ArgumentNullException(nameof(streamResponse)); } @@ -865,11 +827,11 @@ namespace ArchiSteamFarm { } } - public sealed class ObjectResponse : BasicResponse { + public sealed class ObjectResponse : BasicResponse where T : class { [PublicAPI] - public readonly T Content; + public readonly T? Content; - internal ObjectResponse([NotNull] StreamResponse streamResponse, T content) : this(streamResponse) { + internal ObjectResponse(StreamResponse streamResponse, T content) : this(streamResponse) { if (streamResponse == null) { throw new ArgumentNullException(nameof(streamResponse)); } @@ -877,7 +839,7 @@ namespace ArchiSteamFarm { Content = content; } - internal ObjectResponse([NotNull] BasicResponse basicResponse) : base(basicResponse) { + internal ObjectResponse(BasicResponse basicResponse) : base(basicResponse) { if (basicResponse == null) { throw new ArgumentNullException(nameof(basicResponse)); } @@ -886,9 +848,9 @@ namespace ArchiSteamFarm { public sealed class XmlDocumentResponse : BasicResponse { [PublicAPI] - public readonly XmlDocument Content; + public readonly XmlDocument? Content; - internal XmlDocumentResponse([NotNull] StreamResponse streamResponse, XmlDocument content) : this(streamResponse) { + internal XmlDocumentResponse(StreamResponse streamResponse, XmlDocument content) : this(streamResponse) { if (streamResponse == null) { throw new ArgumentNullException(nameof(streamResponse)); } @@ -896,7 +858,7 @@ namespace ArchiSteamFarm { Content = content; } - internal XmlDocumentResponse([NotNull] BasicResponse basicResponse) : base(basicResponse) { + internal XmlDocumentResponse(BasicResponse basicResponse) : base(basicResponse) { if (basicResponse == null) { throw new ArgumentNullException(nameof(basicResponse)); } @@ -910,9 +872,9 @@ namespace ArchiSteamFarm { } internal sealed class BinaryResponse : BasicResponse { - internal readonly byte[] Content; + internal readonly byte[]? Content; - internal BinaryResponse([NotNull] BasicResponse basicResponse, [NotNull] byte[] content) : this(basicResponse) { + internal BinaryResponse(BasicResponse basicResponse, byte[] content) : this(basicResponse) { if ((basicResponse == null) || (content == null)) { throw new ArgumentNullException(nameof(basicResponse) + " || " + nameof(content)); } @@ -920,7 +882,7 @@ namespace ArchiSteamFarm { Content = content; } - internal BinaryResponse([NotNull] BasicResponse basicResponse) : base(basicResponse) { + internal BinaryResponse(BasicResponse basicResponse) : base(basicResponse) { if (basicResponse == null) { throw new ArgumentNullException(nameof(basicResponse)); } @@ -928,12 +890,12 @@ namespace ArchiSteamFarm { } internal sealed class StreamResponse : BasicResponse, IAsyncDisposable { - internal readonly Stream Content; + internal readonly Stream? Content; internal readonly uint Length; private readonly HttpResponseMessage ResponseMessage; - internal StreamResponse([NotNull] HttpResponseMessage httpResponseMessage, [NotNull] Stream content) : this(httpResponseMessage) { + internal StreamResponse(HttpResponseMessage httpResponseMessage, Stream content) : this(httpResponseMessage) { if ((httpResponseMessage == null) || (content == null)) { throw new ArgumentNullException(nameof(httpResponseMessage) + " || " + nameof(content)); } @@ -941,7 +903,7 @@ namespace ArchiSteamFarm { Content = content; } - internal StreamResponse([NotNull] HttpResponseMessage httpResponseMessage) : base(httpResponseMessage) { + internal StreamResponse(HttpResponseMessage httpResponseMessage) : base(httpResponseMessage) { if (httpResponseMessage == null) { throw new ArgumentNullException(nameof(httpResponseMessage)); } @@ -960,9 +922,9 @@ namespace ArchiSteamFarm { } internal sealed class StringResponse : BasicResponse { - internal readonly string Content; + internal readonly string? Content; - internal StringResponse([NotNull] HttpResponseMessage httpResponseMessage, [NotNull] string content) : this(httpResponseMessage) { + internal StringResponse(HttpResponseMessage httpResponseMessage, string content) : this(httpResponseMessage) { if ((httpResponseMessage == null) || (content == null)) { throw new ArgumentNullException(nameof(httpResponseMessage) + " || " + nameof(content)); } @@ -970,7 +932,7 @@ namespace ArchiSteamFarm { Content = content; } - internal StringResponse([NotNull] HttpResponseMessage httpResponseMessage) : base(httpResponseMessage) { + internal StringResponse(HttpResponseMessage httpResponseMessage) : base(httpResponseMessage) { if (httpResponseMessage == null) { throw new ArgumentNullException(nameof(httpResponseMessage)); } diff --git a/Directory.Build.props b/Directory.Build.props index 372936366..92d8b0ba3 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -13,6 +13,7 @@ none latest 1591 + enable ../resources/ASF.ico Apache-2.0 https://github.com/JustArchiNET/ArchiSteamFarm