From f8aa8babcfe2a92294b8c88f7c6b032a4edb5ee5 Mon Sep 17 00:00:00 2001 From: JustArchi Date: Sat, 15 Dec 2018 00:27:15 +0100 Subject: [PATCH] R# cleanup --- ArchiSteamFarm.Tests/Trading.cs | 1 + ArchiSteamFarm.sln.DotSettings | 5 + ArchiSteamFarm/ASF.cs | 61 +++- ArchiSteamFarm/Actions.cs | 15 + ArchiSteamFarm/ArchiCryptoHelper.cs | 25 ++ ArchiSteamFarm/ArchiHandler.cs | 64 ++++ ArchiSteamFarm/ArchiWebHandler.cs | 277 ++++++++++++++- ArchiSteamFarm/Bot.cs | 164 ++++++++- ArchiSteamFarm/BotConfig.cs | 16 + ArchiSteamFarm/BotDatabase.cs | 18 + .../CMsgs/CMsgClientAcknowledgeClanInvite.cs | 2 + ArchiSteamFarm/CardsFarmer.cs | 126 ++++++- .../Collections/ConcurrentHashSet.cs | 9 +- .../Collections/ConcurrentSortedHashSet.cs | 14 +- .../Collections/FixedSizeConcurrentQueue.cs | 2 + ArchiSteamFarm/Commands.cs | 330 ++++++++++++++++++ ArchiSteamFarm/Debugging.cs | 1 + ArchiSteamFarm/GitHub.cs | 11 + ArchiSteamFarm/GlobalConfig.cs | 12 + ArchiSteamFarm/GlobalDatabase.cs | 9 + ArchiSteamFarm/Helpers/ArchiCachable.cs | 1 + ArchiSteamFarm/IPC/ArchiKestrel.cs | 2 + .../IPC/Controllers/Api/ASFController.cs | 6 + .../IPC/Controllers/Api/BotController.cs | 29 ++ .../IPC/Controllers/Api/CommandController.cs | 3 + .../IPC/Controllers/Api/NLogController.cs | 7 + .../Controllers/Api/StructureController.cs | 1 + .../IPC/Controllers/Api/TypeController.cs | 2 + .../IPC/Controllers/Api/WWWController.cs | 8 + .../Middleware/ApiAuthenticationMiddleware.cs | 3 + .../IPC/Responses/GenericResponse.cs | 1 + ArchiSteamFarm/IPC/Startup.cs | 4 + ArchiSteamFarm/IPC/WebUtilities.cs | 4 + ArchiSteamFarm/Json/Steam.cs | 30 ++ ArchiSteamFarm/MobileAuthenticator.cs | 47 +++ ArchiSteamFarm/NLog/ArchiLogger.cs | 19 +- ArchiSteamFarm/NLog/HistoryTarget.cs | 3 + ArchiSteamFarm/NLog/Logging.cs | 4 +- ArchiSteamFarm/NLog/SteamTarget.cs | 3 + ArchiSteamFarm/OS.cs | 6 + ArchiSteamFarm/Program.cs | 37 ++ ArchiSteamFarm/Statistics.cs | 52 +++ .../SteamKit2/InMemoryServerListProvider.cs | 2 + .../SteamKit2/ServerRecordEndPoint.cs | 7 +- ArchiSteamFarm/SteamSaleEvent.cs | 17 + ArchiSteamFarm/Trading.cs | 35 ++ ArchiSteamFarm/Utilities.cs | 19 + ArchiSteamFarm/WebBrowser.cs | 28 ++ 48 files changed, 1510 insertions(+), 32 deletions(-) diff --git a/ArchiSteamFarm.Tests/Trading.cs b/ArchiSteamFarm.Tests/Trading.cs index 620e95c01..456f42e9c 100644 --- a/ArchiSteamFarm.Tests/Trading.cs +++ b/ArchiSteamFarm.Tests/Trading.cs @@ -263,6 +263,7 @@ namespace ArchiSteamFarm.Tests { CreateItem(2), CreateItem(5) }; + HashSet itemsToReceive = new HashSet { CreateItem(3), CreateItem(4) diff --git a/ArchiSteamFarm.sln.DotSettings b/ArchiSteamFarm.sln.DotSettings index f2b4b01df..00086a4f3 100644 --- a/ArchiSteamFarm.sln.DotSettings +++ b/ArchiSteamFarm.sln.DotSettings @@ -186,7 +186,12 @@ False END_OF_LINE 1 + 1 1 + 1 + 1 + 1 + 1 0 0 END_OF_LINE diff --git a/ArchiSteamFarm/ASF.cs b/ArchiSteamFarm/ASF.cs index c9e923f06..637189293 100644 --- a/ArchiSteamFarm/ASF.cs +++ b/ArchiSteamFarm/ASF.cs @@ -51,6 +51,7 @@ namespace ArchiSteamFarm { await Utilities.InParallel(Directory.EnumerateFiles(SharedInfo.ConfigDirectory, "*" + SharedInfo.ConfigExtension).Select(Path.GetFileNameWithoutExtension).Where(botName => !string.IsNullOrEmpty(botName) && IsValidBotName(botName)).OrderBy(botName => botName).Select(Bot.RegisterBot)).ConfigureAwait(false); } catch (Exception e) { ArchiLogger.LogGenericException(e); + return; } @@ -77,6 +78,7 @@ namespace ArchiSteamFarm { internal static bool IsOwner(ulong steamID) { if (steamID == 0) { ArchiLogger.LogNullError(nameof(steamID)); + return false; } @@ -107,6 +109,7 @@ namespace ArchiSteamFarm { // If backup directory from previous update exists, it's a good idea to purge it now string backupDirectory = Path.Combine(SharedInfo.HomeDirectory, SharedInfo.UpdateDirectory); + if (Directory.Exists(backupDirectory)) { // It's entirely possible that old process is still running, wait a short moment for eventual cleanup await Task.Delay(5000).ConfigureAwait(false); @@ -115,28 +118,22 @@ namespace ArchiSteamFarm { Directory.Delete(backupDirectory, true); } catch (Exception e) { ArchiLogger.LogGenericException(e); + return null; } } - // TODO: cleanup from previous update, remove this after next stable - try { - foreach (string file in Directory.EnumerateFiles(SharedInfo.HomeDirectory, "*.old", SearchOption.AllDirectories)) { - File.Delete(file); - } - } catch (Exception e) { - ArchiLogger.LogGenericException(e); - return null; - } - GitHub.ReleaseResponse releaseResponse = await GitHub.GetLatestRelease(Program.GlobalConfig.UpdateChannel == GlobalConfig.EUpdateChannel.Stable).ConfigureAwait(false); + if (releaseResponse == null) { ArchiLogger.LogGenericWarning(Strings.ErrorUpdateCheckFailed); + return null; } if (string.IsNullOrEmpty(releaseResponse.Tag)) { ArchiLogger.LogGenericWarning(Strings.ErrorUpdateCheckFailed); + return null; } @@ -151,18 +148,21 @@ namespace ArchiSteamFarm { if (SharedInfo.Version > newVersion) { ArchiLogger.LogGenericWarning(Strings.WarningPreReleaseVersion); await Task.Delay(15 * 1000).ConfigureAwait(false); + return SharedInfo.Version; } if (!updateOverride && (Program.GlobalConfig.UpdatePeriod == 0)) { ArchiLogger.LogGenericInfo(Strings.UpdateNewVersionAvailable); await Task.Delay(5000).ConfigureAwait(false); + return null; } // Auto update logic starts here if (releaseResponse.Assets == null) { ArchiLogger.LogGenericWarning(Strings.ErrorUpdateNoAssets); + return null; } @@ -171,11 +171,13 @@ namespace ArchiSteamFarm { if (binaryAsset == null) { ArchiLogger.LogGenericWarning(Strings.ErrorUpdateNoAssetForThisVersion); + return null; } if (string.IsNullOrEmpty(binaryAsset.DownloadURL)) { ArchiLogger.LogNullError(nameof(binaryAsset.DownloadURL)); + return null; } @@ -186,6 +188,7 @@ namespace ArchiSteamFarm { ArchiLogger.LogGenericInfo(string.Format(Strings.UpdateDownloadingNewVersion, newVersion, binaryAsset.Size / 1024 / 1024)); WebBrowser.BinaryResponse response = await Program.WebBrowser.UrlGetToBinaryWithProgress(binaryAsset.DownloadURL).ConfigureAwait(false); + if (response?.Content == null) { return null; } @@ -198,17 +201,20 @@ namespace ArchiSteamFarm { } } catch (Exception e) { ArchiLogger.LogGenericException(e); + return null; } if (OS.IsUnix) { string executable = Path.Combine(SharedInfo.HomeDirectory, SharedInfo.AssemblyName); + if (File.Exists(executable)) { OS.UnixSetFileAccessExecutable(executable); } } ArchiLogger.LogGenericInfo(Strings.UpdateFinished); + return newVersion; } finally { UpdateSemaphore.Release(); @@ -234,6 +240,7 @@ namespace ArchiSteamFarm { } Version newVersion = await Update().ConfigureAwait(false); + if ((newVersion == null) || (newVersion <= SharedInfo.Version)) { return; } @@ -244,6 +251,7 @@ namespace ArchiSteamFarm { private static async Task CanHandleWriteEvent(string name) { if (string.IsNullOrEmpty(name)) { ArchiLogger.LogNullError(nameof(name)); + return false; } @@ -261,6 +269,7 @@ namespace ArchiSteamFarm { private static bool IsValidBotName(string botName) { if (string.IsNullOrEmpty(botName)) { ArchiLogger.LogNullError(nameof(botName)); + return false; } @@ -270,8 +279,10 @@ namespace ArchiSteamFarm { switch (botName) { case SharedInfo.ASF: + return false; default: + return true; } } @@ -279,6 +290,7 @@ namespace ArchiSteamFarm { private static async void OnChanged(object sender, FileSystemEventArgs e) { if ((sender == null) || (e == null)) { ArchiLogger.LogNullError(nameof(sender) + " || " + nameof(e)); + return; } @@ -288,6 +300,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; } @@ -300,9 +313,11 @@ namespace ArchiSteamFarm { switch (extension) { case SharedInfo.ConfigExtension: await OnChangedConfigFile(name, fullPath).ConfigureAwait(false); + break; case SharedInfo.KeysExtension: await OnChangedKeysFile(name, fullPath).ConfigureAwait(false); + break; } } @@ -310,6 +325,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; } @@ -319,6 +335,7 @@ namespace ArchiSteamFarm { private static async void OnCreated(object sender, FileSystemEventArgs e) { if ((sender == null) || (e == null)) { ArchiLogger.LogNullError(nameof(sender) + " || " + nameof(e)); + return; } @@ -328,10 +345,12 @@ 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; } string botName = Path.GetFileNameWithoutExtension(name); + if (string.IsNullOrEmpty(botName) || (botName[0] == '.')) { return; } @@ -343,6 +362,7 @@ namespace ArchiSteamFarm { if (botName.Equals(SharedInfo.ASF)) { ArchiLogger.LogGenericInfo(Strings.GlobalConfigChanged); await RestartOrExit().ConfigureAwait(false); + return; } @@ -360,6 +380,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; } @@ -368,9 +389,11 @@ namespace ArchiSteamFarm { switch (extension) { case SharedInfo.ConfigExtension: await OnCreatedConfigFile(name, fullPath).ConfigureAwait(false); + break; case SharedInfo.KeysExtension: await OnCreatedKeysFile(name, fullPath).ConfigureAwait(false); + break; } } @@ -378,10 +401,12 @@ 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; } string botName = Path.GetFileNameWithoutExtension(name); + if (string.IsNullOrEmpty(botName) || (botName[0] == '.')) { return; } @@ -400,6 +425,7 @@ namespace ArchiSteamFarm { private static async void OnDeleted(object sender, FileSystemEventArgs e) { if ((sender == null) || (e == null)) { ArchiLogger.LogNullError(nameof(sender) + " || " + nameof(e)); + return; } @@ -409,10 +435,12 @@ 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; } string botName = Path.GetFileNameWithoutExtension(name); + if (string.IsNullOrEmpty(botName)) { return; } @@ -429,12 +457,14 @@ namespace ArchiSteamFarm { // Some editors might decide to delete file and re-create it in order to modify it // If that's the case, we wait for maximum of 5 seconds before shutting down await Task.Delay(5000).ConfigureAwait(false); + if (File.Exists(fullPath)) { return; } ArchiLogger.LogGenericError(Strings.ErrorGlobalConfigRemoved); await Program.Exit(1).ConfigureAwait(false); + return; } @@ -450,6 +480,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; } @@ -458,6 +489,7 @@ namespace ArchiSteamFarm { switch (extension) { case SharedInfo.ConfigExtension: await OnDeletedConfigFile(name, fullPath).ConfigureAwait(false); + break; } } @@ -465,6 +497,7 @@ namespace ArchiSteamFarm { private static async void OnRenamed(object sender, RenamedEventArgs e) { if ((sender == null) || (e == null)) { ArchiLogger.LogNullError(nameof(sender) + " || " + nameof(e)); + return; } @@ -475,6 +508,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; } @@ -487,6 +521,7 @@ namespace ArchiSteamFarm { if (string.IsNullOrEmpty(fileName)) { ArchiLogger.LogNullError(nameof(fileName)); + return false; } @@ -494,6 +529,7 @@ namespace ArchiSteamFarm { if (string.IsNullOrEmpty(relativeFilePath)) { ArchiLogger.LogNullError(nameof(relativeFilePath)); + return false; } @@ -502,18 +538,22 @@ namespace ArchiSteamFarm { switch (relativeDirectoryName) { // Files in those directories we want to keep in their current place case SharedInfo.ConfigDirectory: + continue; case "": + switch (fileName) { // Files with those names in root directory we want to keep case SharedInfo.LogFile: case "NLog.config": + continue; } break; case null: ArchiLogger.LogNullError(nameof(relativeDirectoryName)); + return false; } @@ -543,6 +583,7 @@ namespace ArchiSteamFarm { if (string.IsNullOrEmpty(directory)) { ArchiLogger.LogNullError(nameof(directory)); + return false; } diff --git a/ArchiSteamFarm/Actions.cs b/ArchiSteamFarm/Actions.cs index 981c34a2e..33d051804 100644 --- a/ArchiSteamFarm/Actions.cs +++ b/ArchiSteamFarm/Actions.cs @@ -68,6 +68,7 @@ namespace ArchiSteamFarm { } HashSet confirmations = await Bot.BotDatabase.MobileAuthenticator.GetConfirmations(acceptedType).ConfigureAwait(false); + if ((confirmations == null) || (confirmations.Count == 0)) { continue; } @@ -125,6 +126,7 @@ namespace ArchiSteamFarm { } HashSet giftCardIDs = await Bot.ArchiWebHandler.GetDigitalGiftCards().ConfigureAwait(false); + if ((giftCardIDs == null) || (giftCardIDs.Count == 0)) { return; } @@ -136,6 +138,7 @@ namespace ArchiSteamFarm { await LimitGiftsRequestsAsync().ConfigureAwait(false); bool result = await Bot.ArchiWebHandler.AcceptDigitalGiftCard(giftCardID).ConfigureAwait(false); + if (result) { Bot.ArchiLogger.LogGenericInfo(Strings.Success); } else { @@ -150,6 +153,7 @@ namespace ArchiSteamFarm { internal async Task AcceptGuestPasses(IReadOnlyCollection guestPassIDs) { if ((guestPassIDs == null) || (guestPassIDs.Count == 0)) { Bot.ArchiLogger.LogNullError(nameof(guestPassIDs)); + return; } @@ -160,6 +164,7 @@ namespace ArchiSteamFarm { await LimitGiftsRequestsAsync().ConfigureAwait(false); ArchiHandler.RedeemGuestPassResponseCallback response = await Bot.ArchiHandler.RedeemGuestPass(guestPassID).ConfigureAwait(false); + if (response != null) { if (response.Result == EResult.OK) { Bot.ArchiLogger.LogGenericInfo(Strings.Success); @@ -186,6 +191,7 @@ namespace ArchiSteamFarm { internal async Task GetTradingLock() { await TradingSemaphore.WaitAsync().ConfigureAwait(false); + return new SemaphoreLock(TradingSemaphore); } @@ -255,12 +261,14 @@ namespace ArchiSteamFarm { } Utilities.InBackground(() => Bot.CardsFarmer.Resume(true)); + return (true, Strings.BotAutomaticIdlingNowResumed); } internal async Task<(bool Success, string Output)> SendTradeOffer(uint appID = Steam.Asset.SteamAppID, byte contextID = Steam.Asset.SteamCommunityContextID, ulong targetSteamID = 0, IReadOnlyCollection wantedTypes = null, IReadOnlyCollection wantedRealAppIDs = null) { if ((appID == 0) || (contextID == 0)) { Bot.ArchiLogger.LogNullError(nameof(appID) + " || " + nameof(contextID)); + return (false, string.Format(Strings.ErrorObjectIsNull, nameof(targetSteamID) + " || " + nameof(appID) + " || " + nameof(contextID))); } @@ -270,6 +278,7 @@ namespace ArchiSteamFarm { if (targetSteamID == 0) { targetSteamID = GetFirstSteamMasterID(); + if (targetSteamID == 0) { return (false, Strings.BotLootingMasterNotDefined); } @@ -295,6 +304,7 @@ namespace ArchiSteamFarm { } HashSet inventory = await Bot.ArchiWebHandler.GetInventory(Bot.SteamID, appID, contextID, true, wantedTypes, wantedRealAppIDs).ConfigureAwait(false); + if ((inventory == null) || (inventory.Count == 0)) { return (false, string.Format(Strings.ErrorIsEmpty, nameof(inventory))); } @@ -328,6 +338,7 @@ namespace ArchiSteamFarm { SkipFirstShutdown = true; Utilities.InBackground(Bot.Start); + return (true, Strings.Done); } @@ -337,11 +348,13 @@ namespace ArchiSteamFarm { } Bot.Stop(); + return (true, Strings.Done); } internal static async Task<(bool Success, string Message)> Update() { Version version = await ASF.Update(true).ConfigureAwait(false); + if (version == null) { return (false, null); } @@ -351,6 +364,7 @@ namespace ArchiSteamFarm { } Utilities.InBackground(ASF.RestartOrExit); + return (true, version.ToString()); } @@ -362,6 +376,7 @@ namespace ArchiSteamFarm { } await GiftsSemaphore.WaitAsync().ConfigureAwait(false); + Utilities.InBackground( async () => { await Task.Delay(Program.GlobalConfig.GiftsLimiterDelay * 1000).ConfigureAwait(false); diff --git a/ArchiSteamFarm/ArchiCryptoHelper.cs b/ArchiSteamFarm/ArchiCryptoHelper.cs index d729cd0b6..f7bf2a3b5 100644 --- a/ArchiSteamFarm/ArchiCryptoHelper.cs +++ b/ArchiSteamFarm/ArchiCryptoHelper.cs @@ -32,18 +32,23 @@ namespace ArchiSteamFarm { internal static string Decrypt(ECryptoMethod cryptoMethod, string encrypted) { if (string.IsNullOrEmpty(encrypted)) { ASF.ArchiLogger.LogNullError(nameof(encrypted)); + return null; } 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; } } @@ -51,18 +56,23 @@ namespace ArchiSteamFarm { internal static string Encrypt(ECryptoMethod cryptoMethod, string decrypted) { if (string.IsNullOrEmpty(decrypted)) { ASF.ArchiLogger.LogNullError(nameof(decrypted)); + return null; } 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; } } @@ -70,6 +80,7 @@ namespace ArchiSteamFarm { internal static void SetEncryptionKey(string key) { if (string.IsNullOrEmpty(key)) { ASF.ArchiLogger.LogNullError(nameof(key)); + return; } @@ -79,20 +90,24 @@ namespace ArchiSteamFarm { private static string DecryptAES(string encrypted) { if (string.IsNullOrEmpty(encrypted)) { ASF.ArchiLogger.LogNullError(nameof(encrypted)); + return null; } try { byte[] key; + using (SHA256 sha256 = SHA256.Create()) { key = sha256.ComputeHash(EncryptionKey); } byte[] decryptedData = Convert.FromBase64String(encrypted); decryptedData = CryptoHelper.SymmetricDecrypt(decryptedData, key); + return Encoding.UTF8.GetString(decryptedData); } catch (Exception e) { ASF.ArchiLogger.LogGenericException(e); + return null; } } @@ -100,6 +115,7 @@ namespace ArchiSteamFarm { private static string DecryptProtectedDataForCurrentUser(string encrypted) { if (string.IsNullOrEmpty(encrypted)) { ASF.ArchiLogger.LogNullError(nameof(encrypted)); + return null; } @@ -113,9 +129,11 @@ namespace ArchiSteamFarm { return Encoding.UTF8.GetString(decryptedData); } catch (PlatformNotSupportedException e) { ASF.ArchiLogger.LogGenericWarningException(e); + return null; } catch (Exception e) { ASF.ArchiLogger.LogGenericException(e); + return null; } } @@ -123,20 +141,24 @@ namespace ArchiSteamFarm { private static string EncryptAES(string decrypted) { if (string.IsNullOrEmpty(decrypted)) { ASF.ArchiLogger.LogNullError(nameof(decrypted)); + return null; } try { byte[] key; + using (SHA256 sha256 = SHA256.Create()) { key = sha256.ComputeHash(EncryptionKey); } byte[] encryptedData = Encoding.UTF8.GetBytes(decrypted); encryptedData = CryptoHelper.SymmetricEncrypt(encryptedData, key); + return Convert.ToBase64String(encryptedData); } catch (Exception e) { ASF.ArchiLogger.LogGenericException(e); + return null; } } @@ -144,6 +166,7 @@ namespace ArchiSteamFarm { private static string EncryptProtectedDataForCurrentUser(string decrypted) { if (string.IsNullOrEmpty(decrypted)) { ASF.ArchiLogger.LogNullError(nameof(decrypted)); + return null; } @@ -157,9 +180,11 @@ namespace ArchiSteamFarm { return Convert.ToBase64String(encryptedData); } catch (PlatformNotSupportedException e) { ASF.ArchiLogger.LogGenericWarningException(e); + return null; } catch (Exception e) { ASF.ArchiLogger.LogGenericException(e); + return null; } } diff --git a/ArchiSteamFarm/ArchiHandler.cs b/ArchiSteamFarm/ArchiHandler.cs index 7df4dffc9..72d183a26 100644 --- a/ArchiSteamFarm/ArchiHandler.cs +++ b/ArchiSteamFarm/ArchiHandler.cs @@ -62,6 +62,7 @@ namespace ArchiSteamFarm { public override void HandleMsg(IPacketMsg packetMsg) { if (packetMsg == null) { ArchiLogger.LogNullError(nameof(packetMsg)); + return; } @@ -70,24 +71,31 @@ namespace ArchiSteamFarm { switch (packetMsg.MsgType) { case EMsg.ClientItemAnnouncements: HandleItemAnnouncements(packetMsg); + break; case EMsg.ClientPlayingSessionState: HandlePlayingSessionState(packetMsg); + break; case EMsg.ClientPurchaseResponse: HandlePurchaseResponse(packetMsg); + break; case EMsg.ClientRedeemGuestPassResponse: HandleRedeemGuestPassResponse(packetMsg); + break; case EMsg.ClientSharedLibraryLockStatus: HandleSharedLibraryLockStatus(packetMsg); + break; case EMsg.ClientUserNotifications: HandleUserNotifications(packetMsg); + break; case EMsg.ClientVanityURLChangedNotification: HandleVanityURLChangedNotification(packetMsg); + break; } } @@ -95,6 +103,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; } @@ -114,6 +123,7 @@ namespace ArchiSteamFarm { internal void AckMessage(ulong steamID, uint timestamp) { if ((steamID == 0) || (timestamp == 0)) { ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(timestamp)); + return; } @@ -132,6 +142,7 @@ namespace ArchiSteamFarm { internal void AcknowledgeClanInvite(ulong clanID, bool acceptInvite) { if (clanID == 0) { ArchiLogger.LogNullError(nameof(clanID)); + return; } @@ -152,6 +163,7 @@ namespace ArchiSteamFarm { internal async Task AddFriend(ulong steamID) { if (steamID == 0) { ArchiLogger.LogNullError(nameof(steamID)); + return false; } @@ -167,11 +179,13 @@ namespace ArchiSteamFarm { response = await UnifiedPlayerService.SendMessage(x => x.AddFriend(request)); } catch (Exception e) { ArchiLogger.LogGenericWarningException(e); + return false; } if (response == null) { ArchiLogger.LogNullError(nameof(response)); + return false; } @@ -181,6 +195,7 @@ namespace ArchiSteamFarm { internal async Task GetClanChatGroupID(ulong steamID) { if ((steamID == 0) || !new SteamID(steamID).IsClanAccount) { ArchiLogger.LogNullError(nameof(steamID)); + return 0; } @@ -199,11 +214,13 @@ namespace ArchiSteamFarm { response = await UnifiedClanChatRoomsService.SendMessage(x => x.GetClanChatRoomInfo(request)); } catch (Exception e) { ArchiLogger.LogGenericWarningException(e); + return 0; } if (response == null) { ArchiLogger.LogNullError(nameof(response)); + return 0; } @@ -212,6 +229,7 @@ namespace ArchiSteamFarm { } CClanChatRooms_GetClanChatRoomInfo_Response body = response.GetDeserializedResponse(); + return body.chat_group_summary.chat_group_id; } @@ -227,11 +245,13 @@ namespace ArchiSteamFarm { response = await UnifiedPlayerService.SendMessage(x => x.GetGameBadgeLevels(request)); } catch (Exception e) { ArchiLogger.LogGenericWarningException(e); + return null; } if (response == null) { ArchiLogger.LogNullError(nameof(response)); + return null; } @@ -240,6 +260,7 @@ namespace ArchiSteamFarm { } CPlayer_GetGameBadgeLevels_Response body = response.GetDeserializedResponse(); + return body.player_level; } @@ -256,11 +277,13 @@ namespace ArchiSteamFarm { response = await UnifiedChatRoomService.SendMessage(x => x.GetMyChatRoomGroups(request)); } catch (Exception e) { ArchiLogger.LogGenericWarningException(e); + return null; } if (response == null) { ArchiLogger.LogNullError(nameof(response)); + return null; } @@ -269,6 +292,7 @@ namespace ArchiSteamFarm { } CChatRoom_GetMyChatRoomGroups_Response body = response.GetDeserializedResponse(); + return body.chat_room_groups.Select(chatRoom => chatRoom.group_summary.chat_group_id).ToHashSet(); } @@ -285,11 +309,13 @@ namespace ArchiSteamFarm { response = await UnifiedEconService.SendMessage(x => x.GetTradeOfferAccessToken(request)); } catch (Exception e) { ArchiLogger.LogGenericWarningException(e); + return null; } if (response == null) { ArchiLogger.LogNullError(nameof(response)); + return null; } @@ -298,12 +324,14 @@ namespace ArchiSteamFarm { } CEcon_GetTradeOfferAccessToken_Response body = response.GetDeserializedResponse(); + return body.trade_offer_access_token; } internal async Task JoinChatRoomGroup(ulong chatGroupID) { if (chatGroupID == 0) { ArchiLogger.LogNullError(nameof(chatGroupID)); + return false; } @@ -319,11 +347,13 @@ namespace ArchiSteamFarm { response = await UnifiedChatRoomService.SendMessage(x => x.JoinChatRoomGroup(request)); } catch (Exception e) { ArchiLogger.LogGenericWarningException(e); + return false; } if (response == null) { ArchiLogger.LogNullError(nameof(response)); + return false; } @@ -333,6 +363,7 @@ namespace ArchiSteamFarm { internal async Task PlayGames(IEnumerable gameIDs, string gameName = null) { if (gameIDs == null) { ArchiLogger.LogNullError(nameof(gameIDs)); + return; } @@ -378,6 +409,7 @@ namespace ArchiSteamFarm { internal async Task RedeemGuestPass(ulong guestPassID) { if (guestPassID == 0) { ArchiLogger.LogNullError(nameof(guestPassID)); + return null; } @@ -396,6 +428,7 @@ namespace ArchiSteamFarm { return await new AsyncJob(Client, request.SourceJobID); } catch (Exception e) { ArchiLogger.LogGenericException(e); + return null; } } @@ -403,6 +436,7 @@ namespace ArchiSteamFarm { internal async Task RedeemKey(string key) { if (string.IsNullOrEmpty(key)) { ArchiLogger.LogNullError(nameof(key)); + return null; } @@ -421,6 +455,7 @@ namespace ArchiSteamFarm { return await new AsyncJob(Client, request.SourceJobID); } catch (Exception e) { ArchiLogger.LogGenericException(e); + return null; } } @@ -428,6 +463,7 @@ namespace ArchiSteamFarm { internal async Task RemoveFriend(ulong steamID) { if (steamID == 0) { ArchiLogger.LogNullError(nameof(steamID)); + return false; } @@ -443,11 +479,13 @@ namespace ArchiSteamFarm { response = await UnifiedPlayerService.SendMessage(x => x.RemoveFriend(request)); } catch (Exception e) { ArchiLogger.LogGenericWarningException(e); + return false; } if (response == null) { ArchiLogger.LogNullError(nameof(response)); + return false; } @@ -466,6 +504,7 @@ namespace ArchiSteamFarm { internal async Task SendMessage(ulong steamID, string message) { if ((steamID == 0) || string.IsNullOrEmpty(message)) { ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(message)); + return EResult.Fail; } @@ -486,11 +525,13 @@ namespace ArchiSteamFarm { response = await UnifiedFriendMessagesService.SendMessage(x => x.SendMessage(request)); } catch (Exception e) { ArchiLogger.LogGenericWarningException(e); + return EResult.Timeout; } if (response == null) { ArchiLogger.LogNullError(nameof(response)); + return EResult.Fail; } @@ -500,6 +541,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.Fail; } @@ -519,11 +561,13 @@ namespace ArchiSteamFarm { response = await UnifiedChatRoomService.SendMessage(x => x.SendChatMessage(request)); } catch (Exception e) { ArchiLogger.LogGenericWarningException(e); + return EResult.Timeout; } if (response == null) { ArchiLogger.LogNullError(nameof(response)); + return EResult.Fail; } @@ -533,6 +577,7 @@ namespace ArchiSteamFarm { internal void SetCurrentMode(uint chatMode) { if (chatMode == 0) { ArchiLogger.LogNullError(nameof(chatMode)); + return; } @@ -547,6 +592,7 @@ namespace ArchiSteamFarm { private void HandleItemAnnouncements(IPacketMsg packetMsg) { if (packetMsg == null) { ArchiLogger.LogNullError(nameof(packetMsg)); + return; } @@ -557,6 +603,7 @@ namespace ArchiSteamFarm { private void HandlePlayingSessionState(IPacketMsg packetMsg) { if (packetMsg == null) { ArchiLogger.LogNullError(nameof(packetMsg)); + return; } @@ -567,6 +614,7 @@ namespace ArchiSteamFarm { private void HandlePurchaseResponse(IPacketMsg packetMsg) { if (packetMsg == null) { ArchiLogger.LogNullError(nameof(packetMsg)); + return; } @@ -577,6 +625,7 @@ namespace ArchiSteamFarm { private void HandleRedeemGuestPassResponse(IPacketMsg packetMsg) { if (packetMsg == null) { ArchiLogger.LogNullError(nameof(packetMsg)); + return; } @@ -587,6 +636,7 @@ namespace ArchiSteamFarm { private void HandleSharedLibraryLockStatus(IPacketMsg packetMsg) { if (packetMsg == null) { ArchiLogger.LogNullError(nameof(packetMsg)); + return; } @@ -597,6 +647,7 @@ namespace ArchiSteamFarm { private void HandleUserNotifications(IPacketMsg packetMsg) { if (packetMsg == null) { ArchiLogger.LogNullError(nameof(packetMsg)); + return; } @@ -607,6 +658,7 @@ namespace ArchiSteamFarm { private void HandleVanityURLChangedNotification(IPacketMsg packetMsg) { if (packetMsg == null) { ArchiLogger.LogNullError(nameof(packetMsg)); + return; } @@ -632,38 +684,48 @@ namespace ArchiSteamFarm { if (msg.purchase_receipt_info == null) { ASF.ArchiLogger.LogNullError(nameof(msg.purchase_receipt_info)); + return; } KeyValue receiptInfo = new KeyValue(); + using (MemoryStream ms = new MemoryStream(msg.purchase_receipt_info)) { if (!receiptInfo.TryReadAsBinary(ms)) { ASF.ArchiLogger.LogNullError(nameof(ms)); + return; } } List lineItems = receiptInfo["lineitems"].Children; + if (lineItems.Count == 0) { return; } Items = new Dictionary(lineItems.Count); + foreach (KeyValue lineItem in lineItems) { uint packageID = lineItem["PackageID"].AsUnsignedInteger(); + if (packageID == 0) { // Coupons have PackageID of -1 (don't ask me why) // We'll use ItemAppID in this case packageID = lineItem["ItemAppID"].AsUnsignedInteger(); + if (packageID == 0) { ASF.ArchiLogger.LogNullError(nameof(packageID)); + return; } } string gameName = lineItem["ItemDescription"].Value; + if (string.IsNullOrEmpty(gameName)) { ASF.ArchiLogger.LogNullError(nameof(gameName)); + return; } @@ -749,9 +811,11 @@ namespace ArchiSteamFarm { case EUserNotification.Items: case EUserNotification.ModeratorMessages: case EUserNotification.Trading: + break; default: ASF.ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(type), type)); + continue; } diff --git a/ArchiSteamFarm/ArchiWebHandler.cs b/ArchiSteamFarm/ArchiWebHandler.cs index ca82ab678..e44b0f18d 100644 --- a/ArchiSteamFarm/ArchiWebHandler.cs +++ b/ArchiSteamFarm/ArchiWebHandler.cs @@ -90,6 +90,7 @@ namespace ArchiSteamFarm { internal async Task AcceptDigitalGiftCard(ulong giftCardID) { if (giftCardID == 0) { Bot.ArchiLogger.LogNullError(nameof(giftCardID)); + return false; } @@ -102,12 +103,14 @@ namespace ArchiSteamFarm { }; Steam.NumberResponse result = await UrlPostToJsonObjectWithSession(SteamStoreURL, request, data).ConfigureAwait(false); + return result?.Success == true; } internal async Task<(bool Success, bool RequiresMobileConfirmation)> AcceptTradeOffer(ulong tradeID) { if (tradeID == 0) { Bot.ArchiLogger.LogNullError(nameof(tradeID)); + return (false, false); } @@ -121,12 +124,14 @@ namespace ArchiSteamFarm { }; 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; } @@ -139,18 +144,22 @@ namespace ArchiSteamFarm { }; HtmlDocument htmlDocument = await UrlPostToHtmlDocumentWithSession(SteamStoreURL, request, data).ConfigureAwait(false); + return htmlDocument?.DocumentNode.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; } string profileURL = await GetAbsoluteProfileURL().ConfigureAwait(false); + if (string.IsNullOrEmpty(profileURL)) { Bot.ArchiLogger.LogGenericWarning(Strings.WarningFailed); + return false; } @@ -163,12 +172,14 @@ namespace ArchiSteamFarm { }; Steam.NumberResponse response = await UrlPostToJsonObjectWithSession(SteamCommunityURL, request, data).ConfigureAwait(false); + if (response == null) { return false; } if (!response.Success) { Bot.ArchiLogger.LogGenericWarning(Strings.WarningFailed); + return false; } @@ -178,6 +189,7 @@ namespace ArchiSteamFarm { internal async Task ClearFromDiscoveryQueue(uint appID) { if (appID == 0) { Bot.ArchiLogger.LogNullError(nameof(appID)); + return false; } @@ -192,15 +204,18 @@ namespace ArchiSteamFarm { internal async Task DeclineTradeOffer(ulong tradeID) { if (tradeID == 0) { Bot.ArchiLogger.LogNullError(nameof(tradeID)); + return false; } (bool success, string steamApiKey) = await CachedApiKey.GetValue().ConfigureAwait(false); + if (!success || string.IsNullOrEmpty(steamApiKey)) { return false; } KeyValue response = null; + for (byte i = 0; (i < WebBrowser.MaxTries) && (response == null); i++) { using (dynamic iEconService = WebAPI.GetAsyncInterface(IEconService, steamApiKey)) { iEconService.Timeout = WebBrowser.Timeout; @@ -208,6 +223,7 @@ namespace ArchiSteamFarm { try { response = await WebLimitRequest( WebAPI.DefaultBaseAddress.Host, + // ReSharper disable once AccessToDisposedClosure async () => await iEconService.DeclineTradeOffer( method: WebRequestMethods.Http.Post, @@ -225,6 +241,7 @@ namespace ArchiSteamFarm { if (response == null) { Bot.ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, WebBrowser.MaxTries)); + return false; } @@ -238,16 +255,19 @@ namespace ArchiSteamFarm { Dictionary data = new Dictionary(2) { { "queuetype", "0" } }; Steam.NewDiscoveryQueueResponse output = await UrlPostToJsonObjectWithSession(SteamStoreURL, request, data).ConfigureAwait(false); + return output?.Queue; } internal async Task> GetActiveTradeOffers() { (bool success, string steamApiKey) = await CachedApiKey.GetValue().ConfigureAwait(false); + if (!success || string.IsNullOrEmpty(steamApiKey)) { return null; } KeyValue response = null; + for (byte i = 0; (i < WebBrowser.MaxTries) && (response == null); i++) { using (dynamic iEconService = WebAPI.GetAsyncInterface(IEconService, steamApiKey)) { iEconService.Timeout = WebBrowser.Timeout; @@ -255,6 +275,7 @@ namespace ArchiSteamFarm { try { response = await WebLimitRequest( WebAPI.DefaultBaseAddress.Host, + // ReSharper disable once AccessToDisposedClosure async () => await iEconService.GetTradeOffers( active_only: 1, @@ -274,14 +295,18 @@ namespace ArchiSteamFarm { if (response == null) { Bot.ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, WebBrowser.MaxTries)); + return null; } Dictionary descriptions = new Dictionary(); + foreach (KeyValue description in response["descriptions"].Children) { ulong classID = description["classid"].AsUnsignedLong(); + if (classID == 0) { Bot.ArchiLogger.LogNullError(nameof(classID)); + return null; } @@ -292,6 +317,7 @@ namespace ArchiSteamFarm { uint appID = 0; string hashName = description["market_hash_name"].Value; + if (!string.IsNullOrEmpty(hashName)) { appID = GetAppIDFromMarketHashName(hashName); } @@ -303,6 +329,7 @@ namespace ArchiSteamFarm { Steam.Asset.EType type = Steam.Asset.EType.Unknown; string descriptionType = description["type"].Value; + if (!string.IsNullOrEmpty(descriptionType)) { type = GetItemType(descriptionType); } @@ -311,10 +338,13 @@ namespace ArchiSteamFarm { } HashSet result = new HashSet(); + foreach (KeyValue trade in response["trade_offers_received"].Children) { Steam.TradeOffer.ETradeOfferState state = trade["trade_offer_state"].AsEnum(); + if (state == Steam.TradeOffer.ETradeOfferState.Unknown) { Bot.ArchiLogger.LogNullError(nameof(state)); + return null; } @@ -323,31 +353,39 @@ namespace ArchiSteamFarm { } ulong tradeOfferID = trade["tradeofferid"].AsUnsignedLong(); + if (tradeOfferID == 0) { Bot.ArchiLogger.LogNullError(nameof(tradeOfferID)); + return null; } uint otherSteamID3 = trade["accountid_other"].AsUnsignedInteger(); + if (otherSteamID3 == 0) { Bot.ArchiLogger.LogNullError(nameof(otherSteamID3)); + return null; } Steam.TradeOffer tradeOffer = new Steam.TradeOffer(tradeOfferID, otherSteamID3, state); List itemsToGive = trade["items_to_give"].Children; + if (itemsToGive.Count > 0) { if (!ParseItems(descriptions, itemsToGive, tradeOffer.ItemsToGive)) { Bot.ArchiLogger.LogGenericError(string.Format(Strings.ErrorParsingObject, nameof(itemsToGive))); + return null; } } List itemsToReceive = trade["items_to_receive"].Children; + if (itemsToReceive.Count > 0) { if (!ParseItems(descriptions, itemsToReceive, tradeOffer.ItemsToReceive)) { Bot.ArchiLogger.LogGenericError(string.Format(Strings.ErrorParsingObject, nameof(itemsToReceive))); + return null; } } @@ -360,6 +398,7 @@ namespace ArchiSteamFarm { internal async Task> GetAppList() { KeyValue response = null; + for (byte i = 0; (i < WebBrowser.MaxTries) && (response == null); i++) { using (dynamic iSteamApps = WebAPI.GetAsyncInterface(ISteamApps)) { iSteamApps.Timeout = WebBrowser.Timeout; @@ -367,6 +406,7 @@ namespace ArchiSteamFarm { try { response = await WebLimitRequest( WebAPI.DefaultBaseAddress.Host, + // ReSharper disable once AccessToDisposedClosure async () => await iSteamApps.GetAppList2(secure: true) ).ConfigureAwait(false); @@ -380,12 +420,15 @@ namespace ArchiSteamFarm { if (response == null) { Bot.ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, WebBrowser.MaxTries)); + return null; } List apps = response["apps"].Children; + if ((apps == null) || (apps.Count == 0)) { Bot.ArchiLogger.LogNullError(nameof(apps)); + return null; } @@ -396,6 +439,7 @@ namespace ArchiSteamFarm { if (appID == 0) { Bot.ArchiLogger.LogNullError(nameof(appID)); + return null; } @@ -408,16 +452,19 @@ namespace ArchiSteamFarm { internal async Task GetBadgePage(byte page) { if (page == 0) { Bot.ArchiLogger.LogNullError(nameof(page)); + return null; } string request = "/my/badges?l=english&p=" + page; + return await UrlGetToHtmlDocumentWithSession(SteamCommunityURL, request, false).ConfigureAwait(false); } internal async Task GetConfirmationDetails(string deviceID, string confirmationHash, uint time, MobileAuthenticator.Confirmation confirmation) { if (string.IsNullOrEmpty(deviceID) || string.IsNullOrEmpty(confirmationHash) || (time == 0) || (confirmation == null)) { Bot.ArchiLogger.LogNullError(nameof(deviceID) + " || " + nameof(confirmationHash) + " || " + nameof(time) + " || " + nameof(confirmation)); + return null; } @@ -428,6 +475,7 @@ namespace ArchiSteamFarm { if (SteamID == 0) { Bot.ArchiLogger.LogGenericWarning(Strings.WarningFailed); + return null; } } @@ -435,17 +483,20 @@ namespace ArchiSteamFarm { string request = "/mobileconf/details/" + confirmation.ID + "?a=" + SteamID + "&k=" + WebUtility.UrlEncode(confirmationHash) + "&l=english&m=android&p=" + WebUtility.UrlEncode(deviceID) + "&t=" + time + "&tag=conf"; Steam.ConfirmationDetails response = await UrlGetToJsonObjectWithSession(SteamCommunityURL, request).ConfigureAwait(false); + if (response?.Success != true) { return null; } response.Confirmation = confirmation; + return response; } 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; } @@ -456,11 +507,13 @@ namespace ArchiSteamFarm { if (SteamID == 0) { Bot.ArchiLogger.LogGenericWarning(Strings.WarningFailed); + return null; } } string request = "/mobileconf/conf?a=" + SteamID + "&k=" + WebUtility.UrlEncode(confirmationHash) + "&l=english&m=android&p=" + WebUtility.UrlEncode(deviceID) + "&t=" + time + "&tag=conf"; + return await UrlGetToHtmlDocumentWithSession(SteamCommunityURL, request).ConfigureAwait(false); } @@ -469,24 +522,29 @@ namespace ArchiSteamFarm { HtmlDocument response = await UrlGetToHtmlDocumentWithSession(SteamStoreURL, request).ConfigureAwait(false); HtmlNodeCollection htmlNodes = response?.DocumentNode.SelectNodes("//div[@class='pending_gift']/div[starts-with(@id, 'pending_gift_')][count(div[@class='pending_giftcard_leftcol']) > 0]/@id"); + if (htmlNodes == null) { return null; } HashSet results = new HashSet(); + foreach (string giftCardIDText in htmlNodes.Select(node => node.GetAttributeValue("id", null))) { if (string.IsNullOrEmpty(giftCardIDText)) { Bot.ArchiLogger.LogNullError(nameof(giftCardIDText)); + return null; } if (giftCardIDText.Length <= 13) { Bot.ArchiLogger.LogGenericError(string.Format(Strings.ErrorIsInvalid, nameof(giftCardIDText))); + return null; } if (!ulong.TryParse(giftCardIDText.Substring(13), out ulong giftCardID) || (giftCardID == 0)) { Bot.ArchiLogger.LogGenericError(string.Format(Strings.ErrorParsingObject, nameof(giftCardID))); + return null; } @@ -498,6 +556,7 @@ namespace ArchiSteamFarm { internal async Task GetDiscoveryQueuePage() { const string request = "/explore?l=english"; + return await UrlGetToHtmlDocumentWithSession(SteamStoreURL, request).ConfigureAwait(false); } @@ -506,6 +565,7 @@ namespace ArchiSteamFarm { HtmlDocument htmlDocument = await UrlGetToHtmlDocumentWithSession(SteamStoreURL, request).ConfigureAwait(false); HtmlNodeCollection htmlNodes = htmlDocument?.DocumentNode.SelectNodes("(//table[@class='accountTable'])[last()]//a/@data-miniprofile"); + if (htmlNodes == null) { return null; // OK, no authorized steamIDs } @@ -515,11 +575,13 @@ namespace ArchiSteamFarm { foreach (string miniProfile in htmlNodes.Select(htmlNode => htmlNode.GetAttributeValue("data-miniprofile", null))) { if (string.IsNullOrEmpty(miniProfile)) { Bot.ArchiLogger.LogNullError(nameof(miniProfile)); + return null; } if (!uint.TryParse(miniProfile, out uint steamID3) || (steamID3 == 0)) { Bot.ArchiLogger.LogNullError(nameof(steamID3)); + return null; } @@ -533,10 +595,12 @@ namespace ArchiSteamFarm { internal async Task GetGameCardsPage(ulong appID) { if (appID == 0) { Bot.ArchiLogger.LogNullError(nameof(appID)); + return null; } string request = "/my/gamecards/" + appID + "?l=english"; + return await UrlGetToHtmlDocumentWithSession(SteamCommunityURL, request, false).ConfigureAwait(false); } @@ -544,6 +608,7 @@ namespace ArchiSteamFarm { internal async Task> GetInventory(ulong steamID = 0, uint appID = Steam.Asset.SteamAppID, byte contextID = Steam.Asset.SteamCommunityContextID, bool? tradable = null, IReadOnlyCollection wantedTypes = null, IReadOnlyCollection wantedRealAppIDs = null, IReadOnlyCollection<(uint AppID, Steam.Asset.EType Type)> wantedSets = null, IReadOnlyCollection<(uint AppID, Steam.Asset.EType Type)> skippedSets = null) { if ((appID == 0) || (contextID == 0)) { Bot.ArchiLogger.LogNullError(nameof(appID) + " || " + nameof(contextID)); + return null; } @@ -555,6 +620,7 @@ namespace ArchiSteamFarm { if (SteamID == 0) { Bot.ArchiLogger.LogGenericWarning(Strings.WarningFailed); + return null; } } @@ -579,6 +645,7 @@ namespace ArchiSteamFarm { if (!response.Success) { Bot.ArchiLogger.LogGenericWarning(!string.IsNullOrEmpty(response.Error) ? string.Format(Strings.WarningFailedWithError, response.Error) : Strings.WarningFailed); + return null; } @@ -589,13 +656,16 @@ namespace ArchiSteamFarm { if ((response.Assets == null) || (response.Assets.Count == 0) || (response.Descriptions == null) || (response.Descriptions.Count == 0)) { Bot.ArchiLogger.LogNullError(nameof(response.Assets) + " || " + nameof(response.Descriptions)); + return null; } Dictionary descriptionMap = new Dictionary(); + foreach (Steam.InventoryResponse.Description description in response.Descriptions.Where(description => description != null)) { if (description.ClassID == 0) { Bot.ArchiLogger.LogNullError(nameof(description.ClassID)); + return null; } @@ -642,6 +712,7 @@ namespace ArchiSteamFarm { if (response.LastAssetID == 0) { Bot.ArchiLogger.LogNullError(nameof(response.LastAssetID)); + return null; } @@ -667,26 +738,33 @@ namespace ArchiSteamFarm { XmlDocument response = await UrlGetToXmlDocumentWithSession(SteamCommunityURL, request, false).ConfigureAwait(false); XmlNodeList xmlNodeList = response?.SelectNodes("gamesList/games/game"); + if ((xmlNodeList == null) || (xmlNodeList.Count == 0)) { return null; } Dictionary result = new Dictionary(xmlNodeList.Count); + foreach (XmlNode xmlNode in xmlNodeList) { XmlNode appNode = xmlNode.SelectSingleNode("appID"); + if (appNode == null) { Bot.ArchiLogger.LogNullError(nameof(appNode)); + return null; } if (!uint.TryParse(appNode.InnerText, out uint appID) || (appID == 0)) { Bot.ArchiLogger.LogNullError(nameof(appID)); + return null; } XmlNode nameNode = xmlNode.SelectSingleNode("name"); + if (nameNode == null) { Bot.ArchiLogger.LogNullError(nameof(nameNode)); + return null; } @@ -699,15 +777,18 @@ namespace ArchiSteamFarm { internal async Task> GetOwnedGames(ulong steamID) { if (steamID == 0) { Bot.ArchiLogger.LogNullError(nameof(steamID)); + return null; } (bool success, string steamApiKey) = await CachedApiKey.GetValue().ConfigureAwait(false); + if (!success || string.IsNullOrEmpty(steamApiKey)) { return null; } KeyValue response = null; + for (byte i = 0; (i < WebBrowser.MaxTries) && (response == null); i++) { using (dynamic iPlayerService = WebAPI.GetAsyncInterface(IPlayerService, steamApiKey)) { iPlayerService.Timeout = WebBrowser.Timeout; @@ -715,6 +796,7 @@ namespace ArchiSteamFarm { try { response = await WebLimitRequest( WebAPI.DefaultBaseAddress.Host, + // ReSharper disable once AccessToDisposedClosure async () => await iPlayerService.GetOwnedGames( include_appinfo: 1, @@ -732,14 +814,20 @@ namespace ArchiSteamFarm { if (response == null) { Bot.ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, WebBrowser.MaxTries)); + return null; } - Dictionary result = new Dictionary(response["games"].Children.Count); - foreach (KeyValue game in response["games"].Children) { + List games = response["games"].Children; + + Dictionary result = new Dictionary(games.Count); + + foreach (KeyValue game in games) { uint appID = game["appid"].AsUnsignedInteger(); + if (appID == 0) { Bot.ArchiLogger.LogNullError(nameof(appID)); + return null; } @@ -751,6 +839,7 @@ namespace ArchiSteamFarm { internal async Task GetServerTime() { KeyValue response = null; + for (byte i = 0; (i < WebBrowser.MaxTries) && (response == null); i++) { using (dynamic iTwoFactorService = WebAPI.GetAsyncInterface(ITwoFactorService)) { iTwoFactorService.Timeout = WebBrowser.Timeout; @@ -758,6 +847,7 @@ namespace ArchiSteamFarm { try { response = await WebLimitRequest( WebAPI.DefaultBaseAddress.Host, + // ReSharper disable once AccessToDisposedClosure async () => await iTwoFactorService.QueryTime( method: WebRequestMethods.Http.Post, @@ -774,12 +864,15 @@ namespace ArchiSteamFarm { if (response == null) { Bot.ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, WebBrowser.MaxTries)); + return 0; } uint result = response["server_time"].AsUnsignedInteger(); + if (result == 0) { Bot.ArchiLogger.LogNullError(nameof(result)); + return 0; } @@ -788,12 +881,14 @@ namespace ArchiSteamFarm { internal async Task GetSteamAwardsPage() { const string request = "/SteamAwards?l=english"; + return await UrlGetToHtmlDocumentWithSession(SteamStoreURL, request).ConfigureAwait(false); } internal async Task GetTradeHoldDurationForTrade(ulong tradeID) { if (tradeID == 0) { Bot.ArchiLogger.LogNullError(nameof(tradeID)); + return null; } @@ -802,20 +897,25 @@ namespace ArchiSteamFarm { HtmlDocument htmlDocument = await UrlGetToHtmlDocumentWithSession(SteamCommunityURL, request).ConfigureAwait(false); HtmlNode htmlNode = htmlDocument?.DocumentNode.SelectSingleNode("//div[@class='pagecontent']/script"); + if (htmlNode == null) { // Trade can be no longer valid return null; } string text = htmlNode.InnerText; + if (string.IsNullOrEmpty(text)) { Bot.ArchiLogger.LogNullError(nameof(text)); + return null; } int index = text.IndexOf("g_daysTheirEscrow = ", StringComparison.Ordinal); + if (index < 0) { Bot.ArchiLogger.LogNullError(nameof(index)); + return null; } @@ -823,8 +923,10 @@ namespace ArchiSteamFarm { text = text.Substring(index); index = text.IndexOf(';'); + if (index < 0) { Bot.ArchiLogger.LogNullError(nameof(index)); + return null; } @@ -832,6 +934,7 @@ namespace ArchiSteamFarm { if (!byte.TryParse(text, out byte result)) { Bot.ArchiLogger.LogNullError(nameof(result)); + return null; } @@ -841,15 +944,18 @@ namespace ArchiSteamFarm { internal async Task GetTradeHoldDurationForUser(ulong steamID, string tradeToken = null) { if (steamID == 0) { Bot.ArchiLogger.LogNullError(nameof(steamID)); + return null; } (bool success, string steamApiKey) = await CachedApiKey.GetValue().ConfigureAwait(false); + if (!success || string.IsNullOrEmpty(steamApiKey)) { return null; } KeyValue response = null; + for (byte i = 0; (i < WebBrowser.MaxTries) && (response == null); i++) { using (dynamic iEconService = WebAPI.GetAsyncInterface(IEconService, steamApiKey)) { iEconService.Timeout = WebBrowser.Timeout; @@ -857,6 +963,7 @@ namespace ArchiSteamFarm { try { response = await WebLimitRequest( WebAPI.DefaultBaseAddress.Host, + // ReSharper disable once AccessToDisposedClosure async () => await iEconService.GetTradeHoldDurations( secure: true, @@ -874,12 +981,15 @@ namespace ArchiSteamFarm { if (response == null) { Bot.ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, WebBrowser.MaxTries)); + return null; } uint resultInSeconds = response["their_escrow"]["escrow_end_duration_seconds"].AsUnsignedInteger(uint.MaxValue); + if (resultInSeconds == uint.MaxValue) { Bot.ArchiLogger.LogNullError(nameof(resultInSeconds)); + return null; } @@ -889,6 +999,7 @@ 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; } @@ -899,6 +1010,7 @@ namespace ArchiSteamFarm { if (SteamID == 0) { Bot.ArchiLogger.LogGenericWarning(Strings.WarningFailed); + return null; } } @@ -906,12 +1018,14 @@ namespace ArchiSteamFarm { string request = "/mobileconf/ajaxop?a=" + 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); + 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; } @@ -922,6 +1036,7 @@ namespace ArchiSteamFarm { if (SteamID == 0) { Bot.ArchiLogger.LogGenericWarning(Strings.WarningFailed); + return null; } } @@ -945,22 +1060,26 @@ namespace ArchiSteamFarm { } Steam.BooleanResponse response = await UrlPostToJsonObjectWithSession(SteamCommunityURL, request, data).ConfigureAwait(false); + return response?.Success; } internal async Task HasPublicInventory() { (bool success, bool hasPublicInventory) = await CachedPublicInventory.GetValue().ConfigureAwait(false); + return success && hasPublicInventory; } internal async Task HasValidApiKey() { (bool success, string steamApiKey) = await CachedApiKey.GetValue().ConfigureAwait(false); + return success && !string.IsNullOrEmpty(steamApiKey); } internal async Task Init(ulong steamID, EUniverse universe, string webAPIUserNonce, string parentalCode = null) { if ((steamID == 0) || (universe == EUniverse.Invalid) || string.IsNullOrEmpty(webAPIUserNonce)) { Bot.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(universe) + " || " + nameof(webAPIUserNonce)); + return false; } @@ -971,6 +1090,7 @@ namespace ArchiSteamFarm { // RSA encrypt it with the public key for the universe we're on byte[] encryptedSessionKey; + using (RSACrypto rsa = new RSACrypto(KeyDictionary.GetPublicKey(universe))) { encryptedSessionKey = rsa.Encrypt(sessionKey); } @@ -996,6 +1116,7 @@ namespace ArchiSteamFarm { try { response = await WebLimitRequest( WebAPI.DefaultBaseAddress.Host, + // ReSharper disable once AccessToDisposedClosure async () => await iSteamUserAuth.AuthenticateUser( encrypted_loginkey: Encoding.ASCII.GetString(WebUtility.UrlEncodeToBytes(encryptedLoginKey, 0, encryptedLoginKey.Length)), @@ -1007,9 +1128,11 @@ namespace ArchiSteamFarm { ).ConfigureAwait(false); } catch (TaskCanceledException e) { Bot.ArchiLogger.LogGenericDebuggingException(e); + return false; } catch (Exception e) { Bot.ArchiLogger.LogGenericWarningException(e); + return false; } } @@ -1019,14 +1142,18 @@ namespace ArchiSteamFarm { } string steamLogin = response["token"].Value; + if (string.IsNullOrEmpty(steamLogin)) { Bot.ArchiLogger.LogNullError(nameof(steamLogin)); + return false; } string steamLoginSecure = response["tokensecure"].Value; + if (string.IsNullOrEmpty(steamLoginSecure)) { Bot.ArchiLogger.LogNullError(nameof(steamLoginSecure)); + return false; } @@ -1050,12 +1177,14 @@ namespace ArchiSteamFarm { SteamID = steamID; LastSessionCheck = LastSessionRefresh = DateTime.UtcNow; + return true; } internal async Task JoinGroup(ulong groupID) { if (groupID == 0) { Bot.ArchiLogger.LogNullError(nameof(groupID)); + return false; } @@ -1103,6 +1232,7 @@ namespace ArchiSteamFarm { internal async Task MarkSentTrades() { const string request = "/my/tradeoffers/sent"; + return await UrlHeadWithSession(SteamCommunityURL, request, false).ConfigureAwait(false); } @@ -1117,12 +1247,14 @@ namespace ArchiSteamFarm { internal async Task<(EResult Result, EPurchaseResultDetail? PurchaseResult)?> RedeemWalletKey(string key) { if (string.IsNullOrEmpty(key)) { Bot.ArchiLogger.LogNullError(nameof(key)); + return null; } // ASF should redeem wallet key only in case of existing wallet if (Bot.WalletCurrency == ECurrencyCode.Invalid) { Bot.ArchiLogger.LogNullError(nameof(Bot.WalletCurrency)); + return null; } @@ -1132,6 +1264,7 @@ namespace ArchiSteamFarm { Dictionary data = new Dictionary(2) { { "wallet_code", key } }; Steam.RedeemWalletResponse responseValidateCode = await UrlPostToJsonObjectWithSession(SteamStoreURL, requestValidateCode, data).ConfigureAwait(false); + if (responseValidateCode == null) { return null; } @@ -1143,12 +1276,14 @@ namespace ArchiSteamFarm { if (responseValidateCode.KeyDetails == null) { Bot.ArchiLogger.LogNullError(nameof(responseValidateCode.KeyDetails)); + return null; } if (responseValidateCode.WalletCurrencyCode != responseValidateCode.KeyDetails.CurrencyCode) { const string requestCheckFunds = "/account/createwalletandcheckfunds"; Steam.EResultResponse responseCheckFunds = await UrlPostToJsonObjectWithSession(SteamStoreURL, requestCheckFunds, data).ConfigureAwait(false); + if (responseCheckFunds == null) { return null; } @@ -1160,6 +1295,7 @@ namespace ArchiSteamFarm { const string requestConfirmRedeem = "/account/confirmredeemwalletcode"; Steam.RedeemWalletResponse responseConfirmRedeem = await UrlPostToJsonObjectWithSession(SteamStoreURL, requestConfirmRedeem, data).ConfigureAwait(false); + if (responseConfirmRedeem == null) { return null; } @@ -1171,6 +1307,7 @@ namespace ArchiSteamFarm { internal async Task<(bool Success, HashSet MobileTradeOfferIDs)> SendTradeOffer(ulong partnerID, IReadOnlyCollection itemsToGive = null, IReadOnlyCollection itemsToReceive = null, string token = null, bool forcedSingleOffer = false) { if ((partnerID == 0) || (((itemsToGive == null) || (itemsToGive.Count == 0)) && ((itemsToReceive == null) || (itemsToReceive.Count == 0)))) { Bot.ArchiLogger.LogNullError(nameof(partnerID) + " || (" + nameof(itemsToGive) + " && " + nameof(itemsToReceive) + ")"); + return (false, null); } @@ -1240,6 +1377,7 @@ namespace ArchiSteamFarm { internal async Task SteamAwardsVote(byte voteID, uint appID) { if ((voteID == 0) || (appID == 0)) { Bot.ArchiLogger.LogNullError(nameof(voteID) + " || " + nameof(appID)); + return false; } @@ -1257,12 +1395,15 @@ namespace ArchiSteamFarm { internal async Task UnpackBooster(uint appID, ulong itemID) { if ((appID == 0) || (itemID == 0)) { Bot.ArchiLogger.LogNullError(nameof(appID) + " || " + nameof(itemID)); + return false; } string profileURL = await GetAbsoluteProfileURL().ConfigureAwait(false); + if (string.IsNullOrEmpty(profileURL)) { Bot.ArchiLogger.LogGenericWarning(Strings.WarningFailed); + return false; } @@ -1275,6 +1416,7 @@ namespace ArchiSteamFarm { }; Steam.EResultResponse response = await UrlPostToJsonObjectWithSession(SteamCommunityURL, request, data).ConfigureAwait(false); + return response?.Result == EResult.OK; } @@ -1286,6 +1428,7 @@ namespace ArchiSteamFarm { if (SteamID == 0) { Bot.ArchiLogger.LogGenericWarning(Strings.WarningFailed); + return null; } } @@ -1298,13 +1441,16 @@ namespace ArchiSteamFarm { HtmlDocument htmlDocument = await UrlGetToHtmlDocumentWithSession(SteamCommunityURL, request).ConfigureAwait(false); HtmlNode titleNode = htmlDocument?.DocumentNode.SelectSingleNode("//div[@id='mainContents']/h2"); + if (titleNode == null) { return (ESteamApiKeyState.Timeout, null); } string title = titleNode.InnerText; + if (string.IsNullOrEmpty(title)) { Bot.ArchiLogger.LogNullError(nameof(title)); + return (ESteamApiKeyState.Error, null); } @@ -1313,14 +1459,18 @@ namespace ArchiSteamFarm { } HtmlNode htmlNode = htmlDocument.DocumentNode.SelectSingleNode("//div[@id='bodyContents_ex']/p"); + if (htmlNode == null) { Bot.ArchiLogger.LogNullError(nameof(htmlNode)); + return (ESteamApiKeyState.Error, null); } string text = htmlNode.InnerText; + if (string.IsNullOrEmpty(text)) { Bot.ArchiLogger.LogNullError(nameof(text)); + return (ESteamApiKeyState.Error, null); } @@ -1329,8 +1479,10 @@ namespace ArchiSteamFarm { } int keyIndex = text.IndexOf("Key: ", StringComparison.Ordinal); + if (keyIndex < 0) { Bot.ArchiLogger.LogNullError(nameof(keyIndex)); + return (ESteamApiKeyState.Error, null); } @@ -1338,12 +1490,15 @@ namespace ArchiSteamFarm { if (text.Length <= keyIndex) { Bot.ArchiLogger.LogNullError(nameof(text)); + return (ESteamApiKeyState.Error, null); } text = text.Substring(keyIndex); + if ((text.Length != 32) || !Utilities.IsValidHexadecimalString(text)) { Bot.ArchiLogger.LogNullError(nameof(text)); + return (ESteamApiKeyState.Error, null); } @@ -1353,25 +1508,31 @@ namespace ArchiSteamFarm { private static uint GetAppIDFromMarketHashName(string hashName) { if (string.IsNullOrEmpty(hashName)) { ASF.ArchiLogger.LogNullError(nameof(hashName)); + return 0; } int index = hashName.IndexOf('-'); + return (index > 0) && uint.TryParse(hashName.Substring(0, index), out uint appID) && (appID != 0) ? appID : 0; } private static Steam.Asset.EType GetItemType(string name) { if (string.IsNullOrEmpty(name)) { ASF.ArchiLogger.LogNullError(nameof(name)); + return Steam.Asset.EType.Unknown; } switch (name) { case "Booster Pack": + return Steam.Asset.EType.BoosterPack; case "Steam Gems": + return Steam.Asset.EType.SteamGems; default: + if (name.EndsWith("Emoticon", StringComparison.Ordinal)) { return Steam.Asset.EType.Emoticon; } @@ -1391,12 +1552,15 @@ namespace ArchiSteamFarm { private async Task IsProfileUri(Uri uri, bool waitForInitialization = true) { if (uri == null) { ASF.ArchiLogger.LogNullError(nameof(uri)); + return false; } string profileURL = await GetAbsoluteProfileURL(waitForInitialization).ConfigureAwait(false); + if (string.IsNullOrEmpty(profileURL)) { Bot.ArchiLogger.LogGenericWarning(Strings.WarningFailed); + return false; } @@ -1422,6 +1586,7 @@ namespace ArchiSteamFarm { const string request = "/my/edit/settings"; WebBrowser.BasicResponse response = await WebLimitRequest(host, async () => await WebBrowser.UrlHead(host + request).ConfigureAwait(false)).ConfigureAwait(false); + if (response?.FinalUri == null) { return null; } @@ -1435,6 +1600,7 @@ namespace ArchiSteamFarm { } LastSessionCheck = now; + return result; } finally { SessionSemaphore.Release(); @@ -1444,6 +1610,7 @@ namespace ArchiSteamFarm { private static bool IsSessionExpiredUri(Uri uri) { if (uri == null) { ASF.ArchiLogger.LogNullError(nameof(uri)); + return false; } @@ -1453,31 +1620,40 @@ namespace ArchiSteamFarm { private static bool ParseItems(Dictionary 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; } foreach (KeyValue item in input) { uint appID = item["appid"].AsUnsignedInteger(); + if (appID == 0) { ASF.ArchiLogger.LogNullError(nameof(appID)); + return false; } ulong contextID = item["contextid"].AsUnsignedLong(); + if (contextID == 0) { ASF.ArchiLogger.LogNullError(nameof(contextID)); + return false; } ulong classID = item["classid"].AsUnsignedLong(); + if (classID == 0) { ASF.ArchiLogger.LogNullError(nameof(classID)); + return false; } uint amount = item["amount"].AsUnsignedInteger(); + if (amount == 0) { ASF.ArchiLogger.LogNullError(nameof(amount)); + return false; } @@ -1550,10 +1726,12 @@ namespace ArchiSteamFarm { switch (result.State) { case ESteamApiKeyState.AccessDenied: + // We succeeded in fetching API key, but it resulted in access denied // Return empty result, API key is unavailable permanently return (true, ""); case ESteamApiKeyState.NotRegisteredYet: + // We succeeded in fetching API key, and it resulted in no key registered yet // Let's try to register a new key if (!await RegisterApiKey().ConfigureAwait(false)) { @@ -1576,15 +1754,19 @@ namespace ArchiSteamFarm { goto case ESteamApiKeyState.Registered; case ESteamApiKeyState.Registered: + // We succeeded in fetching API key, and it resulted in registered key // Cache the result, this is the API key we want return (true, result.Key); case ESteamApiKeyState.Timeout: + // Request timed out, bad luck, we'll try again later return (false, null); default: + // We got an unhandled error, this should never happen Bot.ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(result.State), result.State)); + return (false, null); } } @@ -1598,14 +1780,18 @@ namespace ArchiSteamFarm { } HtmlNode htmlNode = htmlDocument.DocumentNode.SelectSingleNode("//div[@data-component='ProfilePrivacySettings']/@data-privacysettings"); + if (htmlNode == null) { Bot.ArchiLogger.LogNullError(nameof(htmlNode)); + return (false, false); } string json = htmlNode.GetAttributeValue("data-privacysettings", null); + if (string.IsNullOrEmpty(json)) { Bot.ArchiLogger.LogNullError(nameof(json)); + return (false, false); } @@ -1618,22 +1804,27 @@ namespace ArchiSteamFarm { userPrivacy = JsonConvert.DeserializeObject(json); } catch (JsonException e) { Bot.ArchiLogger.LogGenericException(e); + return (false, false); } if (userPrivacy == null) { Bot.ArchiLogger.LogNullError(nameof(userPrivacy)); + return (false, false); } switch (userPrivacy.Settings.Inventory) { case Steam.UserPrivacy.PrivacySettings.EPrivacySetting.FriendsOnly: case Steam.UserPrivacy.PrivacySettings.EPrivacySetting.Private: + return (true, false); case Steam.UserPrivacy.PrivacySettings.EPrivacySetting.Public: + return (true, true); default: Bot.ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(userPrivacy.Settings.Inventory), userPrivacy.Settings.Inventory)); + return (false, false); } } @@ -1641,6 +1832,7 @@ namespace ArchiSteamFarm { private async Task UnlockParentalAccount(string parentalCode) { if (string.IsNullOrEmpty(parentalCode)) { Bot.ArchiLogger.LogNullError(nameof(parentalCode)); + return false; } @@ -1648,21 +1840,25 @@ namespace ArchiSteamFarm { if (!await UnlockParentalAccountForService(SteamCommunityURL, parentalCode).ConfigureAwait(false)) { Bot.ArchiLogger.LogGenericWarning(Strings.WarningFailed); + return false; } if (!await UnlockParentalAccountForService(SteamStoreURL, parentalCode).ConfigureAwait(false)) { Bot.ArchiLogger.LogGenericWarning(Strings.WarningFailed); + return false; } Bot.ArchiLogger.LogGenericInfo(Strings.Success); + return true; } 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; } @@ -1671,6 +1867,7 @@ namespace ArchiSteamFarm { if (maxTries == 0) { Bot.ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, WebBrowser.MaxTries)); Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, serviceURL + request)); + return false; } @@ -1678,6 +1875,7 @@ namespace ArchiSteamFarm { // 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); + if ((response == null) || IsSessionExpiredUri(response.FinalUri)) { // There is no session refresh capability at this stage return false; @@ -1686,6 +1884,7 @@ namespace ArchiSteamFarm { // Under special brain-damaged circumstances, Steam might just return our own profile as a response to the request, for absolutely no reason whatsoever - just try again in this case if (await IsProfileUri(response.FinalUri, false).ConfigureAwait(false)) { Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.WarningWorkaroundTriggered, nameof(IsProfileUri))); + return await UnlockParentalAccountForService(serviceURL, parentalCode, --maxTries).ConfigureAwait(false); } @@ -1695,12 +1894,14 @@ namespace ArchiSteamFarm { private 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; } if (maxTries == 0) { Bot.ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, WebBrowser.MaxTries)); Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, host + request)); + return null; } @@ -1715,6 +1916,7 @@ namespace ArchiSteamFarm { Bot.ArchiLogger.LogGenericWarning(Strings.WarningFailed); Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, host + request)); + return null; } } else { @@ -1731,11 +1933,13 @@ namespace ArchiSteamFarm { if (SteamID == 0) { Bot.ArchiLogger.LogGenericWarning(Strings.WarningFailed); Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, host + request)); + return null; } } WebBrowser.HtmlDocumentResponse response = await WebLimitRequest(host, async () => await WebBrowser.UrlGetToHtmlDocument(host + request).ConfigureAwait(false)).ConfigureAwait(false); + if (response == null) { return null; } @@ -1747,12 +1951,14 @@ namespace ArchiSteamFarm { Bot.ArchiLogger.LogGenericWarning(Strings.WarningFailed); Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, host + request)); + return null; } // Under special brain-damaged circumstances, Steam might just return our own profile as a response to the request, for absolutely no reason whatsoever - just try again in this case if (await IsProfileUri(response.FinalUri).ConfigureAwait(false)) { Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.WarningWorkaroundTriggered, nameof(IsProfileUri))); + return await UrlGetToHtmlDocumentWithSession(host, request, checkSessionPreemptively, --maxTries).ConfigureAwait(false); } @@ -1762,12 +1968,14 @@ namespace ArchiSteamFarm { private 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; } if (maxTries == 0) { Bot.ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, WebBrowser.MaxTries)); Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, host + request)); + return default; } @@ -1782,6 +1990,7 @@ namespace ArchiSteamFarm { Bot.ArchiLogger.LogGenericWarning(Strings.WarningFailed); Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, host + request)); + return null; } } else { @@ -1798,11 +2007,13 @@ namespace ArchiSteamFarm { if (SteamID == 0) { Bot.ArchiLogger.LogGenericWarning(Strings.WarningFailed); Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, host + request)); + return default; } } WebBrowser.ObjectResponse response = await WebLimitRequest(host, async () => await WebBrowser.UrlGetToJsonObject(host + request).ConfigureAwait(false)).ConfigureAwait(false); + if (response == null) { return default; } @@ -1814,12 +2025,14 @@ namespace ArchiSteamFarm { Bot.ArchiLogger.LogGenericWarning(Strings.WarningFailed); Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, host + request)); + return null; } // Under special brain-damaged circumstances, Steam might just return our own profile as a response to the request, for absolutely no reason whatsoever - just try again in this case if (await IsProfileUri(response.FinalUri).ConfigureAwait(false)) { Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.WarningWorkaroundTriggered, nameof(IsProfileUri))); + return await UrlGetToJsonObjectWithSession(host, request, checkSessionPreemptively, --maxTries).ConfigureAwait(false); } @@ -1829,12 +2042,14 @@ namespace ArchiSteamFarm { private 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; } if (maxTries == 0) { Bot.ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, WebBrowser.MaxTries)); Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, host + request)); + return null; } @@ -1849,6 +2064,7 @@ namespace ArchiSteamFarm { Bot.ArchiLogger.LogGenericWarning(Strings.WarningFailed); Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, host + request)); + return null; } } else { @@ -1865,11 +2081,13 @@ namespace ArchiSteamFarm { if (SteamID == 0) { Bot.ArchiLogger.LogGenericWarning(Strings.WarningFailed); Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, host + request)); + return null; } } WebBrowser.XmlDocumentResponse response = await WebLimitRequest(host, async () => await WebBrowser.UrlGetToXmlDocument(host + request).ConfigureAwait(false)).ConfigureAwait(false); + if (response == null) { return null; } @@ -1881,12 +2099,14 @@ namespace ArchiSteamFarm { Bot.ArchiLogger.LogGenericWarning(Strings.WarningFailed); Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, host + request)); + return null; } // Under special brain-damaged circumstances, Steam might just return our own profile as a response to the request, for absolutely no reason whatsoever - just try again in this case if (await IsProfileUri(response.FinalUri).ConfigureAwait(false)) { Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.WarningWorkaroundTriggered, nameof(IsProfileUri))); + return await UrlGetToXmlDocumentWithSession(host, request, checkSessionPreemptively, --maxTries).ConfigureAwait(false); } @@ -1896,12 +2116,14 @@ namespace ArchiSteamFarm { private 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; } if (maxTries == 0) { Bot.ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, WebBrowser.MaxTries)); Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, host + request)); + return false; } @@ -1916,6 +2138,7 @@ namespace ArchiSteamFarm { Bot.ArchiLogger.LogGenericWarning(Strings.WarningFailed); Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, host + request)); + return false; } } else { @@ -1932,11 +2155,13 @@ namespace ArchiSteamFarm { if (SteamID == 0) { Bot.ArchiLogger.LogGenericWarning(Strings.WarningFailed); Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, host + request)); + return false; } } WebBrowser.BasicResponse response = await WebLimitRequest(host, async () => await WebBrowser.UrlHead(host + request).ConfigureAwait(false)).ConfigureAwait(false); + if (response == null) { return false; } @@ -1948,12 +2173,14 @@ namespace ArchiSteamFarm { Bot.ArchiLogger.LogGenericWarning(Strings.WarningFailed); Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, host + request)); + return false; } // Under special brain-damaged circumstances, Steam might just return our own profile as a response to the request, for absolutely no reason whatsoever - just try again in this case if (await IsProfileUri(response.FinalUri).ConfigureAwait(false)) { Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.WarningWorkaroundTriggered, nameof(IsProfileUri))); + return await UrlHeadWithSession(host, request, checkSessionPreemptively, --maxTries).ConfigureAwait(false); } @@ -1963,12 +2190,14 @@ namespace ArchiSteamFarm { private async Task UrlPostToHtmlDocumentWithSession(string host, string request, Dictionary data = null, string referer = null, ESession session = ESession.Lowercase, bool checkSessionPreemptively = true, byte maxTries = WebBrowser.MaxTries) { if (string.IsNullOrEmpty(host) || string.IsNullOrEmpty(request)) { Bot.ArchiLogger.LogNullError(nameof(host) + " || " + nameof(request)); + return null; } if (maxTries == 0) { Bot.ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, WebBrowser.MaxTries)); Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, host + request)); + return null; } @@ -1983,6 +2212,7 @@ namespace ArchiSteamFarm { Bot.ArchiLogger.LogGenericWarning(Strings.WarningFailed); Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, host + request)); + return null; } } else { @@ -1999,6 +2229,7 @@ namespace ArchiSteamFarm { if (SteamID == 0) { Bot.ArchiLogger.LogGenericWarning(Strings.WarningFailed); Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, host + request)); + return null; } } @@ -2008,6 +2239,7 @@ namespace ArchiSteamFarm { if (string.IsNullOrEmpty(sessionID)) { Bot.ArchiLogger.LogNullError(nameof(sessionID)); + return null; } @@ -2016,12 +2248,15 @@ namespace ArchiSteamFarm { switch (session) { case ESession.CamelCase: sessionName = "sessionID"; + break; case ESession.Lowercase: sessionName = "sessionid"; + break; default: Bot.ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(session), session)); + return null; } @@ -2033,6 +2268,7 @@ namespace ArchiSteamFarm { } WebBrowser.HtmlDocumentResponse response = await WebLimitRequest(host, async () => await WebBrowser.UrlPostToHtmlDocument(host + request, data, referer).ConfigureAwait(false)).ConfigureAwait(false); + if (response == null) { return null; } @@ -2044,12 +2280,14 @@ namespace ArchiSteamFarm { Bot.ArchiLogger.LogGenericWarning(Strings.WarningFailed); Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, host + request)); + return null; } // Under special brain-damaged circumstances, Steam might just return our own profile as a response to the request, for absolutely no reason whatsoever - just try again in this case if (await IsProfileUri(response.FinalUri).ConfigureAwait(false)) { Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.WarningWorkaroundTriggered, nameof(IsProfileUri))); + return await UrlPostToHtmlDocumentWithSession(host, request, data, referer, session, checkSessionPreemptively, --maxTries).ConfigureAwait(false); } @@ -2059,12 +2297,14 @@ namespace ArchiSteamFarm { private 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 { if (string.IsNullOrEmpty(host) || string.IsNullOrEmpty(request)) { Bot.ArchiLogger.LogNullError(nameof(host) + " || " + nameof(request)); + return null; } if (maxTries == 0) { Bot.ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, WebBrowser.MaxTries)); Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, host + request)); + return null; } @@ -2079,6 +2319,7 @@ namespace ArchiSteamFarm { Bot.ArchiLogger.LogGenericWarning(Strings.WarningFailed); Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, host + request)); + return null; } } else { @@ -2095,6 +2336,7 @@ namespace ArchiSteamFarm { if (SteamID == 0) { Bot.ArchiLogger.LogGenericWarning(Strings.WarningFailed); Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, host + request)); + return null; } } @@ -2104,6 +2346,7 @@ namespace ArchiSteamFarm { if (string.IsNullOrEmpty(sessionID)) { Bot.ArchiLogger.LogNullError(nameof(sessionID)); + return null; } @@ -2112,12 +2355,15 @@ namespace ArchiSteamFarm { switch (session) { case ESession.CamelCase: sessionName = "sessionID"; + break; case ESession.Lowercase: sessionName = "sessionid"; + break; default: Bot.ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(session), session)); + return null; } @@ -2129,6 +2375,7 @@ namespace ArchiSteamFarm { } WebBrowser.ObjectResponse response = await WebLimitRequest(host, async () => await WebBrowser.UrlPostToJsonObject(host + request, data, referer).ConfigureAwait(false)).ConfigureAwait(false); + if (response == null) { return null; } @@ -2140,12 +2387,14 @@ namespace ArchiSteamFarm { Bot.ArchiLogger.LogGenericWarning(Strings.WarningFailed); Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, host + request)); + return null; } // Under special brain-damaged circumstances, Steam might just return our own profile as a response to the request, for absolutely no reason whatsoever - just try again in this case if (await IsProfileUri(response.FinalUri).ConfigureAwait(false)) { Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.WarningWorkaroundTriggered, nameof(IsProfileUri))); + return await UrlPostToJsonObjectWithSession(host, request, data, referer, session, checkSessionPreemptively, --maxTries).ConfigureAwait(false); } @@ -2155,12 +2404,14 @@ namespace ArchiSteamFarm { private 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 { if (string.IsNullOrEmpty(host) || string.IsNullOrEmpty(request)) { Bot.ArchiLogger.LogNullError(nameof(host) + " || " + nameof(request)); + return null; } if (maxTries == 0) { Bot.ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, WebBrowser.MaxTries)); Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, host + request)); + return null; } @@ -2175,6 +2426,7 @@ namespace ArchiSteamFarm { Bot.ArchiLogger.LogGenericWarning(Strings.WarningFailed); Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, host + request)); + return null; } } else { @@ -2191,6 +2443,7 @@ namespace ArchiSteamFarm { if (SteamID == 0) { Bot.ArchiLogger.LogGenericWarning(Strings.WarningFailed); Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, host + request)); + return null; } } @@ -2200,6 +2453,7 @@ namespace ArchiSteamFarm { if (string.IsNullOrEmpty(sessionID)) { Bot.ArchiLogger.LogNullError(nameof(sessionID)); + return null; } @@ -2208,12 +2462,15 @@ namespace ArchiSteamFarm { switch (session) { case ESession.CamelCase: sessionName = "sessionID"; + break; case ESession.Lowercase: sessionName = "sessionid"; + break; default: Bot.ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(session), session)); + return null; } @@ -2228,6 +2485,7 @@ namespace ArchiSteamFarm { } WebBrowser.ObjectResponse response = await WebLimitRequest(host, async () => await WebBrowser.UrlPostToJsonObject(host + request, data, referer).ConfigureAwait(false)).ConfigureAwait(false); + if (response == null) { return null; } @@ -2239,12 +2497,14 @@ namespace ArchiSteamFarm { Bot.ArchiLogger.LogGenericWarning(Strings.WarningFailed); Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, host + request)); + return null; } // Under special brain-damaged circumstances, Steam might just return our own profile as a response to the request, for absolutely no reason whatsoever - just try again in this case if (await IsProfileUri(response.FinalUri).ConfigureAwait(false)) { Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.WarningWorkaroundTriggered, nameof(IsProfileUri))); + return await UrlPostToJsonObjectWithSession(host, request, data, referer, session, checkSessionPreemptively, --maxTries).ConfigureAwait(false); } @@ -2254,12 +2514,14 @@ namespace ArchiSteamFarm { private async Task UrlPostWithSession(string host, string request, Dictionary data = null, string referer = null, ESession session = ESession.Lowercase, bool checkSessionPreemptively = true, byte maxTries = WebBrowser.MaxTries) { if (string.IsNullOrEmpty(host) || string.IsNullOrEmpty(request)) { Bot.ArchiLogger.LogNullError(nameof(host) + " || " + nameof(request)); + return false; } if (maxTries == 0) { Bot.ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, WebBrowser.MaxTries)); Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, host + request)); + return false; } @@ -2274,6 +2536,7 @@ namespace ArchiSteamFarm { Bot.ArchiLogger.LogGenericWarning(Strings.WarningFailed); Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, host + request)); + return false; } } else { @@ -2290,6 +2553,7 @@ namespace ArchiSteamFarm { if (SteamID == 0) { Bot.ArchiLogger.LogGenericWarning(Strings.WarningFailed); Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, host + request)); + return false; } } @@ -2299,6 +2563,7 @@ namespace ArchiSteamFarm { if (string.IsNullOrEmpty(sessionID)) { Bot.ArchiLogger.LogNullError(nameof(sessionID)); + return false; } @@ -2307,12 +2572,15 @@ namespace ArchiSteamFarm { switch (session) { case ESession.CamelCase: sessionName = "sessionID"; + break; case ESession.Lowercase: sessionName = "sessionid"; + break; default: Bot.ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(session), session)); + return false; } @@ -2324,6 +2592,7 @@ namespace ArchiSteamFarm { } WebBrowser.BasicResponse response = await WebLimitRequest(host, async () => await WebBrowser.UrlPost(host + request, data, referer).ConfigureAwait(false)).ConfigureAwait(false); + if (response == null) { return false; } @@ -2335,12 +2604,14 @@ namespace ArchiSteamFarm { Bot.ArchiLogger.LogGenericWarning(Strings.WarningFailed); Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, host + request)); + return false; } // Under special brain-damaged circumstances, Steam might just return our own profile as a response to the request, for absolutely no reason whatsoever - just try again in this case if (await IsProfileUri(response.FinalUri).ConfigureAwait(false)) { Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.WarningWorkaroundTriggered, nameof(IsProfileUri))); + return await UrlPostWithSession(host, request, data, referer, session, checkSessionPreemptively, --maxTries).ConfigureAwait(false); } @@ -2350,6 +2621,7 @@ namespace ArchiSteamFarm { private static async Task WebLimitRequest(string service, Func> function) { if (string.IsNullOrEmpty(service) || (function == null)) { ASF.ArchiLogger.LogNullError(nameof(service) + " || " + nameof(function)); + return default; } @@ -2359,6 +2631,7 @@ namespace ArchiSteamFarm { if (!WebLimitingSemaphores.TryGetValue(service, out (SemaphoreSlim RateLimitingSemaphore, SemaphoreSlim OpenConnectionsSemaphore) limiters)) { ASF.ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(service), service)); + return await function().ConfigureAwait(false); } diff --git a/ArchiSteamFarm/Bot.cs b/ArchiSteamFarm/Bot.cs index 95fdfe30a..8ba7ab93f 100755 --- a/ArchiSteamFarm/Bot.cs +++ b/ArchiSteamFarm/Bot.cs @@ -272,6 +272,7 @@ namespace ArchiSteamFarm { internal async Task AddGamesToRedeemInBackground(IOrderedDictionary gamesToRedeemInBackground) { if ((gamesToRedeemInBackground == null) || (gamesToRedeemInBackground.Count == 0)) { ArchiLogger.LogNullError(nameof(gamesToRedeemInBackground)); + return; } @@ -313,6 +314,7 @@ namespace ArchiSteamFarm { return true; } catch (Exception e) { ArchiLogger.LogGenericException(e); + return false; } } @@ -330,6 +332,7 @@ namespace ArchiSteamFarm { return true; } catch (Exception e) { ArchiLogger.LogGenericException(e); + return false; } } @@ -337,6 +340,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; } @@ -346,11 +350,13 @@ namespace ArchiSteamFarm { internal async Task<(uint PlayableAppID, DateTime IgnoredUntil)> GetAppDataForIdling(uint appID, float hoursPlayed, bool allowRecursiveDiscovery = true, bool optimisticDiscovery = true) { if ((appID == 0) || (hoursPlayed < 0)) { ArchiLogger.LogNullError(nameof(appID) + " || " + nameof(hoursPlayed)); + return (0, DateTime.MaxValue); } if ((hoursPlayed < CardsFarmer.HoursForRefund) && !BotConfig.IdleRefundableGames) { HashSet packageIDs = Program.GlobalDatabase.GetPackageIDs(appID); + if (packageIDs == null) { return (0, DateTime.MaxValue); } @@ -370,6 +376,7 @@ namespace ArchiSteamFarm { if (mostRecent > DateTime.MinValue) { DateTime playableIn = mostRecent.AddDays(CardsFarmer.DaysForRefund); + if (playableIn > DateTime.UtcNow) { return (0, playableIn); } @@ -401,32 +408,40 @@ namespace ArchiSteamFarm { } KeyValue productInfo = productInfoApp.KeyValues; + if (productInfo == KeyValue.Invalid) { ArchiLogger.LogNullError(nameof(productInfo)); + break; } KeyValue commonProductInfo = productInfo["common"]; + if (commonProductInfo == KeyValue.Invalid) { continue; } string releaseState = commonProductInfo["ReleaseState"].Value; + 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()) { case "RELEASED": + break; case "PRELOADONLY": case "PRERELEASE": + return (0, DateTime.MaxValue); default: ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(releaseState), releaseState)); + break; } } string type = commonProductInfo["type"].Value; + if (string.IsNullOrEmpty(type)) { return (appID, DateTime.MinValue); } @@ -442,6 +457,7 @@ namespace ArchiSteamFarm { case "SERIES": case "TOOL": case "VIDEO": + return (appID, DateTime.MinValue); // Types that can't be idled @@ -450,9 +466,11 @@ namespace ArchiSteamFarm { case "DLC": case "GUIDE": case "HARDWARE": + break; default: ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(type), type)); + break; } @@ -461,6 +479,7 @@ namespace ArchiSteamFarm { } string listOfDlc = productInfo["extended"]["listofdlc"].Value; + if (string.IsNullOrEmpty(listOfDlc)) { return (appID, DateTime.MinValue); } @@ -470,10 +489,12 @@ namespace ArchiSteamFarm { foreach (string dlcAppIDsText in dlcAppIDsTexts) { if (!uint.TryParse(dlcAppIDsText, out uint dlcAppID) || (dlcAppID == 0)) { ArchiLogger.LogNullError(nameof(dlcAppID)); + break; } (uint playableAppID, _) = await GetAppDataForIdling(dlcAppID, hoursPlayed, false, false).ConfigureAwait(false); + if (playableAppID != 0) { return (playableAppID, DateTime.MinValue); } @@ -488,12 +509,14 @@ namespace ArchiSteamFarm { internal static HashSet GetBots(string args) { if (string.IsNullOrEmpty(args)) { ASF.ArchiLogger.LogNullError(nameof(args)); + return null; } string[] botNames = args.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); HashSet result = new HashSet(); + foreach (string botName in botNames) { if (botName.Equals(SharedInfo.ASF, StringComparison.OrdinalIgnoreCase)) { foreach (Bot bot in Bots.OrderBy(bot => bot.Key).Select(bot => bot.Value)) { @@ -505,6 +528,7 @@ namespace ArchiSteamFarm { if (botName.Contains("..")) { string[] botRange = botName.Split(new[] { ".." }, StringSplitOptions.RemoveEmptyEntries); + if (botRange.Length == 2) { if (Bots.TryGetValue(botRange[0], out Bot firstBot) && Bots.TryGetValue(botRange[1], out Bot lastBot)) { bool inRange = false; @@ -534,6 +558,7 @@ namespace ArchiSteamFarm { result.UnionWith(regexMatches); } catch (ArgumentException e) { ASF.ArchiLogger.LogGenericWarningException(e); + return null; } } @@ -553,6 +578,7 @@ namespace ArchiSteamFarm { internal async Task AppIDs)>> GetPackagesData(IReadOnlyCollection packageIDs) { if ((packageIDs == null) || (packageIDs.Count == 0)) { ArchiLogger.LogNullError(nameof(packageIDs)); + return null; } @@ -579,6 +605,7 @@ namespace ArchiSteamFarm { 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) { ArchiLogger.LogNullError(nameof(productInfo)); + return null; } @@ -586,6 +613,7 @@ namespace ArchiSteamFarm { try { KeyValue appIDs = productInfo.KeyValues["appids"]; + if (appIDs == KeyValue.Invalid) { continue; } @@ -595,6 +623,7 @@ namespace ArchiSteamFarm { foreach (string appIDText in appIDs.Children.Select(app => app.Value)) { if (!uint.TryParse(appIDText, out uint appID) || (appID == 0)) { ArchiLogger.LogNullError(nameof(appID)); + return null; } @@ -611,6 +640,7 @@ namespace ArchiSteamFarm { internal BotConfig.EPermission GetSteamUserPermission(ulong steamID) { if (steamID == 0) { ArchiLogger.LogNullError(nameof(steamID)); + return BotConfig.EPermission.None; } @@ -620,6 +650,7 @@ namespace ArchiSteamFarm { internal async Task GetTradeHoldDuration(ulong steamID, ulong tradeID) { if ((steamID == 0) || (tradeID == 0)) { ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(tradeID)); + return null; } @@ -628,8 +659,10 @@ namespace ArchiSteamFarm { } Bot targetBot = Bots.Values.FirstOrDefault(bot => bot.SteamID == steamID); + if (targetBot != null) { string targetTradeToken = await targetBot.ArchiHandler.GetTradeToken().ConfigureAwait(false); + if (!string.IsNullOrEmpty(targetTradeToken)) { return await ArchiWebHandler.GetTradeHoldDurationForUser(steamID, targetTradeToken).ConfigureAwait(false); } @@ -640,12 +673,14 @@ namespace ArchiSteamFarm { internal async Task<(Dictionary UnusedKeys, Dictionary UsedKeys)> GetUsedAndUnusedKeys() { IList> results = await Utilities.InParallel(new[] { KeysToRedeemUnusedFilePath, KeysToRedeemUsedFilePath }.Select(GetKeysFromFile)).ConfigureAwait(false); + return (results[0], results[1]); } internal async Task IdleGame(CardsFarmer.Game game) { if (game == null) { ArchiLogger.LogNullError(nameof(game)); + return; } @@ -655,6 +690,7 @@ namespace ArchiSteamFarm { internal async Task IdleGames(IReadOnlyCollection games) { if ((games == null) || (games.Count == 0)) { ArchiLogger.LogNullError(nameof(games)); + return; } @@ -664,6 +700,7 @@ namespace ArchiSteamFarm { internal async Task ImportKeysToRedeem(string filePath) { if (string.IsNullOrEmpty(filePath) || !File.Exists(filePath)) { ArchiLogger.LogNullError(nameof(filePath)); + return; } @@ -686,6 +723,7 @@ namespace ArchiSteamFarm { if (parsedArgs.Length < 1) { ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorIsInvalid, line)); + continue; } @@ -713,6 +751,7 @@ namespace ArchiSteamFarm { internal static async Task InitializeSteamConfiguration(ProtocolTypes protocolTypes, uint cellID, IServerListProvider serverListProvider) { if (serverListProvider == null) { ASF.ArchiLogger.LogNullError(nameof(serverListProvider)); + return; } @@ -720,6 +759,7 @@ namespace ArchiSteamFarm { // Ensure that we ask for a list of servers if we don't have any saved servers available IEnumerable servers = await SteamConfiguration.ServerListProvider.FetchServerListAsync().ConfigureAwait(false); + if (servers?.Any() != true) { ASF.ArchiLogger.LogGenericInfo(string.Format(Strings.Initializing, nameof(SteamDirectory))); @@ -735,6 +775,7 @@ namespace ArchiSteamFarm { internal bool IsBlacklistedFromIdling(uint appID) { if (appID == 0) { ArchiLogger.LogNullError(nameof(appID)); + return false; } @@ -744,6 +785,7 @@ namespace ArchiSteamFarm { internal bool IsBlacklistedFromTrades(ulong steamID) { if (steamID == 0) { ArchiLogger.LogNullError(nameof(steamID)); + return false; } @@ -753,6 +795,7 @@ namespace ArchiSteamFarm { internal bool IsFamilySharing(ulong steamID) { if (steamID == 0) { ArchiLogger.LogNullError(nameof(steamID)); + return false; } @@ -762,6 +805,7 @@ namespace ArchiSteamFarm { internal bool IsMaster(ulong steamID) { if (steamID == 0) { ArchiLogger.LogNullError(nameof(steamID)); + return false; } @@ -771,6 +815,7 @@ namespace ArchiSteamFarm { internal bool IsPriorityIdling(uint appID) { if (appID == 0) { ArchiLogger.LogNullError(nameof(appID)); + return false; } @@ -780,6 +825,7 @@ namespace ArchiSteamFarm { internal async Task OnConfigChanged(bool deleted) { if (deleted) { Destroy(); + return; } @@ -787,6 +833,7 @@ namespace ArchiSteamFarm { if (botConfig == null) { Destroy(); + return; } @@ -825,6 +872,7 @@ namespace ArchiSteamFarm { if (BotConfig.ShutdownOnFarmingFinished) { if (farmedSomething || (Program.GlobalConfig.IdleFarmingPeriod == 0)) { Stop(); + return; } @@ -850,11 +898,13 @@ namespace ArchiSteamFarm { } catch (Exception e) { ArchiLogger.LogGenericWarningException(e); await Connect(true).ConfigureAwait(false); + return false; } if (string.IsNullOrEmpty(callback?.Nonce)) { await Connect(true).ConfigureAwait(false); + return false; } @@ -863,12 +913,14 @@ namespace ArchiSteamFarm { } await Connect(true).ConfigureAwait(false); + return false; } internal static async Task RegisterBot(string botName) { if (string.IsNullOrEmpty(botName)) { ASF.ArchiLogger.LogNullError(nameof(botName)); + return; } @@ -880,8 +932,10 @@ namespace ArchiSteamFarm { string configFilePath = botPath + SharedInfo.ConfigExtension; BotConfig botConfig = await BotConfig.Load(botPath + SharedInfo.ConfigExtension).ConfigureAwait(false); + if (botConfig == null) { ASF.ArchiLogger.LogGenericError(string.Format(Strings.ErrorBotConfigInvalid, configFilePath)); + return; } @@ -892,8 +946,10 @@ namespace ArchiSteamFarm { string databaseFilePath = botPath + SharedInfo.DatabaseExtension; BotDatabase botDatabase = await BotDatabase.CreateOrLoad(databaseFilePath).ConfigureAwait(false); + if (botDatabase == null) { ASF.ArchiLogger.LogGenericError(string.Format(Strings.ErrorDatabaseInvalid, databaseFilePath)); + return; } @@ -928,6 +984,7 @@ namespace ArchiSteamFarm { internal async Task SendMessage(ulong steamID, string message) { if ((steamID == 0) || string.IsNullOrEmpty(message)) { ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(message)); + return false; } @@ -965,19 +1022,23 @@ namespace ArchiSteamFarm { switch (result) { case EResult.OK: sent = true; + break; case EResult.RateLimitExceeded: case EResult.Timeout: await Task.Delay(5000).ConfigureAwait(false); + continue; default: ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(result), result)); + return false; } } if (!sent) { ArchiLogger.LogGenericWarning(Strings.WarningFailed); + return false; } } finally { @@ -991,6 +1052,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; } @@ -1028,19 +1090,23 @@ namespace ArchiSteamFarm { switch (result) { case EResult.OK: sent = true; + break; case EResult.RateLimitExceeded: case EResult.Timeout: await Task.Delay(5000).ConfigureAwait(false); + continue; default: ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(result), result)); + return false; } } if (!sent) { ArchiLogger.LogGenericWarning(Strings.WarningFailed); + return false; } } finally { @@ -1060,14 +1126,17 @@ namespace ArchiSteamFarm { switch (inputType) { case ASF.EUserInputType.DeviceID: DeviceID = inputValue; + break; case ASF.EUserInputType.Login: + if (BotConfig != null) { BotConfig.SteamLogin = inputValue; } break; case ASF.EUserInputType.Password: + if (BotConfig != null) { BotConfig.DecryptedSteamPassword = inputValue; } @@ -1075,8 +1144,10 @@ namespace ArchiSteamFarm { break; case ASF.EUserInputType.SteamGuard: AuthCode = inputValue; + break; case ASF.EUserInputType.SteamParentalCode: + if (BotConfig != null) { BotConfig.SteamParentalCode = inputValue; } @@ -1084,9 +1155,11 @@ namespace ArchiSteamFarm { break; case ASF.EUserInputType.TwoFactorAuthentication: TwoFactorCode = inputValue; + break; default: ASF.ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(inputType), inputType)); + break; } } @@ -1132,6 +1205,7 @@ namespace ArchiSteamFarm { internal static IOrderedDictionary ValidateGamesToRedeemInBackground(IOrderedDictionary gamesToRedeemInBackground) { if ((gamesToRedeemInBackground == null) || (gamesToRedeemInBackground.Count == 0)) { ASF.ArchiLogger.LogNullError(nameof(gamesToRedeemInBackground)); + return null; } @@ -1141,6 +1215,7 @@ namespace ArchiSteamFarm { bool invalid = false; string key = game.Key as string; + if (string.IsNullOrEmpty(key)) { invalid = true; ASF.ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorIsInvalid, nameof(key))); @@ -1150,6 +1225,7 @@ namespace ArchiSteamFarm { } string name = game.Value as string; + if (string.IsNullOrEmpty(name)) { invalid = true; ASF.ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorIsInvalid, nameof(name))); @@ -1175,6 +1251,7 @@ namespace ArchiSteamFarm { if (!IsPlayingPossible) { ArchiLogger.LogGenericInfo(Strings.BotAccountOccupied); PlayingWasBlocked = true; + return; } @@ -1221,6 +1298,7 @@ namespace ArchiSteamFarm { private static string Escape(string message) { if (string.IsNullOrEmpty(message)) { ASF.ArchiLogger.LogNullError(nameof(message)); + return null; } @@ -1230,6 +1308,7 @@ namespace ArchiSteamFarm { private async Task> GetKeysFromFile(string filePath) { if (string.IsNullOrEmpty(filePath)) { ArchiLogger.LogNullError(nameof(filePath)); + return null; } @@ -1249,14 +1328,18 @@ namespace ArchiSteamFarm { } string[] parsedArgs = line.Split(DefaultBackgroundKeysRedeemerSeparator, StringSplitOptions.RemoveEmptyEntries); + if (parsedArgs.Length < 3) { ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorIsInvalid, line)); + continue; } string key = parsedArgs[parsedArgs.Length - 1]; + if (!Utilities.IsValidCdKey(key)) { ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorIsInvalid, key)); + continue; } @@ -1268,12 +1351,14 @@ namespace ArchiSteamFarm { return keys; } catch (Exception e) { ArchiLogger.LogGenericException(e); + return null; } } private void HandleCallbacks() { TimeSpan timeSpan = TimeSpan.FromMilliseconds(CallbackSleep); + while (KeepRunning || SteamClient.IsConnected) { if (!CallbackSemaphore.Wait(0)) { if (Debugging.IsUserDebugging) { @@ -1296,6 +1381,7 @@ namespace ArchiSteamFarm { private async Task HandleMessage(ulong chatGroupID, ulong chatID, ulong steamID, string message) { if ((chatGroupID == 0) || (chatID == 0) || (steamID == 0) || string.IsNullOrEmpty(message)) { ArchiLogger.LogNullError(nameof(chatGroupID) + " || " + nameof(chatID) + " || " + nameof(steamID) + " || " + nameof(message)); + return; } @@ -1312,6 +1398,7 @@ namespace ArchiSteamFarm { private async Task HandleMessage(ulong steamID, string message) { if ((steamID == 0) || string.IsNullOrEmpty(message)) { ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(message)); + return; } @@ -1368,11 +1455,13 @@ namespace ArchiSteamFarm { File.Delete(maFilePath); } catch (Exception e) { ArchiLogger.LogGenericException(e); + return; } if (BotDatabase.MobileAuthenticator == null) { ArchiLogger.LogNullError(nameof(BotDatabase.MobileAuthenticator)); + return; } @@ -1380,10 +1469,13 @@ namespace ArchiSteamFarm { if (!BotDatabase.MobileAuthenticator.HasCorrectDeviceID) { ArchiLogger.LogGenericWarning(Strings.BotAuthenticatorInvalidDeviceID); + if (string.IsNullOrEmpty(DeviceID)) { string deviceID = Program.GetUserInput(ASF.EUserInputType.DeviceID, BotName); + if (string.IsNullOrEmpty(deviceID)) { await BotDatabase.SetMobileAuthenticator().ConfigureAwait(false); + return; } @@ -1411,6 +1503,7 @@ namespace ArchiSteamFarm { private async Task InitializeFamilySharing() { HashSet steamIDs = await ArchiWebHandler.GetFamilySharingSteamIDs().ConfigureAwait(false); + if ((steamIDs == null) || (steamIDs.Count == 0)) { return; } @@ -1421,6 +1514,7 @@ namespace ArchiSteamFarm { private bool InitLoginAndPassword(bool requiresPassword) { if (string.IsNullOrEmpty(BotConfig.SteamLogin)) { string steamLogin = Program.GetUserInput(ASF.EUserInputType.Login, BotName); + if (string.IsNullOrEmpty(steamLogin)) { return false; } @@ -1430,6 +1524,7 @@ namespace ArchiSteamFarm { if (requiresPassword && string.IsNullOrEmpty(BotConfig.DecryptedSteamPassword)) { string steamPassword = Program.GetUserInput(ASF.EUserInputType.Password, BotName); + if (string.IsNullOrEmpty(steamPassword)) { return false; } @@ -1480,6 +1575,7 @@ namespace ArchiSteamFarm { private void InitStart() { if (!BotConfig.Enabled) { ArchiLogger.LogGenericInfo(Strings.BotInstanceNotStartingBecauseDisabled); + return; } @@ -1490,6 +1586,7 @@ namespace ArchiSteamFarm { private bool IsMasterClanID(ulong steamID) { if (steamID == 0) { ArchiLogger.LogNullError(nameof(steamID)); + return false; } @@ -1499,6 +1596,7 @@ namespace ArchiSteamFarm { private static bool IsRefundable(EPaymentMethod method) { if (method == EPaymentMethod.None) { ASF.ArchiLogger.LogNullError(nameof(method)); + return false; } @@ -1507,8 +1605,10 @@ namespace ArchiSteamFarm { case EPaymentMethod.Complimentary: // This is also a flag case EPaymentMethod.GuestPass: case EPaymentMethod.HardwarePromo: + return false; default: + if (method.HasFlag(EPaymentMethod.Complimentary)) { return false; } @@ -1545,6 +1645,7 @@ namespace ArchiSteamFarm { } await LoginSemaphore.WaitAsync().ConfigureAwait(false); + Utilities.InBackground( async () => { await Task.Delay(Program.GlobalConfig.LoginLimiterDelay * 1000).ConfigureAwait(false); @@ -1556,6 +1657,7 @@ namespace ArchiSteamFarm { private async void OnConnected(SteamClient.ConnectedCallback callback) { if (callback == null) { ArchiLogger.LogNullError(nameof(callback)); + return; } @@ -1568,6 +1670,7 @@ namespace ArchiSteamFarm { if (!KeepRunning) { ArchiLogger.LogGenericInfo(Strings.BotDisconnecting); Disconnect(); + return; } @@ -1607,6 +1710,7 @@ namespace ArchiSteamFarm { if (!InitLoginAndPassword(string.IsNullOrEmpty(loginKey))) { Stop(); + return; } @@ -1614,8 +1718,8 @@ namespace ArchiSteamFarm { const string nonAsciiPattern = @"[^\u0000-\u007F]+"; string username = Regex.Replace(BotConfig.SteamLogin, nonAsciiPattern, "", RegexOptions.CultureInvariant | RegexOptions.IgnoreCase); - string password = BotConfig.DecryptedSteamPassword; + if (!string.IsNullOrEmpty(password)) { password = Regex.Replace(password, nonAsciiPattern, "", RegexOptions.CultureInvariant | RegexOptions.IgnoreCase); } @@ -1647,6 +1751,7 @@ namespace ArchiSteamFarm { private async void OnDisconnected(SteamClient.DisconnectedCallback callback) { if (callback == null) { ArchiLogger.LogNullError(nameof(callback)); + return; } @@ -1672,14 +1777,18 @@ namespace ArchiSteamFarm { switch (lastLogOnResult) { case EResult.AccountDisabled: + // Do not attempt to reconnect, those failures are permanent return; case EResult.Invalid: + // Invalid means that we didn't get OnLoggedOn() in the first place, so Steam is down // Always reset one-time-only access tokens in this case, as OnLoggedOn() didn't do that for us AuthCode = TwoFactorCode = null; + break; case EResult.InvalidPassword: + // If we didn't use login key, it's nearly always rate limiting if (string.IsNullOrEmpty(BotDatabase.LoginKey)) { goto case EResult.RateLimitExceeded; @@ -1687,16 +1796,19 @@ namespace ArchiSteamFarm { await BotDatabase.SetLoginKey().ConfigureAwait(false); ArchiLogger.LogGenericInfo(Strings.BotRemovedExpiredLoginKey); + break; case EResult.NoConnection: case EResult.ServiceUnavailable: case EResult.Timeout: case EResult.TryAnotherCM: await Task.Delay(5000).ConfigureAwait(false); + break; case EResult.RateLimitExceeded: ArchiLogger.LogGenericInfo(string.Format(Strings.BotRateLimitExceeded, TimeSpan.FromMinutes(LoginCooldownInMinutes).ToHumanReadable())); await Task.Delay(LoginCooldownInMinutes * 60 * 1000).ConfigureAwait(false); + break; } @@ -1711,6 +1823,7 @@ namespace ArchiSteamFarm { private async void OnFriendsList(SteamFriends.FriendsListCallback callback) { if (callback?.FriendList == null) { ArchiLogger.LogNullError(nameof(callback) + " || " + nameof(callback.FriendList)); + return; } @@ -1719,14 +1832,17 @@ namespace ArchiSteamFarm { case EAccountType.Clan when IsMasterClanID(friend.SteamID): ArchiHandler.AcknowledgeClanInvite(friend.SteamID, true); await JoinMasterChatGroupID().ConfigureAwait(false); + break; case EAccountType.Clan: + if (BotConfig.BotBehaviour.HasFlag(BotConfig.EBotBehaviour.RejectInvalidGroupInvites)) { ArchiHandler.AcknowledgeClanInvite(friend.SteamID, false); } break; default: + if (IsFamilySharing(friend.SteamID)) { await ArchiHandler.AddFriend(friend.SteamID).ConfigureAwait(false); } else if (BotConfig.BotBehaviour.HasFlag(BotConfig.EBotBehaviour.RejectInvalidFriendInvites)) { @@ -1741,6 +1857,7 @@ namespace ArchiSteamFarm { private async void OnGuestPassList(SteamApps.GuestPassListCallback callback) { if (callback?.GuestPasses == null) { ArchiLogger.LogNullError(nameof(callback) + " || " + nameof(callback.GuestPasses)); + return; } @@ -1749,6 +1866,7 @@ namespace ArchiSteamFarm { } HashSet guestPassIDs = callback.GuestPasses.Select(guestPass => guestPass["gid"].AsUnsignedLong()).Where(gid => gid != 0).ToHashSet(); + if (guestPassIDs.Count == 0) { return; } @@ -1759,6 +1877,7 @@ namespace ArchiSteamFarm { private async Task OnIncomingChatMessage(CChatRoom_IncomingChatMessage_Notification notification) { if (notification == null) { ArchiLogger.LogNullError(nameof(notification)); + return; } @@ -1794,6 +1913,7 @@ namespace ArchiSteamFarm { private async Task OnIncomingMessage(CFriendMessages_IncomingMessage_Notification notification) { if (notification == null) { ArchiLogger.LogNullError(nameof(notification)); + return; } @@ -1833,6 +1953,7 @@ namespace ArchiSteamFarm { private async void OnLicenseList(SteamApps.LicenseListCallback callback) { if (callback?.LicenseList == null) { ArchiLogger.LogNullError(nameof(callback) + " || " + nameof(callback.LicenseList)); + return; } @@ -1881,6 +2002,7 @@ namespace ArchiSteamFarm { private void OnLoggedOff(SteamUser.LoggedOffCallback callback) { if (callback == null) { ArchiLogger.LogNullError(nameof(callback)); + return; } @@ -1890,8 +2012,10 @@ namespace ArchiSteamFarm { switch (callback.Result) { case EResult.LoggedInElsewhere: + // This result directly indicates that playing was blocked when we got (forcefully) disconnected PlayingWasBlocked = true; + break; case EResult.LogonSessionReplaced: DateTime now = DateTime.UtcNow; @@ -1899,10 +2023,12 @@ namespace ArchiSteamFarm { if (now.Subtract(LastLogonSessionReplaced).TotalHours < 1) { ArchiLogger.LogGenericError(Strings.BotLogonSessionReplaced); Stop(); + return; } LastLogonSessionReplaced = now; + break; } @@ -1913,6 +2039,7 @@ namespace ArchiSteamFarm { private async void OnLoggedOn(SteamUser.LoggedOnCallback callback) { if (callback == null) { ArchiLogger.LogNullError(nameof(callback)); + return; } @@ -1927,24 +2054,32 @@ namespace ArchiSteamFarm { switch (callback.Result) { case EResult.AccountDisabled: + // Those failures are permanent, we should Stop() the bot if any of those happen ArchiLogger.LogGenericWarning(string.Format(Strings.BotUnableToLogin, callback.Result, callback.ExtendedResult)); Stop(); + break; case EResult.AccountLogonDenied: string authCode = Program.GetUserInput(ASF.EUserInputType.SteamGuard, BotName); + if (string.IsNullOrEmpty(authCode)) { Stop(); + break; } SetUserInput(ASF.EUserInputType.SteamGuard, authCode); + break; case EResult.AccountLoginDeniedNeedTwoFactor: + if (!HasMobileAuthenticator) { string twoFactorCode = Program.GetUserInput(ASF.EUserInputType.TwoFactorAuthentication, BotName); + if (string.IsNullOrEmpty(twoFactorCode)) { Stop(); + break; } @@ -1986,6 +2121,7 @@ namespace ArchiSteamFarm { if (!HasMobileAuthenticator) { // Support and convert 2FA files string maFilePath = Path.Combine(SharedInfo.ConfigDirectory, callback.ClientSteamID.ConvertToUInt64() + ".maFile"); + if (File.Exists(maFilePath)) { await ImportAuthenticator(maFilePath).ConfigureAwait(false); } @@ -1993,8 +2129,10 @@ namespace ArchiSteamFarm { if (!string.IsNullOrEmpty(BotConfig.SteamParentalCode) && (BotConfig.SteamParentalCode.Length != 4)) { string steamParentalCode = Program.GetUserInput(ASF.EUserInputType.SteamParentalCode, BotName); + if (string.IsNullOrEmpty(steamParentalCode) || (steamParentalCode.Length != 4)) { Stop(); + break; } @@ -2065,9 +2203,11 @@ namespace ArchiSteamFarm { break; default: + // Unexpected result, shutdown immediately ArchiLogger.LogGenericError(string.Format(Strings.BotUnableToLogin, callback.Result, callback.ExtendedResult)); Stop(); + break; } } @@ -2075,6 +2215,7 @@ namespace ArchiSteamFarm { private async void OnLoginKey(SteamUser.LoginKeyCallback callback) { if (string.IsNullOrEmpty(callback?.LoginKey)) { ArchiLogger.LogNullError(nameof(callback) + " || " + nameof(callback.LoginKey)); + return; } @@ -2083,6 +2224,7 @@ namespace ArchiSteamFarm { } string loginKey = callback.LoginKey; + if (BotConfig.PasswordFormat != ArchiCryptoHelper.ECryptoMethod.PlainText) { loginKey = ArchiCryptoHelper.Encrypt(BotConfig.PasswordFormat, loginKey); } @@ -2094,6 +2236,7 @@ namespace ArchiSteamFarm { private void OnMachineAuth(SteamUser.UpdateMachineAuthCallback callback) { if (callback == null) { ArchiLogger.LogNullError(nameof(callback)); + return; } @@ -2107,6 +2250,7 @@ namespace ArchiSteamFarm { fileSize = (int) fileStream.Length; fileStream.Seek(0, SeekOrigin.Begin); + using (SHA1CryptoServiceProvider sha = new SHA1CryptoServiceProvider()) { sentryHash = sha.ComputeHash(fileStream); } @@ -2142,6 +2286,7 @@ namespace ArchiSteamFarm { private async void OnPersonaState(SteamFriends.PersonaStateCallback callback) { if (callback == null) { ArchiLogger.LogNullError(nameof(callback)); + return; } @@ -2171,6 +2316,7 @@ namespace ArchiSteamFarm { private async void OnPlayingSessionState(ArchiHandler.PlayingSessionStateCallback callback) { if (callback == null) { ArchiLogger.LogNullError(nameof(callback)); + return; } @@ -2185,15 +2331,18 @@ namespace ArchiSteamFarm { private async void OnServiceMethod(SteamUnifiedMessages.ServiceMethodNotification notification) { if (notification == null) { ArchiLogger.LogNullError(nameof(notification)); + return; } switch (notification.MethodName) { case "ChatRoomClient.NotifyIncomingChatMessage#1": await OnIncomingChatMessage((CChatRoom_IncomingChatMessage_Notification) notification.Body).ConfigureAwait(false); + break; case "FriendMessagesClient.IncomingMessage#1": await OnIncomingMessage((CFriendMessages_IncomingMessage_Notification) notification.Body).ConfigureAwait(false); + break; } } @@ -2201,6 +2350,7 @@ namespace ArchiSteamFarm { private async void OnSharedLibraryLockStatus(ArchiHandler.SharedLibraryLockStatusCallback callback) { if (callback == null) { ArchiLogger.LogNullError(nameof(callback)); + return; } @@ -2229,6 +2379,7 @@ namespace ArchiSteamFarm { private void OnUserNotifications(ArchiHandler.UserNotificationsCallback callback) { if (callback == null) { ArchiLogger.LogNullError(nameof(callback)); + return; } @@ -2279,6 +2430,7 @@ namespace ArchiSteamFarm { private void OnVanityURLChangedCallback(ArchiHandler.VanityURLChangedCallback callback) { if (callback == null) { ArchiLogger.LogNullError(nameof(callback)); + return; } @@ -2288,6 +2440,7 @@ namespace ArchiSteamFarm { private void OnWalletUpdate(SteamUser.WalletInfoCallback callback) { if (callback == null) { ArchiLogger.LogNullError(nameof(callback)); + return; } @@ -2311,12 +2464,15 @@ namespace ArchiSteamFarm { while (IsConnectedAndLoggedOn && BotDatabase.HasGamesToRedeemInBackground) { (string key, string name) = BotDatabase.GetGameToRedeemInBackground(); + if (string.IsNullOrEmpty(key) || string.IsNullOrEmpty(name)) { ArchiLogger.LogNullError(nameof(key) + " || " + nameof(name)); + break; } ArchiHandler.PurchaseResponseCallback result = await Actions.RedeemKey(key).ConfigureAwait(false); + if (result == null) { continue; } @@ -2346,17 +2502,21 @@ namespace ArchiSteamFarm { case EPurchaseResultDetail.DoesNotOwnRequiredApp: case EPurchaseResultDetail.RestrictedCountry: case EPurchaseResultDetail.Timeout: + break; case EPurchaseResultDetail.BadActivationCode: case EPurchaseResultDetail.DuplicateActivationCode: case EPurchaseResultDetail.NoDetail: // OK redeemed = true; + break; case EPurchaseResultDetail.RateLimited: rateLimited = true; + break; default: ASF.ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(result.PurchaseResultDetail), result.PurchaseResultDetail)); + break; } @@ -2378,6 +2538,7 @@ namespace ArchiSteamFarm { } catch (Exception e) { ArchiLogger.LogGenericException(e); ArchiLogger.LogGenericError(string.Format(Strings.Content, logEntry)); + break; } } @@ -2442,6 +2603,7 @@ namespace ArchiSteamFarm { private static string UnEscape(string message) { if (string.IsNullOrEmpty(message)) { ASF.ArchiLogger.LogNullError(nameof(message)); + return null; } diff --git a/ArchiSteamFarm/BotConfig.cs b/ArchiSteamFarm/BotConfig.cs index 1dd85a4e9..d3f1d9202 100644 --- a/ArchiSteamFarm/BotConfig.cs +++ b/ArchiSteamFarm/BotConfig.cs @@ -153,8 +153,10 @@ namespace ArchiSteamFarm { } string decryptedPassword = ArchiCryptoHelper.Decrypt(PasswordFormat, SteamPassword); + if (string.IsNullOrEmpty(decryptedPassword)) { ASF.ArchiLogger.LogGenericError(string.Format(Strings.ErrorIsInvalid, nameof(SteamPassword))); + return null; } @@ -163,6 +165,7 @@ namespace ArchiSteamFarm { set { if (string.IsNullOrEmpty(value)) { ASF.ArchiLogger.LogNullError(nameof(value)); + return; } @@ -179,6 +182,7 @@ namespace ArchiSteamFarm { [JsonProperty] internal string SteamLogin { get => _SteamLogin; + set { IsSteamLoginSet = true; _SteamLogin = value; @@ -191,6 +195,7 @@ namespace ArchiSteamFarm { [JsonProperty] internal string SteamParentalCode { get => _SteamParentalCode; + set { IsSteamParentalCodeSet = true; _SteamParentalCode = value; @@ -200,6 +205,7 @@ namespace ArchiSteamFarm { [JsonProperty] internal string SteamPassword { get => _SteamPassword; + set { IsSteamPasswordSet = true; _SteamPassword = value; @@ -214,9 +220,11 @@ namespace ArchiSteamFarm { [JsonProperty(PropertyName = SharedInfo.UlongCompatibilityStringPrefix + nameof(SteamMasterClanID), Required = Required.DisallowNull)] private string SSteamMasterClanID { get => SteamMasterClanID.ToString(); + set { if (string.IsNullOrEmpty(value) || !ulong.TryParse(value, out ulong result)) { ASF.ArchiLogger.LogGenericError(string.Format(Strings.ErrorIsInvalid, nameof(SSteamMasterClanID))); + return; } @@ -281,6 +289,7 @@ namespace ArchiSteamFarm { internal static async Task Load(string filePath) { if (string.IsNullOrEmpty(filePath)) { ASF.ArchiLogger.LogNullError(nameof(filePath)); + return null; } @@ -294,28 +303,34 @@ namespace ArchiSteamFarm { botConfig = JsonConvert.DeserializeObject(await RuntimeCompatibility.File.ReadAllTextAsync(filePath).ConfigureAwait(false)); } catch (Exception e) { ASF.ArchiLogger.LogGenericException(e); + return null; } if (botConfig == null) { ASF.ArchiLogger.LogNullError(nameof(botConfig)); + return null; } (bool valid, string errorMessage) = botConfig.CheckValidation(); + if (!valid) { ASF.ArchiLogger.LogGenericError(errorMessage); + return null; } botConfig.ShouldSerializeEverything = false; botConfig.ShouldSerializeSensitiveDetails = false; + return botConfig; } internal static async Task Write(string filePath, BotConfig botConfig) { if (string.IsNullOrEmpty(filePath) || (botConfig == null)) { ASF.ArchiLogger.LogNullError(nameof(filePath) + " || " + nameof(botConfig)); + return false; } @@ -334,6 +349,7 @@ namespace ArchiSteamFarm { } } catch (Exception e) { ASF.ArchiLogger.LogGenericException(e); + return false; } finally { WriteSemaphore.Release(); diff --git a/ArchiSteamFarm/BotDatabase.cs b/ArchiSteamFarm/BotDatabase.cs index c60269b65..7b6ba4987 100644 --- a/ArchiSteamFarm/BotDatabase.cs +++ b/ArchiSteamFarm/BotDatabase.cs @@ -79,6 +79,7 @@ namespace ArchiSteamFarm { internal async Task AddBlacklistedFromTradesSteamIDs(IReadOnlyCollection steamIDs) { if ((steamIDs == null) || (steamIDs.Count == 0)) { ASF.ArchiLogger.LogNullError(nameof(steamIDs)); + return; } @@ -90,6 +91,7 @@ namespace ArchiSteamFarm { internal async Task AddGamesToRedeemInBackground(IOrderedDictionary games) { if ((games == null) || (games.Count == 0)) { ASF.ArchiLogger.LogNullError(nameof(games)); + return; } @@ -114,6 +116,7 @@ namespace ArchiSteamFarm { internal async Task AddIdlingBlacklistedAppIDs(IReadOnlyCollection appIDs) { if ((appIDs == null) || (appIDs.Count == 0)) { ASF.ArchiLogger.LogNullError(nameof(appIDs)); + return; } @@ -125,6 +128,7 @@ namespace ArchiSteamFarm { internal async Task AddIdlingPriorityAppIDs(IReadOnlyCollection appIDs) { if ((appIDs == null) || (appIDs.Count == 0)) { ASF.ArchiLogger.LogNullError(nameof(appIDs)); + return; } @@ -136,6 +140,7 @@ namespace ArchiSteamFarm { internal async Task CorrectMobileAuthenticatorDeviceID(string deviceID) { if (string.IsNullOrEmpty(deviceID) || (MobileAuthenticator == null)) { ASF.ArchiLogger.LogNullError(nameof(deviceID) + " || " + nameof(MobileAuthenticator)); + return; } @@ -147,6 +152,7 @@ namespace ArchiSteamFarm { internal static async Task CreateOrLoad(string filePath) { if (string.IsNullOrEmpty(filePath)) { ASF.ArchiLogger.LogNullError(nameof(filePath)); + return null; } @@ -160,15 +166,18 @@ namespace ArchiSteamFarm { botDatabase = JsonConvert.DeserializeObject(await RuntimeCompatibility.File.ReadAllTextAsync(filePath).ConfigureAwait(false)); } catch (Exception e) { ASF.ArchiLogger.LogGenericException(e); + return null; } if (botDatabase == null) { ASF.ArchiLogger.LogNullError(nameof(botDatabase)); + return null; } botDatabase.FilePath = filePath; + return botDatabase; } @@ -190,6 +199,7 @@ namespace ArchiSteamFarm { internal bool IsBlacklistedFromIdling(uint appID) { if (appID == 0) { ASF.ArchiLogger.LogNullError(nameof(appID)); + return false; } @@ -199,6 +209,7 @@ namespace ArchiSteamFarm { internal bool IsBlacklistedFromTrades(ulong steamID) { if (steamID == 0) { ASF.ArchiLogger.LogNullError(nameof(steamID)); + return false; } @@ -208,6 +219,7 @@ namespace ArchiSteamFarm { internal bool IsPriorityIdling(uint appID) { if (appID == 0) { ASF.ArchiLogger.LogNullError(nameof(appID)); + return false; } @@ -235,6 +247,7 @@ namespace ArchiSteamFarm { internal async Task RemoveBlacklistedFromTradesSteamIDs(IReadOnlyCollection steamIDs) { if ((steamIDs == null) || (steamIDs.Count == 0)) { ASF.ArchiLogger.LogNullError(nameof(steamIDs)); + return; } @@ -246,6 +259,7 @@ namespace ArchiSteamFarm { internal async Task RemoveGameToRedeemInBackground(string key) { if (string.IsNullOrEmpty(key)) { ASF.ArchiLogger.LogNullError(nameof(key)); + return; } @@ -263,6 +277,7 @@ namespace ArchiSteamFarm { internal async Task RemoveIdlingBlacklistedAppIDs(IReadOnlyCollection appIDs) { if ((appIDs == null) || (appIDs.Count == 0)) { ASF.ArchiLogger.LogNullError(nameof(appIDs)); + return; } @@ -274,6 +289,7 @@ namespace ArchiSteamFarm { internal async Task RemoveIdlingPriorityAppIDs(IReadOnlyCollection appIDs) { if ((appIDs == null) || (appIDs.Count == 0)) { ASF.ArchiLogger.LogNullError(nameof(appIDs)); + return; } @@ -306,8 +322,10 @@ namespace ArchiSteamFarm { } string json = JsonConvert.SerializeObject(this); + if (string.IsNullOrEmpty(json)) { ASF.ArchiLogger.LogNullError(nameof(json)); + return; } diff --git a/ArchiSteamFarm/CMsgs/CMsgClientAcknowledgeClanInvite.cs b/ArchiSteamFarm/CMsgs/CMsgClientAcknowledgeClanInvite.cs index 15b7e52e9..55e84234b 100644 --- a/ArchiSteamFarm/CMsgs/CMsgClientAcknowledgeClanInvite.cs +++ b/ArchiSteamFarm/CMsgs/CMsgClientAcknowledgeClanInvite.cs @@ -31,6 +31,7 @@ namespace ArchiSteamFarm.CMsgs { void ISteamSerializable.Deserialize(Stream stream) { if (stream == null) { ASF.ArchiLogger.LogNullError(nameof(stream)); + return; } @@ -44,6 +45,7 @@ namespace ArchiSteamFarm.CMsgs { void ISteamSerializable.Serialize(Stream stream) { if (stream == null) { ASF.ArchiLogger.LogNullError(nameof(stream)); + return; } diff --git a/ArchiSteamFarm/CardsFarmer.cs b/ArchiSteamFarm/CardsFarmer.cs index cc96765cc..f8c94c61e 100755 --- a/ArchiSteamFarm/CardsFarmer.cs +++ b/ArchiSteamFarm/CardsFarmer.cs @@ -135,6 +135,7 @@ namespace ArchiSteamFarm { // If we're not farming yet, obviously it's worth it to make a check if (!NowFarming) { await StartFarming().ConfigureAwait(false); + return; } @@ -192,6 +193,7 @@ namespace ArchiSteamFarm { if (PermanentlyPaused) { if (!userAction) { Bot.ArchiLogger.LogGenericInfo(Strings.IgnoredPermanentPauseEnabled); + return false; } @@ -209,6 +211,7 @@ namespace ArchiSteamFarm { } await StartFarming().ConfigureAwait(false); + return true; } @@ -224,6 +227,7 @@ namespace ArchiSteamFarm { if (!Bot.CanReceiveSteamCards) { await Bot.OnFarmingFinished(false).ConfigureAwait(false); + return; } @@ -235,24 +239,28 @@ namespace ArchiSteamFarm { } bool? isAnythingToFarm = await IsAnythingToFarm().ConfigureAwait(false); - if (isAnythingToFarm == null) { + + if (!isAnythingToFarm.HasValue) { return; } if (!isAnythingToFarm.Value) { Bot.ArchiLogger.LogGenericInfo(Strings.NothingToIdle); await Bot.OnFarmingFinished(false).ConfigureAwait(false); + return; } if (GamesToFarm.Count == 0) { Bot.ArchiLogger.LogNullError(nameof(GamesToFarm)); + return; } // This is the last moment for final check if we can farm if (!Bot.IsPlayingPossible) { Bot.ArchiLogger.LogGenericInfo(Strings.PlayingNotAvailable); + return; } @@ -263,6 +271,7 @@ namespace ArchiSteamFarm { if (!Bot.IsPlayingPossible) { Bot.ArchiLogger.LogGenericInfo(Strings.PlayingNotAvailable); + return; } } @@ -310,12 +319,15 @@ 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; } ushort? cardsRemaining = await GetCardsRemaining(appID).ConfigureAwait(false); + if (!cardsRemaining.HasValue) { Bot.ArchiLogger.LogGenericWarning(string.Format(Strings.WarningCouldNotCheckCardsStatus, appID, name)); + return; } @@ -337,10 +349,12 @@ namespace ArchiSteamFarm { private async Task CheckPage(HtmlDocument htmlDocument) { if (htmlDocument == null) { Bot.ArchiLogger.LogNullError(nameof(htmlDocument)); + return; } HtmlNodeCollection htmlNodes = htmlDocument.DocumentNode.SelectNodes("//div[@class='badge_row_inner']"); + if (htmlNodes == null) { // No eligible badges whatsoever return; @@ -350,22 +364,26 @@ namespace ArchiSteamFarm { foreach (HtmlNode htmlNode in htmlNodes) { HtmlNode statsNode = htmlNode.SelectSingleNode(".//div[@class='badge_title_stats_content']"); - HtmlNode appIDNode = statsNode?.SelectSingleNode(".//div[@class='card_drop_info_dialog']"); + if (appIDNode == null) { // It's just a badge, nothing more continue; } string appIDText = appIDNode.GetAttributeValue("id", null); + if (string.IsNullOrEmpty(appIDText)) { Bot.ArchiLogger.LogNullError(nameof(appIDText)); + continue; } string[] appIDSplitted = appIDText.Split('_'); + if (appIDSplitted.Length < 5) { Bot.ArchiLogger.LogNullError(nameof(appIDSplitted)); + continue; } @@ -373,6 +391,7 @@ namespace ArchiSteamFarm { if (!uint.TryParse(appIDText, out uint appID) || (appID == 0)) { Bot.ArchiLogger.LogNullError(nameof(appID)); + continue; } @@ -393,14 +412,18 @@ namespace ArchiSteamFarm { // Cards HtmlNode progressNode = statsNode.SelectSingleNode(".//span[@class='progress_info_bold']"); + if (progressNode == null) { Bot.ArchiLogger.LogNullError(nameof(progressNode)); + continue; } string progressText = progressNode.InnerText; + if (string.IsNullOrEmpty(progressText)) { Bot.ArchiLogger.LogNullError(nameof(progressText)); + continue; } @@ -411,6 +434,7 @@ namespace ArchiSteamFarm { if (progressMatch.Success) { if (!ushort.TryParse(progressMatch.Value, out cardsRemaining) || (cardsRemaining == 0)) { Bot.ArchiLogger.LogNullError(nameof(cardsRemaining)); + continue; } } @@ -427,25 +451,32 @@ namespace ArchiSteamFarm { // To save us on extra work, check cards earned so far first HtmlNode cardsEarnedNode = statsNode.SelectSingleNode(".//div[@class='card_drop_info_header']"); + if (cardsEarnedNode == null) { Bot.ArchiLogger.LogNullError(nameof(cardsEarnedNode)); + continue; } string cardsEarnedText = cardsEarnedNode.InnerText; + if (string.IsNullOrEmpty(cardsEarnedText)) { Bot.ArchiLogger.LogNullError(nameof(cardsEarnedText)); + continue; } Match cardsEarnedMatch = Regex.Match(cardsEarnedText, @"\d+"); + if (!cardsEarnedMatch.Success) { Bot.ArchiLogger.LogNullError(nameof(cardsEarnedMatch)); + continue; } if (!ushort.TryParse(cardsEarnedMatch.Value, out ushort cardsEarned)) { Bot.ArchiLogger.LogNullError(nameof(cardsEarned)); + continue; } @@ -465,14 +496,18 @@ namespace ArchiSteamFarm { // Hours HtmlNode timeNode = statsNode.SelectSingleNode(".//div[@class='badge_title_stats_playtime']"); + if (timeNode == null) { Bot.ArchiLogger.LogNullError(nameof(timeNode)); + continue; } string hoursText = timeNode.InnerText; + if (string.IsNullOrEmpty(hoursText)) { Bot.ArchiLogger.LogNullError(nameof(hoursText)); + continue; } @@ -483,29 +518,37 @@ namespace ArchiSteamFarm { if (hoursMatch.Success) { if (!float.TryParse(hoursMatch.Value, NumberStyles.Number, CultureInfo.InvariantCulture, out hours) || (hours <= 0.0F)) { Bot.ArchiLogger.LogNullError(nameof(hours)); + continue; } } // Names HtmlNode nameNode = statsNode.SelectSingleNode("(.//div[@class='card_drop_info_body'])[last()]"); + if (nameNode == null) { Bot.ArchiLogger.LogNullError(nameof(nameNode)); + continue; } string name = nameNode.InnerText; + if (string.IsNullOrEmpty(name)) { Bot.ArchiLogger.LogNullError(nameof(name)); + continue; } // We handle two cases here - normal one, and no card drops remaining int nameStartIndex = name.IndexOf(" by playing ", StringComparison.Ordinal); + if (nameStartIndex <= 0) { nameStartIndex = name.IndexOf("You don't have any more drops remaining for ", StringComparison.Ordinal); + if (nameStartIndex <= 0) { Bot.ArchiLogger.LogNullError(nameof(nameStartIndex)); + continue; } @@ -515,8 +558,10 @@ namespace ArchiSteamFarm { nameStartIndex += 12; int nameEndIndex = name.LastIndexOf('.'); + if (nameEndIndex <= nameStartIndex) { Bot.ArchiLogger.LogNullError(nameof(nameEndIndex)); + continue; } @@ -526,29 +571,38 @@ namespace ArchiSteamFarm { byte badgeLevel = 0; HtmlNode levelNode = htmlNode.SelectSingleNode(".//div[@class='badge_info_description']/div[2]"); + if (levelNode != null) { // There is no levelNode if we didn't craft that badge yet (level 0) string levelText = levelNode.InnerText; + if (string.IsNullOrEmpty(levelText)) { Bot.ArchiLogger.LogNullError(nameof(levelText)); + continue; } int levelIndex = levelText.IndexOf("Level ", StringComparison.OrdinalIgnoreCase); + if (levelIndex < 0) { Bot.ArchiLogger.LogNullError(nameof(levelIndex)); + continue; } levelIndex += 6; + if (levelText.Length <= levelIndex) { Bot.ArchiLogger.LogNullError(nameof(levelIndex)); + continue; } levelText = levelText.Substring(levelIndex, 1); + if (!byte.TryParse(levelText, out badgeLevel) || (badgeLevel == 0) || (badgeLevel > 5)) { Bot.ArchiLogger.LogNullError(nameof(badgeLevel)); + continue; } } @@ -564,13 +618,16 @@ namespace ArchiSteamFarm { switch (Program.GlobalConfig.OptimizationMode) { case GlobalConfig.EOptimizationMode.MinMemoryUsage: await task.ConfigureAwait(false); + break; default: + if (backgroundTasks == null) { backgroundTasks = new List(); } backgroundTasks.Add(task); + break; } } @@ -585,10 +642,12 @@ namespace ArchiSteamFarm { private async Task CheckPage(byte page) { if (page == 0) { Bot.ArchiLogger.LogNullError(nameof(page)); + return; } HtmlDocument htmlDocument = await Bot.ArchiWebHandler.GetBadgePage(page).ConfigureAwait(false); + if (htmlDocument == null) { return; } @@ -616,15 +675,18 @@ namespace ArchiSteamFarm { if (!await IsPlayableGame(game).ConfigureAwait(false)) { GamesToFarm.Remove(game); innerGamesToFarm.Remove(game); + continue; } if (await FarmSolo(game).ConfigureAwait(false)) { innerGamesToFarm.Remove(game); + continue; } NowFarming = false; + return; } @@ -635,6 +697,7 @@ namespace ArchiSteamFarm { foreach (Game game in GamesToFarm.OrderByDescending(game => game.HoursPlayed).ToList()) { if (!await IsPlayableGame(game).ConfigureAwait(false)) { GamesToFarm.Remove(game); + continue; } @@ -656,6 +719,7 @@ namespace ArchiSteamFarm { Bot.ArchiLogger.LogGenericInfo(string.Format(Strings.IdlingFinishedForGames, string.Join(", ", innerGamesToFarm.Select(game => game.AppID)))); } else { NowFarming = false; + return; } } @@ -669,6 +733,7 @@ namespace ArchiSteamFarm { if (!await IsPlayableGame(game).ConfigureAwait(false)) { GamesToFarm.Remove(game); + continue; } @@ -677,6 +742,7 @@ namespace ArchiSteamFarm { } NowFarming = false; + return; } } @@ -691,6 +757,7 @@ namespace ArchiSteamFarm { private async Task FarmCards(Game game) { if (game == null) { Bot.ArchiLogger.LogNullError(nameof(game)); + return false; } @@ -707,6 +774,7 @@ namespace ArchiSteamFarm { Bot.ArchiLogger.LogGenericInfo(string.Format(Strings.StillIdling, game.AppID, game.GameName)); DateTime startFarmingPeriod = DateTime.UtcNow; + if (await FarmingResetSemaphore.WaitAsync(Program.GlobalConfig.FarmingDelay * 60 * 1000 + ExtraFarmingDelaySeconds * 1000).ConfigureAwait(false)) { success = KeepFarming; } @@ -720,39 +788,47 @@ namespace ArchiSteamFarm { } Bot.ArchiLogger.LogGenericInfo(string.Format(Strings.StoppedIdling, game.AppID, game.GameName)); + return success; } private async Task FarmHours(IReadOnlyCollection games) { if ((games == null) || (games.Count == 0)) { Bot.ArchiLogger.LogNullError(nameof(games)); + return false; } float maxHour = games.Max(game => game.HoursPlayed); + if (maxHour < 0) { Bot.ArchiLogger.LogNullError(nameof(maxHour)); + return false; } if (maxHour >= Bot.BotConfig.HoursUntilCardDrops) { Bot.ArchiLogger.LogGenericError(string.Format(Strings.ErrorIsInvalid, nameof(maxHour))); + return true; } await Bot.IdleGames(games).ConfigureAwait(false); bool success = true; + while (maxHour < Bot.BotConfig.HoursUntilCardDrops) { Bot.ArchiLogger.LogGenericInfo(string.Format(Strings.StillIdlingList, string.Join(", ", games.Select(game => game.AppID)))); DateTime startFarmingPeriod = DateTime.UtcNow; + if (await FarmingResetSemaphore.WaitAsync(Program.GlobalConfig.FarmingDelay * 60 * 1000 + ExtraFarmingDelaySeconds * 1000).ConfigureAwait(false)) { success = KeepFarming; } // Don't forget to update our GamesToFarm hours float timePlayed = (float) DateTime.UtcNow.Subtract(startFarmingPeriod).TotalHours; + foreach (Game game in games) { game.HoursPlayed += timePlayed; } @@ -765,12 +841,14 @@ namespace ArchiSteamFarm { } Bot.ArchiLogger.LogGenericInfo(string.Format(Strings.StoppedIdlingList, string.Join(", ", games.Select(game => game.AppID)))); + return success; } private async Task FarmMultiple(IReadOnlyCollection games) { if ((games == null) || (games.Count == 0)) { Bot.ArchiLogger.LogNullError(nameof(games)); + return false; } @@ -780,12 +858,14 @@ namespace ArchiSteamFarm { bool result = await FarmHours(games).ConfigureAwait(false); CurrentGamesFarming.Clear(); + return result; } private async Task FarmSolo(Game game) { if (game == null) { Bot.ArchiLogger.LogNullError(nameof(game)); + return true; } @@ -803,35 +883,42 @@ namespace ArchiSteamFarm { GamesToFarm.Remove(game); Bot.ArchiLogger.LogGenericInfo(string.Format(Strings.IdlingFinishedForGame, game.AppID, game.GameName, TimeSpan.FromHours(game.HoursPlayed).ToHumanReadable())); + return true; } private async Task GetCardsRemaining(uint appID) { if (appID == 0) { Bot.ArchiLogger.LogNullError(nameof(appID)); + return 0; } HtmlDocument htmlDocument = await Bot.ArchiWebHandler.GetGameCardsPage(appID).ConfigureAwait(false); HtmlNode progressNode = htmlDocument?.DocumentNode.SelectSingleNode("//span[@class='progress_info_bold']"); + if (progressNode == null) { return null; } string progress = progressNode.InnerText; + if (string.IsNullOrEmpty(progress)) { Bot.ArchiLogger.LogNullError(nameof(progress)); + return null; } Match match = Regex.Match(progress, @"\d+"); + if (!match.Success) { return 0; } if (!ushort.TryParse(match.Value, out ushort cardsRemaining) || (cardsRemaining == 0)) { Bot.ArchiLogger.LogNullError(nameof(cardsRemaining)); + return null; } @@ -842,23 +929,29 @@ namespace ArchiSteamFarm { // Find the number of badge pages Bot.ArchiLogger.LogGenericInfo(Strings.CheckingFirstBadgePage); HtmlDocument htmlDocument = await Bot.ArchiWebHandler.GetBadgePage(1).ConfigureAwait(false); + if (htmlDocument == null) { Bot.ArchiLogger.LogGenericWarning(Strings.WarningCouldNotCheckBadges); + return null; } byte maxPages = 1; HtmlNode htmlNode = htmlDocument.DocumentNode.SelectSingleNode("(//a[@class='pagelink'])[last()]"); + if (htmlNode != null) { string lastPage = htmlNode.InnerText; + if (string.IsNullOrEmpty(lastPage)) { Bot.ArchiLogger.LogNullError(nameof(lastPage)); + return null; } if (!byte.TryParse(lastPage, out maxPages) || (maxPages == 0)) { Bot.ArchiLogger.LogNullError(nameof(maxPages)); + return null; } } @@ -894,46 +987,56 @@ namespace ArchiSteamFarm { } await Task.WhenAll(tasks).ConfigureAwait(false); + break; } if (GamesToFarm.Count == 0) { ShouldResumeFarming = false; + return false; } ShouldResumeFarming = true; await SortGamesToFarm().ConfigureAwait(false); + return true; } private async Task IsPlayableGame(Game game) { (uint playableAppID, DateTime ignoredUntil) = await Bot.GetAppDataForIdling(game.AppID, game.HoursPlayed).ConfigureAwait(false); + if (playableAppID == 0) { IgnoredAppIDs[game.AppID] = ignoredUntil < DateTime.MaxValue ? ignoredUntil : DateTime.UtcNow.AddHours(HoursToIgnore); Bot.ArchiLogger.LogGenericInfo(string.Format(Strings.IdlingGameNotPossible, game.AppID, game.GameName)); + return false; } game.PlayableAppID = playableAppID; + return true; } private async Task ShouldFarm(Game game) { if (game == null) { Bot.ArchiLogger.LogNullError(nameof(game)); + return false; } ushort? cardsRemaining = await GetCardsRemaining(game.AppID).ConfigureAwait(false); + if (!cardsRemaining.HasValue) { Bot.ArchiLogger.LogGenericWarning(string.Format(Strings.WarningCouldNotCheckCardsStatus, game.AppID, game.GameName)); + return null; } game.CardsRemaining = cardsRemaining.Value; Bot.ArchiLogger.LogGenericInfo(string.Format(Strings.IdlingStatusForGame, game.AppID, game.GameName, game.CardsRemaining)); + return game.CardsRemaining > 0; } @@ -944,24 +1047,31 @@ namespace ArchiSteamFarm { foreach (BotConfig.EFarmingOrder farmingOrder in Bot.BotConfig.FarmingOrders) { switch (farmingOrder) { case BotConfig.EFarmingOrder.Unordered: + break; case BotConfig.EFarmingOrder.AppIDsAscending: gamesToFarm = gamesToFarm.ThenBy(game => game.AppID); + break; case BotConfig.EFarmingOrder.AppIDsDescending: gamesToFarm = gamesToFarm.ThenByDescending(game => game.AppID); + break; case BotConfig.EFarmingOrder.BadgeLevelsAscending: gamesToFarm = gamesToFarm.ThenBy(game => game.BadgeLevel); + break; case BotConfig.EFarmingOrder.BadgeLevelsDescending: gamesToFarm = gamesToFarm.ThenByDescending(game => game.BadgeLevel); + break; case BotConfig.EFarmingOrder.CardDropsAscending: gamesToFarm = gamesToFarm.ThenBy(game => game.CardsRemaining); + break; case BotConfig.EFarmingOrder.CardDropsDescending: gamesToFarm = gamesToFarm.ThenByDescending(game => game.CardsRemaining); + break; case BotConfig.EFarmingOrder.MarketableAscending: case BotConfig.EFarmingOrder.MarketableDescending: @@ -971,12 +1081,15 @@ namespace ArchiSteamFarm { switch (farmingOrder) { case BotConfig.EFarmingOrder.MarketableAscending: gamesToFarm = gamesToFarm.ThenBy(game => marketableAppIDs.Contains(game.AppID)); + break; case BotConfig.EFarmingOrder.MarketableDescending: gamesToFarm = gamesToFarm.ThenByDescending(game => marketableAppIDs.Contains(game.AppID)); + break; default: Bot.ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(farmingOrder), farmingOrder)); + return; } } @@ -984,18 +1097,23 @@ namespace ArchiSteamFarm { break; case BotConfig.EFarmingOrder.HoursAscending: gamesToFarm = gamesToFarm.ThenBy(game => game.HoursPlayed); + break; case BotConfig.EFarmingOrder.HoursDescending: gamesToFarm = gamesToFarm.ThenByDescending(game => game.HoursPlayed); + break; case BotConfig.EFarmingOrder.NamesAscending: gamesToFarm = gamesToFarm.ThenBy(game => game.GameName); + break; case BotConfig.EFarmingOrder.NamesDescending: gamesToFarm = gamesToFarm.ThenByDescending(game => game.GameName); + break; case BotConfig.EFarmingOrder.Random: gamesToFarm = gamesToFarm.ThenBy(game => Utilities.RandomNext()); + break; case BotConfig.EFarmingOrder.RedeemDateTimesAscending: case BotConfig.EFarmingOrder.RedeemDateTimesDescending: @@ -1023,18 +1141,22 @@ namespace ArchiSteamFarm { switch (farmingOrder) { case BotConfig.EFarmingOrder.RedeemDateTimesAscending: gamesToFarm = gamesToFarm.ThenBy(game => redeemDates[game.AppID]); + break; case BotConfig.EFarmingOrder.RedeemDateTimesDescending: gamesToFarm = gamesToFarm.ThenByDescending(game => redeemDates[game.AppID]); + break; default: Bot.ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(farmingOrder), farmingOrder)); + return; } break; default: Bot.ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(farmingOrder), farmingOrder)); + return; } } diff --git a/ArchiSteamFarm/Collections/ConcurrentHashSet.cs b/ArchiSteamFarm/Collections/ConcurrentHashSet.cs index cd22fc396..df4266fdd 100644 --- a/ArchiSteamFarm/Collections/ConcurrentHashSet.cs +++ b/ArchiSteamFarm/Collections/ConcurrentHashSet.cs @@ -54,26 +54,31 @@ namespace ArchiSteamFarm.Collections { public bool IsProperSubsetOf(IEnumerable other) { ISet otherSet = other as ISet ?? other.ToHashSet(); + return (otherSet.Count > Count) && IsSubsetOf(otherSet); } public bool IsProperSupersetOf(IEnumerable other) { ISet otherSet = other as ISet ?? other.ToHashSet(); + return (otherSet.Count < Count) && IsSupersetOf(otherSet); } public bool IsSubsetOf(IEnumerable other) { ISet otherSet = other as ISet ?? other.ToHashSet(); + return this.All(otherSet.Contains); } public bool IsSupersetOf(IEnumerable other) { ISet otherSet = other as ISet ?? other.ToHashSet(); + return otherSet.All(Contains); } public bool Overlaps(IEnumerable other) { ISet otherSet = other as ISet ?? other.ToHashSet(); + return otherSet.Any(Contains); } @@ -81,13 +86,14 @@ namespace ArchiSteamFarm.Collections { public bool SetEquals(IEnumerable other) { ISet otherSet = other as ISet ?? other.ToHashSet(); + return (otherSet.Count == Count) && otherSet.All(Contains); } public void SymmetricExceptWith(IEnumerable other) { ISet otherSet = other as ISet ?? other.ToHashSet(); - HashSet removed = new HashSet(); + foreach (T item in otherSet.Where(Contains)) { removed.Add(item); Remove(item); @@ -119,6 +125,7 @@ namespace ArchiSteamFarm.Collections { } ReplaceWith(other); + return true; } diff --git a/ArchiSteamFarm/Collections/ConcurrentSortedHashSet.cs b/ArchiSteamFarm/Collections/ConcurrentSortedHashSet.cs index e41588758..797f6044d 100644 --- a/ArchiSteamFarm/Collections/ConcurrentSortedHashSet.cs +++ b/ArchiSteamFarm/Collections/ConcurrentSortedHashSet.cs @@ -218,22 +218,22 @@ namespace ArchiSteamFarm.Collections { public T Current => Enumerator.Current; private readonly IEnumerator Enumerator; - private readonly SemaphoreSlim SemaphoreSlim; + private readonly SemaphoreSlim Semaphore; object IEnumerator.Current => Current; - internal ConcurrentEnumerator(IReadOnlyCollection collection, SemaphoreSlim semaphoreSlim) { - if ((collection == null) || (semaphoreSlim == null)) { - throw new ArgumentNullException(nameof(collection) + " || " + nameof(semaphoreSlim)); + internal ConcurrentEnumerator(IReadOnlyCollection collection, SemaphoreSlim semaphore) { + if ((collection == null) || (semaphore == null)) { + throw new ArgumentNullException(nameof(collection) + " || " + nameof(semaphore)); } - SemaphoreSlim = semaphoreSlim; - semaphoreSlim.Wait(); + Semaphore = semaphore; + semaphore.Wait(); Enumerator = collection.GetEnumerator(); } - public void Dispose() => SemaphoreSlim.Release(); + public void Dispose() => Semaphore.Release(); public bool MoveNext() => Enumerator.MoveNext(); public void Reset() => Enumerator.Reset(); } diff --git a/ArchiSteamFarm/Collections/FixedSizeConcurrentQueue.cs b/ArchiSteamFarm/Collections/FixedSizeConcurrentQueue.cs index 6f513c798..597568f81 100644 --- a/ArchiSteamFarm/Collections/FixedSizeConcurrentQueue.cs +++ b/ArchiSteamFarm/Collections/FixedSizeConcurrentQueue.cs @@ -30,9 +30,11 @@ namespace ArchiSteamFarm.Collections { internal byte MaxCount { get => _MaxCount; + set { if (value == 0) { ASF.ArchiLogger.LogNullError(nameof(value)); + return; } diff --git a/ArchiSteamFarm/Commands.cs b/ArchiSteamFarm/Commands.cs index aaf9258a6..1523faefb 100644 --- a/ArchiSteamFarm/Commands.cs +++ b/ArchiSteamFarm/Commands.cs @@ -46,6 +46,7 @@ namespace ArchiSteamFarm { internal async Task Response(ulong steamID, string message) { if ((steamID == 0) || string.IsNullOrEmpty(message)) { Bot.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(message)); + return null; } @@ -62,187 +63,276 @@ namespace ArchiSteamFarm { switch (args.Length) { case 0: Bot.ArchiLogger.LogNullError(nameof(args)); + return null; case 1: + switch (args[0].ToUpperInvariant()) { case "2FA": + return await Response2FA(steamID).ConfigureAwait(false); case "2FANO": + return await Response2FAConfirm(steamID, false).ConfigureAwait(false); case "2FAOK": + return await Response2FAConfirm(steamID, true).ConfigureAwait(false); case "BALANCE": + return ResponseWalletBalance(steamID); case "BL": + return ResponseBlacklist(steamID); case "EXIT": + return ResponseExit(steamID); case "FARM": + return await ResponseFarm(steamID).ConfigureAwait(false); case "HELP": + return ResponseHelp(steamID); case "IB": + return ResponseIdleBlacklist(steamID); case "IQ": + return ResponseIdleQueue(steamID); case "LEVEL": + return await ResponseLevel(steamID).ConfigureAwait(false); case "LOOT": + return await ResponseLoot(steamID).ConfigureAwait(false); case "PASSWORD": + return ResponsePassword(steamID); case "PAUSE": + return await ResponsePause(steamID, true).ConfigureAwait(false); case "PAUSE~": + return await ResponsePause(steamID, false).ConfigureAwait(false); case "RESUME": + return ResponseResume(steamID); case "RESTART": + return ResponseRestart(steamID); case "SA": + return await ResponseStatus(steamID, SharedInfo.ASF).ConfigureAwait(false); case "START": + return ResponseStart(steamID); case "STATS": + return ResponseStats(steamID); case "STATUS": + return ResponseStatus(steamID).Response; case "STOP": + return ResponseStop(steamID); case "UNPACK": + return await ResponseUnpackBoosters(steamID).ConfigureAwait(false); case "UPDATE": + return await ResponseUpdate(steamID).ConfigureAwait(false); case "VERSION": + return ResponseVersion(steamID); default: + return ResponseUnknown(steamID); } default: + switch (args[0].ToUpperInvariant()) { case "2FA": + return await Response2FA(steamID, Utilities.GetArgsAsText(args, 1, ",")).ConfigureAwait(false); case "2FANO": + return await Response2FAConfirm(steamID, Utilities.GetArgsAsText(args, 1, ","), false).ConfigureAwait(false); case "2FAOK": + return await Response2FAConfirm(steamID, Utilities.GetArgsAsText(args, 1, ","), true).ConfigureAwait(false); case "ADDLICENSE" when args.Length > 2: + return await ResponseAddLicense(steamID, args[1], Utilities.GetArgsAsText(args, 2, ",")).ConfigureAwait(false); case "ADDLICENSE": + return await ResponseAddLicense(steamID, args[1]).ConfigureAwait(false); case "BALANCE": + return await ResponseWalletBalance(steamID, Utilities.GetArgsAsText(args, 1, ",")).ConfigureAwait(false); case "BL": + return await ResponseBlacklist(steamID, Utilities.GetArgsAsText(args, 1, ",")).ConfigureAwait(false); case "BLADD" when args.Length > 2: + return await ResponseBlacklistAdd(steamID, args[1], Utilities.GetArgsAsText(args, 2, ",")).ConfigureAwait(false); case "BLADD": + return await ResponseBlacklistAdd(steamID, args[1]).ConfigureAwait(false); case "BLRM" when args.Length > 2: + return await ResponseBlacklistRemove(steamID, args[1], Utilities.GetArgsAsText(args, 2, ",")).ConfigureAwait(false); case "BLRM": + return await ResponseBlacklistRemove(steamID, args[1]).ConfigureAwait(false); case "FARM": + return await ResponseFarm(steamID, Utilities.GetArgsAsText(args, 1, ",")).ConfigureAwait(false); case "INPUT" when args.Length > 3: + return await ResponseInput(steamID, args[1], args[2], Utilities.GetArgsAsText(message, 3)).ConfigureAwait(false); case "INPUT" when args.Length > 2: + return ResponseInput(steamID, args[1], args[2]); case "IB": + return await ResponseIdleBlacklist(steamID, Utilities.GetArgsAsText(args, 1, ",")).ConfigureAwait(false); case "IBADD" when args.Length > 2: + return await ResponseIdleBlacklistAdd(steamID, args[1], Utilities.GetArgsAsText(args, 2, ",")).ConfigureAwait(false); case "IBADD": + return await ResponseIdleBlacklistAdd(steamID, args[1]).ConfigureAwait(false); case "IBRM" when args.Length > 2: + return await ResponseIdleBlacklistRemove(steamID, args[1], Utilities.GetArgsAsText(args, 2, ",")).ConfigureAwait(false); case "IBRM": + return await ResponseIdleBlacklistRemove(steamID, args[1]).ConfigureAwait(false); case "IQ": + return await ResponseIdleQueue(steamID, Utilities.GetArgsAsText(args, 1, ",")).ConfigureAwait(false); case "IQADD" when args.Length > 2: + return await ResponseIdleQueueAdd(steamID, args[1], Utilities.GetArgsAsText(args, 2, ",")).ConfigureAwait(false); case "IQADD": + return await ResponseIdleQueueAdd(steamID, args[1]).ConfigureAwait(false); case "IQRM" when args.Length > 2: + return await ResponseIdleQueueRemove(steamID, args[1], Utilities.GetArgsAsText(args, 2, ",")).ConfigureAwait(false); case "IQRM": + return await ResponseIdleQueueRemove(steamID, args[1]).ConfigureAwait(false); case "LEVEL": + return await ResponseLevel(steamID, Utilities.GetArgsAsText(args, 1, ",")).ConfigureAwait(false); case "LOOT": + return await ResponseLoot(steamID, Utilities.GetArgsAsText(args, 1, ",")).ConfigureAwait(false); case "LOOT^" when args.Length > 3: + return await ResponseAdvancedLoot(steamID, args[1], args[2], Utilities.GetArgsAsText(message, 3)).ConfigureAwait(false); case "LOOT^" when args.Length > 2: + return await ResponseAdvancedLoot(steamID, args[1], args[2]).ConfigureAwait(false); case "LOOT@" when args.Length > 2: + return await ResponseLootByRealAppIDs(steamID, args[1], Utilities.GetArgsAsText(args, 2, ",")).ConfigureAwait(false); case "LOOT@": + return await ResponseLootByRealAppIDs(steamID, args[1]).ConfigureAwait(false); case "NICKNAME" when args.Length > 2: + return await ResponseNickname(steamID, args[1], Utilities.GetArgsAsText(message, 2)).ConfigureAwait(false); case "NICKNAME": + return ResponseNickname(steamID, args[1]); case "OA": + return await ResponseOwns(steamID, SharedInfo.ASF, Utilities.GetArgsAsText(message, 1)).ConfigureAwait(false); case "OWNS" when args.Length > 2: + return await ResponseOwns(steamID, args[1], Utilities.GetArgsAsText(message, 2)).ConfigureAwait(false); case "OWNS": + return (await ResponseOwns(steamID, args[1]).ConfigureAwait(false)).Response; case "PASSWORD": + return await ResponsePassword(steamID, Utilities.GetArgsAsText(args, 1, ",")).ConfigureAwait(false); case "PAUSE": + return await ResponsePause(steamID, Utilities.GetArgsAsText(args, 1, ","), true).ConfigureAwait(false); case "PAUSE~": + return await ResponsePause(steamID, Utilities.GetArgsAsText(args, 1, ","), false).ConfigureAwait(false); case "PAUSE&" when args.Length > 2: + return await ResponsePause(steamID, args[1], true, Utilities.GetArgsAsText(message, 2)).ConfigureAwait(false); case "PAUSE&": + return await ResponsePause(steamID, true, args[1]).ConfigureAwait(false); case "PLAY" when args.Length > 2: + return await ResponsePlay(steamID, args[1], Utilities.GetArgsAsText(message, 2)).ConfigureAwait(false); case "PLAY": + return await ResponsePlay(steamID, args[1]).ConfigureAwait(false); case "PRIVACY" when args.Length > 2: + return await ResponsePrivacy(steamID, args[1], Utilities.GetArgsAsText(args, 2, ",")).ConfigureAwait(false); case "PRIVACY": + return await ResponsePrivacy(steamID, args[1]).ConfigureAwait(false); case "R" when args.Length > 2: case "REDEEM" when args.Length > 2: + return await ResponseRedeem(steamID, args[1], Utilities.GetArgsAsText(args, 2, ",")).ConfigureAwait(false); case "R": case "REDEEM": + return await ResponseRedeem(steamID, args[1]).ConfigureAwait(false); case "R^" when args.Length > 3: case "REDEEM^" when args.Length > 3: + return await ResponseAdvancedRedeem(steamID, args[1], args[2], Utilities.GetArgsAsText(args, 3, ",")).ConfigureAwait(false); case "R^" when args.Length > 2: case "REDEEM^" when args.Length > 2: + return await ResponseAdvancedRedeem(steamID, args[1], args[2]).ConfigureAwait(false); case "RESUME": + return await ResponseResume(steamID, Utilities.GetArgsAsText(args, 1, ",")).ConfigureAwait(false); case "START": + return await ResponseStart(steamID, Utilities.GetArgsAsText(args, 1, ",")).ConfigureAwait(false); case "STATUS": + return await ResponseStatus(steamID, Utilities.GetArgsAsText(args, 1, ",")).ConfigureAwait(false); case "STOP": + return await ResponseStop(steamID, Utilities.GetArgsAsText(args, 1, ",")).ConfigureAwait(false); case "TRANSFER" when args.Length > 2: + return await ResponseTransfer(steamID, args[1], Utilities.GetArgsAsText(message, 2)).ConfigureAwait(false); case "TRANSFER": + return await ResponseTransfer(steamID, args[1]).ConfigureAwait(false); case "TRANSFER^" when args.Length > 4: + return await ResponseAdvancedTransfer(steamID, args[1], args[2], args[3], Utilities.GetArgsAsText(message, 4)).ConfigureAwait(false); case "TRANSFER^" when args.Length > 3: + return await ResponseAdvancedTransfer(steamID, args[1], args[2], args[3]).ConfigureAwait(false); case "TRANSFER@" when args.Length > 3: + return await ResponseTransferByRealAppIDs(steamID, args[1], args[2], Utilities.GetArgsAsText(message, 3)).ConfigureAwait(false); case "TRANSFER@" when args.Length > 2: + return await ResponseTransferByRealAppIDs(steamID, args[1], args[2]).ConfigureAwait(false); case "UNPACK": + return await ResponseUnpackBoosters(steamID, Utilities.GetArgsAsText(args, 1, ",")).ConfigureAwait(false); default: + return ResponseUnknown(steamID); } } @@ -251,6 +341,7 @@ namespace ArchiSteamFarm { private static string FormatBotResponse(string response, string botName) { if (string.IsNullOrEmpty(response) || string.IsNullOrEmpty(botName)) { ASF.ArchiLogger.LogNullError(nameof(response) + " || " + nameof(botName)); + return null; } @@ -260,6 +351,7 @@ namespace ArchiSteamFarm { private string FormatBotResponse(string response) { if (string.IsNullOrEmpty(response)) { ASF.ArchiLogger.LogNullError(nameof(response)); + return null; } @@ -269,6 +361,7 @@ namespace ArchiSteamFarm { private static string FormatStaticResponse(string response) { if (string.IsNullOrEmpty(response)) { ASF.ArchiLogger.LogNullError(nameof(response)); + return null; } @@ -278,6 +371,7 @@ namespace ArchiSteamFarm { private bool IsOperator(ulong steamID) { if (steamID == 0) { Bot.ArchiLogger.LogNullError(nameof(steamID)); + return false; } @@ -287,6 +381,7 @@ namespace ArchiSteamFarm { private async Task Response2FA(ulong steamID) { if (steamID == 0) { Bot.ArchiLogger.LogNullError(nameof(steamID)); + return null; } @@ -299,16 +394,19 @@ namespace ArchiSteamFarm { } string token = await Bot.BotDatabase.MobileAuthenticator.GenerateToken().ConfigureAwait(false); + return FormatBotResponse(!string.IsNullOrEmpty(token) ? string.Format(Strings.BotAuthenticatorToken, token) : Strings.WarningFailed); } private static async Task Response2FA(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; } @@ -316,12 +414,14 @@ namespace ArchiSteamFarm { IList results = await Utilities.InParallel(bots.Select(bot => bot.Commands.Response2FA(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 Response2FAConfirm(ulong steamID, bool confirm) { if (steamID == 0) { Bot.ArchiLogger.LogNullError(nameof(steamID)); + return null; } @@ -338,16 +438,19 @@ namespace ArchiSteamFarm { } bool result = await Bot.Actions.AcceptConfirmations(confirm).ConfigureAwait(false); + return FormatBotResponse(result ? Strings.Success : Strings.WarningFailed); } 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; } HashSet bots = Bot.GetBots(botNames); + if ((bots == null) || (bots.Count == 0)) { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } @@ -355,12 +458,14 @@ namespace ArchiSteamFarm { 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))); + return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; } private async Task ResponseAddLicense(ulong steamID, IReadOnlyCollection gameIDs) { if ((steamID == 0) || (gameIDs == null) || (gameIDs.Count == 0)) { Bot.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(gameIDs) + " || " + nameof(gameIDs.Count)); + return null; } @@ -377,6 +482,7 @@ namespace ArchiSteamFarm { foreach (uint gameID in gameIDs) { if (await Bot.ArchiWebHandler.AddFreeLicense(gameID).ConfigureAwait(false)) { response.AppendLine(FormatBotResponse(string.Format(Strings.BotAddLicenseWithItems, gameID, EResult.OK, "sub/" + gameID))); + continue; } @@ -387,11 +493,13 @@ namespace ArchiSteamFarm { } catch (Exception e) { Bot.ArchiLogger.LogGenericWarningException(e); response.AppendLine(FormatBotResponse(string.Format(Strings.BotAddLicense, gameID, EResult.Timeout))); + break; } if (callback == null) { response.AppendLine(FormatBotResponse(string.Format(Strings.BotAddLicense, gameID, EResult.Timeout))); + break; } @@ -404,6 +512,7 @@ namespace ArchiSteamFarm { private async Task ResponseAddLicense(ulong steamID, string targetGameIDs) { if ((steamID == 0) || string.IsNullOrEmpty(targetGameIDs)) { ASF.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(targetGameIDs)); + return null; } @@ -437,10 +546,12 @@ namespace ArchiSteamFarm { private static async Task ResponseAddLicense(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; } HashSet bots = Bot.GetBots(botNames); + if ((bots == null) || (bots.Count == 0)) { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } @@ -448,12 +559,14 @@ namespace ArchiSteamFarm { IList results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseAddLicense(steamID, targetGameIDs))).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 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; } @@ -474,16 +587,19 @@ namespace ArchiSteamFarm { } (bool success, string output) = await Bot.Actions.SendTradeOffer(appID, contextID).ConfigureAwait(false); + return FormatBotResponse(success ? output : string.Format(Strings.WarningFailedWithError, output)); } 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; } HashSet bots = Bot.GetBots(botNames); + if ((bots == null) || (bots.Count == 0)) { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } @@ -491,12 +607,14 @@ namespace ArchiSteamFarm { 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))); + 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; } @@ -516,29 +634,38 @@ namespace ArchiSteamFarm { switch (flag.ToUpperInvariant()) { case "FD": redeemFlags |= ERedeemFlags.ForceDistributing; + break; case "FF": redeemFlags |= ERedeemFlags.ForceForwarding; + break; case "FKMG": redeemFlags |= ERedeemFlags.ForceKeepMissingGames; + break; case "SD": redeemFlags |= ERedeemFlags.SkipDistributing; + break; case "SF": redeemFlags |= ERedeemFlags.SkipForwarding; + break; case "SI": redeemFlags |= ERedeemFlags.SkipInitial; + break; case "SKMG": redeemFlags |= ERedeemFlags.SkipKeepMissingGames; + break; case "V": redeemFlags |= ERedeemFlags.Validate; + break; default: + return FormatBotResponse(string.Format(Strings.ErrorIsInvalid, flag)); } } @@ -549,10 +676,12 @@ namespace ArchiSteamFarm { 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; } HashSet bots = Bot.GetBots(botNames); + if ((bots == null) || (bots.Count == 0)) { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } @@ -560,12 +689,14 @@ namespace ArchiSteamFarm { 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))); + return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; } private async Task ResponseAdvancedTransfer(ulong steamID, uint appID, byte 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; } @@ -582,12 +713,14 @@ namespace ArchiSteamFarm { } (bool success, string output) = await Bot.Actions.SendTradeOffer(appID, contextID, targetBot.SteamID).ConfigureAwait(false); + return FormatBotResponse(success ? output : string.Format(Strings.WarningFailedWithError, output)); } 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; } @@ -609,10 +742,12 @@ namespace ArchiSteamFarm { 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; } HashSet bots = Bot.GetBots(botNames); + if ((bots == null) || (bots.Count == 0)) { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } @@ -632,12 +767,14 @@ namespace ArchiSteamFarm { 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))); + 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; } @@ -646,16 +783,19 @@ namespace ArchiSteamFarm { } IReadOnlyCollection blacklist = Bot.BotDatabase.GetBlacklistedFromTradesSteamIDs(); + return FormatBotResponse(blacklist.Count > 0 ? string.Join(", ", blacklist) : string.Format(Strings.ErrorIsEmpty, nameof(blacklist))); } private static async Task ResponseBlacklist(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; } @@ -663,12 +803,14 @@ namespace ArchiSteamFarm { 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))); + return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; } private async Task ResponseBlacklistAdd(ulong steamID, string targetSteamIDs) { if ((steamID == 0) || string.IsNullOrEmpty(targetSteamIDs)) { Bot.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(targetSteamIDs)); + return null; } @@ -693,16 +835,19 @@ namespace ArchiSteamFarm { } await Bot.BotDatabase.AddBlacklistedFromTradesSteamIDs(targetIDs).ConfigureAwait(false); + return FormatBotResponse(Strings.Done); } 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; } HashSet bots = Bot.GetBots(botNames); + if ((bots == null) || (bots.Count == 0)) { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } @@ -710,12 +855,14 @@ namespace ArchiSteamFarm { IList results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseBlacklistAdd(steamID, targetSteamIDs))).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 ResponseBlacklistRemove(ulong steamID, string targetSteamIDs) { if ((steamID == 0) || string.IsNullOrEmpty(targetSteamIDs)) { Bot.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(targetSteamIDs)); + return null; } @@ -740,16 +887,19 @@ namespace ArchiSteamFarm { } await Bot.BotDatabase.RemoveBlacklistedFromTradesSteamIDs(targetIDs).ConfigureAwait(false); + return FormatBotResponse(Strings.Done); } 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; } HashSet bots = Bot.GetBots(botNames); + if ((bots == null) || (bots.Count == 0)) { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } @@ -757,12 +907,14 @@ namespace ArchiSteamFarm { IList results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseBlacklistRemove(steamID, targetSteamIDs))).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 ResponseExit(ulong steamID) { if (steamID == 0) { ASF.ArchiLogger.LogNullError(nameof(steamID)); + return null; } @@ -771,12 +923,14 @@ namespace ArchiSteamFarm { } (bool success, string output) = Actions.Exit(); + return FormatStaticResponse(success ? output : string.Format(Strings.WarningFailedWithError, output)); } private async Task ResponseFarm(ulong steamID) { if (steamID == 0) { Bot.ArchiLogger.LogNullError(nameof(steamID)); + return null; } @@ -793,16 +947,19 @@ namespace ArchiSteamFarm { } Utilities.InBackground(Bot.CardsFarmer.StartFarming); + return FormatBotResponse(Strings.Done); } private static async Task ResponseFarm(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; } @@ -810,12 +967,14 @@ namespace ArchiSteamFarm { IList results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseFarm(steamID))).ConfigureAwait(false); 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; } @@ -825,6 +984,7 @@ namespace ArchiSteamFarm { private string ResponseIdleBlacklist(ulong steamID) { if (steamID == 0) { Bot.ArchiLogger.LogNullError(nameof(steamID)); + return null; } @@ -833,16 +993,19 @@ namespace ArchiSteamFarm { } IReadOnlyCollection idleBlacklist = Bot.BotDatabase.GetIdlingBlacklistedAppIDs(); + return FormatBotResponse(idleBlacklist.Count > 0 ? string.Join(", ", idleBlacklist) : string.Format(Strings.ErrorIsEmpty, nameof(idleBlacklist))); } private static async Task ResponseIdleBlacklist(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; } @@ -850,12 +1013,14 @@ namespace ArchiSteamFarm { 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))); + return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; } private async Task ResponseIdleBlacklistAdd(ulong steamID, string targetAppIDs) { if ((steamID == 0) || string.IsNullOrEmpty(targetAppIDs)) { Bot.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(targetAppIDs)); + return null; } @@ -880,16 +1045,19 @@ namespace ArchiSteamFarm { } await Bot.BotDatabase.AddIdlingBlacklistedAppIDs(appIDs).ConfigureAwait(false); + return FormatBotResponse(Strings.Done); } 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; } HashSet bots = Bot.GetBots(botNames); + if ((bots == null) || (bots.Count == 0)) { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } @@ -897,12 +1065,14 @@ namespace ArchiSteamFarm { IList results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseIdleBlacklistAdd(steamID, targetAppIDs))).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 ResponseIdleBlacklistRemove(ulong steamID, string targetAppIDs) { if ((steamID == 0) || string.IsNullOrEmpty(targetAppIDs)) { Bot.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(targetAppIDs)); + return null; } @@ -927,16 +1097,19 @@ namespace ArchiSteamFarm { } await Bot.BotDatabase.RemoveIdlingBlacklistedAppIDs(appIDs).ConfigureAwait(false); + return FormatBotResponse(Strings.Done); } 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; } HashSet bots = Bot.GetBots(botNames); + if ((bots == null) || (bots.Count == 0)) { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } @@ -944,12 +1117,14 @@ namespace ArchiSteamFarm { IList results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseIdleBlacklistRemove(steamID, targetAppIDs))).ConfigureAwait(false); 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; } @@ -958,16 +1133,19 @@ namespace ArchiSteamFarm { } IReadOnlyCollection idleQueue = Bot.BotDatabase.GetIdlingPriorityAppIDs(); + return FormatBotResponse(idleQueue.Count > 0 ? string.Join(", ", idleQueue) : string.Format(Strings.ErrorIsEmpty, nameof(idleQueue))); } private static async Task ResponseIdleQueue(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; } @@ -975,12 +1153,14 @@ namespace ArchiSteamFarm { 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))); + return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; } private async Task ResponseIdleQueueAdd(ulong steamID, string targetAppIDs) { if ((steamID == 0) || string.IsNullOrEmpty(targetAppIDs)) { Bot.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(targetAppIDs)); + return null; } @@ -1005,16 +1185,19 @@ namespace ArchiSteamFarm { } await Bot.BotDatabase.AddIdlingPriorityAppIDs(appIDs).ConfigureAwait(false); + return FormatBotResponse(Strings.Done); } 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; } HashSet bots = Bot.GetBots(botNames); + if ((bots == null) || (bots.Count == 0)) { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } @@ -1022,12 +1205,14 @@ namespace ArchiSteamFarm { IList results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseIdleQueueAdd(steamID, targetAppIDs))).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 ResponseIdleQueueRemove(ulong steamID, string targetAppIDs) { if ((steamID == 0) || string.IsNullOrEmpty(targetAppIDs)) { Bot.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(targetAppIDs)); + return null; } @@ -1052,16 +1237,19 @@ namespace ArchiSteamFarm { } await Bot.BotDatabase.RemoveIdlingPriorityAppIDs(appIDs).ConfigureAwait(false); + return FormatBotResponse(Strings.Done); } 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; } HashSet bots = Bot.GetBots(botNames); + if ((bots == null) || (bots.Count == 0)) { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } @@ -1069,12 +1257,14 @@ namespace ArchiSteamFarm { IList results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseIdleQueueRemove(steamID, targetAppIDs))).ConfigureAwait(false); 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; } @@ -1091,16 +1281,19 @@ namespace ArchiSteamFarm { } Bot.SetUserInput(inputType, inputValue); + return FormatBotResponse(Strings.Done); } 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; } HashSet bots = Bot.GetBots(botNames); + if ((bots == null) || (bots.Count == 0)) { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } @@ -1108,12 +1301,14 @@ namespace ArchiSteamFarm { 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))); + 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; } @@ -1126,16 +1321,19 @@ namespace ArchiSteamFarm { } uint? level = await Bot.ArchiHandler.GetLevel().ConfigureAwait(false); + return FormatBotResponse(level.HasValue ? string.Format(Strings.BotLevel, level.Value) : Strings.WarningFailed); } private static async Task ResponseLevel(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; } @@ -1143,12 +1341,14 @@ namespace ArchiSteamFarm { IList results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseLevel(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 ResponseLoot(ulong steamID) { if (steamID == 0) { Bot.ArchiLogger.LogNullError(nameof(steamID)); + return null; } @@ -1165,16 +1365,19 @@ namespace ArchiSteamFarm { } (bool success, string output) = await Bot.Actions.SendTradeOffer(wantedTypes: Bot.BotConfig.LootableTypes).ConfigureAwait(false); + return FormatBotResponse(success ? output : string.Format(Strings.WarningFailedWithError, output)); } private static async Task ResponseLoot(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; } @@ -1182,12 +1385,14 @@ namespace ArchiSteamFarm { IList results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseLoot(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 ResponseLootByRealAppIDs(ulong steamID, string realAppIDsText) { if ((steamID == 0) || string.IsNullOrEmpty(realAppIDsText)) { Bot.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(realAppIDsText)); + return null; } @@ -1220,16 +1425,19 @@ namespace ArchiSteamFarm { } (bool success, string output) = await Bot.Actions.SendTradeOffer(wantedTypes: Bot.BotConfig.LootableTypes, wantedRealAppIDs: realAppIDs).ConfigureAwait(false); + return FormatBotResponse(success ? output : string.Format(Strings.WarningFailedWithError, output)); } private static async Task ResponseLootByRealAppIDs(ulong steamID, string botNames, string realAppIDsText) { if ((steamID == 0) || string.IsNullOrEmpty(botNames) || string.IsNullOrEmpty(realAppIDsText)) { ASF.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(botNames) + " || " + nameof(realAppIDsText)); + return null; } HashSet bots = Bot.GetBots(botNames); + if ((bots == null) || (bots.Count == 0)) { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } @@ -1237,12 +1445,14 @@ namespace ArchiSteamFarm { IList results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseLootByRealAppIDs(steamID, realAppIDsText))).ConfigureAwait(false); 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; } @@ -1255,16 +1465,19 @@ namespace ArchiSteamFarm { } Bot.SteamFriends.SetPersonaName(nickname); + return FormatBotResponse(Strings.Done); } 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; } HashSet bots = Bot.GetBots(botNames); + if ((bots == null) || (bots.Count == 0)) { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } @@ -1272,12 +1485,14 @@ namespace ArchiSteamFarm { 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))); + return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; } private async Task<(string Response, HashSet OwnedGameIDs)> ResponseOwns(ulong steamID, string query) { if ((steamID == 0) || string.IsNullOrEmpty(query)) { Bot.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(query)); + return (null, null); } @@ -1299,6 +1514,7 @@ namespace ArchiSteamFarm { if (ownedGames == null) { ownedGames = await Bot.ArchiWebHandler.HasValidApiKey().ConfigureAwait(false) ? await Bot.ArchiWebHandler.GetOwnedGames(Bot.SteamID).ConfigureAwait(false) : await Bot.ArchiWebHandler.GetMyOwnedGames().ConfigureAwait(false); + if ((ownedGames == null) || (ownedGames.Count == 0)) { return (FormatBotResponse(string.Format(Strings.ErrorIsEmpty, nameof(ownedGames))), null); } @@ -1335,6 +1551,7 @@ namespace ArchiSteamFarm { if (Bot.OwnedPackageIDs.ContainsKey(gameID)) { ownedGameIDs.Add(gameID); response.AppendLine(FormatBotResponse(string.Format(Strings.BotOwnedAlready, gameID))); + continue; } @@ -1362,10 +1579,12 @@ namespace ArchiSteamFarm { 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; } HashSet bots = Bot.GetBots(botNames); + if ((bots == null) || (bots.Count == 0)) { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } @@ -1373,22 +1592,26 @@ namespace ArchiSteamFarm { IList<(string Response, HashSet OwnedGameIDs)> results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseOwns(steamID, query))).ConfigureAwait(false); List<(string Response, HashSet OwnedGameIDs)> validResults = new List<(string Response, HashSet OwnedGameIDs)>(results.Where(result => !string.IsNullOrEmpty(result.Response))); + if (validResults.Count == 0) { return null; } Dictionary ownedGameCounts = new Dictionary(); + foreach (uint gameID in validResults.Where(validResult => (validResult.OwnedGameIDs != null) && (validResult.OwnedGameIDs.Count > 0)).SelectMany(validResult => validResult.OwnedGameIDs)) { ownedGameCounts[gameID] = ownedGameCounts.TryGetValue(gameID, out ushort count) ? ++count : (ushort) 1; } IEnumerable extraResponses = ownedGameCounts.Select(kv => FormatStaticResponse(string.Format(Strings.BotOwnsOverviewPerGame, kv.Value, validResults.Count, kv.Key))); + 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; } @@ -1401,16 +1624,19 @@ namespace ArchiSteamFarm { } string response = FormatBotResponse(string.Format(Strings.BotEncryptedPassword, ArchiCryptoHelper.ECryptoMethod.AES, ArchiCryptoHelper.Encrypt(ArchiCryptoHelper.ECryptoMethod.AES, Bot.BotConfig.DecryptedSteamPassword))) + FormatBotResponse(string.Format(Strings.BotEncryptedPassword, ArchiCryptoHelper.ECryptoMethod.ProtectedDataForCurrentUser, ArchiCryptoHelper.Encrypt(ArchiCryptoHelper.ECryptoMethod.ProtectedDataForCurrentUser, Bot.BotConfig.DecryptedSteamPassword))); + return response; } private static async Task ResponsePassword(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; } @@ -1418,12 +1644,14 @@ namespace ArchiSteamFarm { 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))); + 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; } @@ -1442,16 +1670,19 @@ namespace ArchiSteamFarm { } (bool success, string output) = await Bot.Actions.Pause(permanent, resumeInSeconds).ConfigureAwait(false); + return FormatBotResponse(success ? output : string.Format(Strings.WarningFailedWithError, output)); } 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; } HashSet bots = Bot.GetBots(botNames); + if ((bots == null) || (bots.Count == 0)) { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } @@ -1459,12 +1690,14 @@ namespace ArchiSteamFarm { 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))); + return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; } private async Task ResponsePlay(ulong steamID, IEnumerable gameIDs, string gameName = null) { if ((steamID == 0) || (gameIDs == null)) { Bot.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(gameIDs)); + return null; } @@ -1481,12 +1714,14 @@ namespace ArchiSteamFarm { } await Bot.ArchiHandler.PlayGames(gameIDs, gameName).ConfigureAwait(false); + return FormatBotResponse(Strings.Done); } private async Task ResponsePlay(ulong steamID, string targetGameIDs) { if ((steamID == 0) || string.IsNullOrEmpty(targetGameIDs)) { ASF.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(targetGameIDs)); + return null; } @@ -1510,6 +1745,7 @@ namespace ArchiSteamFarm { foreach (string game in games) { if (!uint.TryParse(game, out uint gameID) || (gameID == 0)) { gameName.Append((gameName.Length > 0 ? " " : "") + game); + continue; } @@ -1526,10 +1762,12 @@ namespace ArchiSteamFarm { 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; } HashSet bots = Bot.GetBots(botNames); + if ((bots == null) || (bots.Count == 0)) { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } @@ -1537,12 +1775,14 @@ namespace ArchiSteamFarm { 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))); + 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; } @@ -1583,43 +1823,55 @@ namespace ArchiSteamFarm { switch (index) { case 0: // Profile profile = privacySetting; + break; case 1: // OwnedGames, child of Profile + if (profile < privacySetting) { return FormatBotResponse(string.Format(Strings.ErrorIsInvalid, nameof(ownedGames))); } ownedGames = privacySetting; + break; case 2: // Playtime, child of OwnedGames + if (ownedGames < privacySetting) { return FormatBotResponse(string.Format(Strings.ErrorIsInvalid, nameof(playtime))); } playtime = privacySetting; + break; case 3: // FriendsList, child of Profile + if (profile < privacySetting) { return FormatBotResponse(string.Format(Strings.ErrorIsInvalid, nameof(ownedGames))); } friendsList = privacySetting; + break; case 4: // Inventory, child of Profile + if (profile < privacySetting) { return FormatBotResponse(string.Format(Strings.ErrorIsInvalid, nameof(inventory))); } inventory = privacySetting; + break; case 5: // InventoryGifts, child of Inventory + if (inventory < privacySetting) { return FormatBotResponse(string.Format(Strings.ErrorIsInvalid, nameof(inventoryGifts))); } inventoryGifts = privacySetting; + break; case 6: // Comments, child of Profile + if (profile < privacySetting) { return FormatBotResponse(string.Format(Strings.ErrorIsInvalid, nameof(comments))); } @@ -1628,36 +1880,44 @@ namespace ArchiSteamFarm { switch (privacySetting) { case Steam.UserPrivacy.PrivacySettings.EPrivacySetting.FriendsOnly: comments = Steam.UserPrivacy.ECommentPermission.FriendsOnly; + break; case Steam.UserPrivacy.PrivacySettings.EPrivacySetting.Private: comments = Steam.UserPrivacy.ECommentPermission.Private; + break; case Steam.UserPrivacy.PrivacySettings.EPrivacySetting.Public: comments = Steam.UserPrivacy.ECommentPermission.Public; + break; default: Bot.ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(privacySetting), privacySetting)); + return FormatBotResponse(string.Format(Strings.ErrorIsInvalid, nameof(privacySetting))); } break; default: Bot.ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(index), index)); + return FormatBotResponse(string.Format(Strings.ErrorIsInvalid, nameof(index))); } } Steam.UserPrivacy userPrivacy = new Steam.UserPrivacy(new Steam.UserPrivacy.PrivacySettings(profile, ownedGames, playtime, friendsList, inventory, inventoryGifts), comments); + return FormatBotResponse(await Bot.ArchiWebHandler.ChangePrivacySettings(userPrivacy).ConfigureAwait(false) ? Strings.Success : Strings.WarningFailed); } private static async Task ResponsePrivacy(ulong steamID, string botNames, string privacySettingsText) { if ((steamID == 0) || string.IsNullOrEmpty(botNames) || string.IsNullOrEmpty(privacySettingsText)) { ASF.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(botNames) + " || " + nameof(privacySettingsText)); + return null; } HashSet bots = Bot.GetBots(botNames); + if ((bots == null) || (bots.Count == 0)) { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } @@ -1665,6 +1925,7 @@ namespace ArchiSteamFarm { 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))); + return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; } @@ -1672,6 +1933,7 @@ namespace ArchiSteamFarm { 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; } @@ -1711,6 +1973,7 @@ namespace ArchiSteamFarm { while (!string.IsNullOrEmpty(key) && (currentBot != null)) { if (redeemFlags.HasFlag(ERedeemFlags.Validate) && !Utilities.IsValidCdKey(key)) { key = keysEnumerator.MoveNext() ? keysEnumerator.Current : null; // Next key + continue; // Keep current bot } @@ -1718,6 +1981,7 @@ namespace ArchiSteamFarm { currentBot = null; // Either bot will be changed, or loop aborted } else { ArchiHandler.PurchaseResponseCallback result = await currentBot.Actions.RedeemKey(key).ConfigureAwait(false); + if (result == null) { response.AppendLine(FormatBotResponse(string.Format(Strings.BotRedeem, key, EPurchaseResultDetail.Timeout), currentBot.BotName)); currentBot = null; // Either bot will be changed, or loop aborted @@ -1741,6 +2005,7 @@ namespace ArchiSteamFarm { case EPurchaseResultDetail.DuplicateActivationCode: case EPurchaseResultDetail.NoDetail: // OK case EPurchaseResultDetail.Timeout: + if ((result.Items != null) && (result.Items.Count > 0)) { response.AppendLine(FormatBotResponse(string.Format(Strings.BotRedeemWithItems, key, result.Result + "/" + result.PurchaseResultDetail, string.Join(", ", result.Items)), currentBot.BotName)); } else { @@ -1762,6 +2027,7 @@ namespace ArchiSteamFarm { case EPurchaseResultDetail.AlreadyPurchased: case EPurchaseResultDetail.DoesNotOwnRequiredApp: case EPurchaseResultDetail.RestrictedCountry: + if ((result.Items != null) && (result.Items.Count > 0)) { response.AppendLine(FormatBotResponse(string.Format(Strings.BotRedeemWithItems, key, result.Result + "/" + result.PurchaseResultDetail, string.Join(", ", result.Items)), currentBot.BotName)); } else { @@ -1770,6 +2036,7 @@ namespace ArchiSteamFarm { if (!forward || (keepMissingGames && (result.PurchaseResultDetail != EPurchaseResultDetail.AlreadyPurchased))) { key = keysEnumerator.MoveNext() ? keysEnumerator.Current : null; // Next key + break; // Next bot (if needed) } @@ -1780,10 +2047,13 @@ namespace ArchiSteamFarm { Dictionary items = result.Items ?? new Dictionary(); bool alreadyHandled = false; + foreach (Bot innerBot in Bot.Bots.Where(bot => (bot.Value != currentBot) && (!redeemFlags.HasFlag(ERedeemFlags.SkipInitial) || (bot.Value != Bot)) && !rateLimitedBots.Contains(bot.Value) && bot.Value.IsConnectedAndLoggedOn && bot.Value.Commands.IsOperator(steamID) && ((items.Count == 0) || items.Keys.Any(packageID => !bot.Value.OwnedPackageIDs.ContainsKey(packageID)))).OrderBy(bot => bot.Key).Select(bot => bot.Value)) { ArchiHandler.PurchaseResponseCallback otherResult = await innerBot.Actions.RedeemKey(key).ConfigureAwait(false); + if (otherResult == null) { response.AppendLine(FormatBotResponse(string.Format(Strings.BotRedeem, key, EResult.Timeout + "/" + EPurchaseResultDetail.Timeout), innerBot.BotName)); + continue; } @@ -1793,9 +2063,11 @@ namespace ArchiSteamFarm { case EPurchaseResultDetail.NoDetail: // OK alreadyHandled = true; // This key is already handled, as we either redeemed it or we're sure it's dupe/invalid unusedKeys.Remove(key); + break; case EPurchaseResultDetail.RateLimited: rateLimitedBots.Add(innerBot); + break; } @@ -1819,6 +2091,7 @@ namespace ArchiSteamFarm { } key = keysEnumerator.MoveNext() ? keysEnumerator.Current : null; // Next key + break; // Next bot (if needed) case EPurchaseResultDetail.RateLimited: rateLimitedBots.Add(currentBot); @@ -1835,6 +2108,7 @@ namespace ArchiSteamFarm { unusedKeys.Remove(key); key = keysEnumerator.MoveNext() ? keysEnumerator.Current : null; // Next key + break; // Next bot (if needed) } } @@ -1866,10 +2140,12 @@ namespace ArchiSteamFarm { 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; } HashSet bots = Bot.GetBots(botNames); + if ((bots == null) || (bots.Count == 0)) { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } @@ -1877,12 +2153,14 @@ namespace ArchiSteamFarm { 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))); + return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; } private static string ResponseRestart(ulong steamID) { if (steamID == 0) { ASF.ArchiLogger.LogNullError(nameof(steamID)); + return null; } @@ -1891,12 +2169,14 @@ namespace ArchiSteamFarm { } (bool success, string output) = Actions.Restart(); + return FormatStaticResponse(success ? output : string.Format(Strings.WarningFailedWithError, output)); } private string ResponseResume(ulong steamID) { if (steamID == 0) { Bot.ArchiLogger.LogNullError(nameof(steamID)); + return null; } @@ -1905,16 +2185,19 @@ namespace ArchiSteamFarm { } (bool success, string output) = Bot.Actions.Resume(); + return FormatBotResponse(success ? output : string.Format(Strings.WarningFailedWithError, output)); } private static async Task ResponseResume(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; } @@ -1922,12 +2205,14 @@ namespace ArchiSteamFarm { 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))); + 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; } @@ -1936,16 +2221,19 @@ namespace ArchiSteamFarm { } (bool success, string output) = Bot.Actions.Start(); + return FormatBotResponse(success ? output : string.Format(Strings.WarningFailedWithError, output)); } private static async Task ResponseStart(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; } @@ -1953,12 +2241,14 @@ namespace ArchiSteamFarm { 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))); + 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; } @@ -1967,12 +2257,14 @@ namespace ArchiSteamFarm { } ushort memoryInMegabytes = (ushort) (GC.GetTotalMemory(false) / 1024 / 1024); + return FormatBotResponse(string.Format(Strings.BotStats, memoryInMegabytes)); } private (string Response, Bot Bot) ResponseStatus(ulong steamID) { if (steamID == 0) { Bot.ArchiLogger.LogNullError(nameof(steamID)); + return (null, Bot); } @@ -2009,16 +2301,19 @@ namespace ArchiSteamFarm { } CardsFarmer.Game soloGame = Bot.CardsFarmer.CurrentGamesFarming.First(); + return (FormatBotResponse(string.Format(Strings.BotStatusIdling, soloGame.AppID, soloGame.GameName, soloGame.CardsRemaining, Bot.CardsFarmer.GamesToFarm.Count, Bot.CardsFarmer.GamesToFarm.Sum(game => game.CardsRemaining), Bot.CardsFarmer.TimeRemaining.ToHumanReadable())), Bot); } private static async Task ResponseStatus(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; } @@ -2026,6 +2321,7 @@ namespace ArchiSteamFarm { 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))); + if (validResults.Count == 0) { return null; } @@ -2033,12 +2329,14 @@ namespace ArchiSteamFarm { HashSet botsRunning = validResults.Where(result => result.Bot.KeepRunning).Select(result => result.Bot).ToHashSet(); string extraResponse = string.Format(Strings.BotStatusOverview, botsRunning.Count, validResults.Count, botsRunning.Sum(bot => bot.CardsFarmer.GamesToFarm.Count), botsRunning.Sum(bot => bot.CardsFarmer.GamesToFarm.Sum(game => game.CardsRemaining))); + return string.Join(Environment.NewLine, validResults.Select(result => result.Response).Union(extraResponse.ToEnumerable())); } private string ResponseStop(ulong steamID) { if (steamID == 0) { Bot.ArchiLogger.LogNullError(nameof(steamID)); + return null; } @@ -2047,16 +2345,19 @@ namespace ArchiSteamFarm { } (bool success, string output) = Bot.Actions.Stop(); + return FormatBotResponse(success ? output : string.Format(Strings.WarningFailedWithError, output)); } private static async Task ResponseStop(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; } @@ -2064,12 +2365,14 @@ namespace ArchiSteamFarm { 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))); + 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; } @@ -2098,16 +2401,19 @@ namespace ArchiSteamFarm { } (bool success, string output) = await Bot.Actions.SendTradeOffer(targetSteamID: targetBot.SteamID, wantedTypes: Bot.BotConfig.TransferableTypes).ConfigureAwait(false); + return FormatBotResponse(success ? output : string.Format(Strings.WarningFailedWithError, output)); } 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; } HashSet bots = Bot.GetBots(botNames); + if ((bots == null) || (bots.Count == 0)) { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } @@ -2115,12 +2421,14 @@ namespace ArchiSteamFarm { 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))); + return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; } private async Task ResponseTransferByRealAppIDs(ulong steamID, IReadOnlyCollection realAppIDs, Bot targetBot) { if ((steamID == 0) || (realAppIDs == null) || (realAppIDs.Count == 0) || (targetBot == null)) { Bot.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(realAppIDs) + " || " + nameof(targetBot)); + return null; } @@ -2145,12 +2453,14 @@ namespace ArchiSteamFarm { } (bool success, string output) = await Bot.Actions.SendTradeOffer(targetSteamID: targetBot.SteamID, wantedTypes: Bot.BotConfig.TransferableTypes, wantedRealAppIDs: realAppIDs).ConfigureAwait(false); + return FormatBotResponse(success ? output : string.Format(Strings.WarningFailedWithError, output)); } private async Task ResponseTransferByRealAppIDs(ulong steamID, string realAppIDsText, string botNameTo) { if ((steamID == 0) || string.IsNullOrEmpty(realAppIDsText) || string.IsNullOrEmpty(botNameTo)) { Bot.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(realAppIDsText) + " || " + nameof(botNameTo)); + return null; } @@ -2163,11 +2473,13 @@ namespace ArchiSteamFarm { } string[] appIDTexts = realAppIDsText.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + if (appIDTexts.Length == 0) { return FormatBotResponse(string.Format(Strings.ErrorIsEmpty, nameof(appIDTexts))); } HashSet realAppIDs = new HashSet(); + foreach (string appIDText in appIDTexts) { if (!uint.TryParse(appIDText, out uint appID) || (appID == 0)) { return FormatBotResponse(string.Format(Strings.ErrorIsInvalid, nameof(appID))); @@ -2182,20 +2494,24 @@ namespace ArchiSteamFarm { private static async Task ResponseTransferByRealAppIDs(ulong steamID, string botNames, string realAppIDsText, string botNameTo) { 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; } HashSet bots = Bot.GetBots(botNames); + if ((bots == null) || (bots.Count == 0)) { return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; } string[] appIDTexts = realAppIDsText.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + if (appIDTexts.Length == 0) { return FormatStaticResponse(string.Format(Strings.ErrorIsEmpty, nameof(appIDTexts))); } HashSet realAppIDs = new HashSet(); + foreach (string appIDText in appIDTexts) { if (!uint.TryParse(appIDText, out uint appID) || (appID == 0)) { return FormatStaticResponse(string.Format(Strings.ErrorIsInvalid, nameof(appID))); @@ -2211,12 +2527,14 @@ namespace ArchiSteamFarm { IList results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseTransferByRealAppIDs(steamID, realAppIDs, targetBot))).ConfigureAwait(false); 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; } @@ -2226,6 +2544,7 @@ namespace ArchiSteamFarm { private async Task ResponseUnpackBoosters(ulong steamID) { if (steamID == 0) { Bot.ArchiLogger.LogNullError(nameof(steamID)); + return null; } @@ -2238,6 +2557,7 @@ namespace ArchiSteamFarm { } HashSet inventory = await Bot.ArchiWebHandler.GetInventory(Bot.SteamID, wantedTypes: new HashSet { Steam.Asset.EType.BoosterPack }).ConfigureAwait(false); + if ((inventory == null) || (inventory.Count == 0)) { return FormatBotResponse(string.Format(Strings.ErrorIsEmpty, nameof(inventory))); } @@ -2254,10 +2574,12 @@ namespace ArchiSteamFarm { private static async Task ResponseUnpackBoosters(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; } @@ -2265,12 +2587,14 @@ namespace ArchiSteamFarm { IList results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseUnpackBoosters(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 async Task ResponseUpdate(ulong steamID) { if (steamID == 0) { ASF.ArchiLogger.LogNullError(nameof(steamID)); + return null; } @@ -2279,12 +2603,14 @@ namespace ArchiSteamFarm { } (bool success, string message) = await Actions.Update().ConfigureAwait(false); + return FormatStaticResponse((success ? Strings.Success : Strings.WarningFailed) + (!string.IsNullOrEmpty(message) ? " " + message : "")); } private string ResponseVersion(ulong steamID) { if (steamID == 0) { Bot.ArchiLogger.LogNullError(nameof(steamID)); + return null; } @@ -2294,6 +2620,7 @@ namespace ArchiSteamFarm { private string ResponseWalletBalance(ulong steamID) { if (steamID == 0) { Bot.ArchiLogger.LogNullError(nameof(steamID)); + return null; } @@ -2307,10 +2634,12 @@ namespace ArchiSteamFarm { private static async Task ResponseWalletBalance(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; } @@ -2318,6 +2647,7 @@ namespace ArchiSteamFarm { 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))); + return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; } diff --git a/ArchiSteamFarm/Debugging.cs b/ArchiSteamFarm/Debugging.cs index 0d6d67d6f..251a27eff 100644 --- a/ArchiSteamFarm/Debugging.cs +++ b/ArchiSteamFarm/Debugging.cs @@ -35,6 +35,7 @@ namespace ArchiSteamFarm { public void WriteLine(string category, string msg) { if (string.IsNullOrEmpty(category) && string.IsNullOrEmpty(msg)) { ASF.ArchiLogger.LogNullError(nameof(category) + " && " + nameof(msg)); + return; } diff --git a/ArchiSteamFarm/GitHub.cs b/ArchiSteamFarm/GitHub.cs index 4547fb25b..b0fa6a4f0 100644 --- a/ArchiSteamFarm/GitHub.cs +++ b/ArchiSteamFarm/GitHub.cs @@ -41,12 +41,14 @@ namespace ArchiSteamFarm { } List response = await GetReleasesFromURL(releaseURL).ConfigureAwait(false); + return response?.FirstOrDefault(); } internal static async Task GetRelease(string version) { if (string.IsNullOrEmpty(version)) { ASF.ArchiLogger.LogNullError(nameof(version)); + return null; } @@ -56,16 +58,19 @@ namespace ArchiSteamFarm { internal static async Task> GetReleases(byte count) { if (count == 0) { ASF.ArchiLogger.LogNullError(nameof(count)); + return null; } string releaseURL = SharedInfo.GithubReleaseURL + "?per_page=" + count; + return await GetReleasesFromURL(releaseURL).ConfigureAwait(false); } private static MarkdownDocument ExtractChangelogFromBody(string markdownText) { if (string.IsNullOrEmpty(markdownText)) { ASF.ArchiLogger.LogNullError(nameof(markdownText)); + return null; } @@ -84,20 +89,24 @@ namespace ArchiSteamFarm { private static async Task GetReleaseFromURL(string releaseURL) { if (string.IsNullOrEmpty(nameof(releaseURL))) { ASF.ArchiLogger.LogNullError(nameof(releaseURL)); + return null; } WebBrowser.ObjectResponse objectResponse = await Program.WebBrowser.UrlGetToJsonObject(releaseURL).ConfigureAwait(false); + return objectResponse?.Content; } private static async Task> GetReleasesFromURL(string releaseURL) { if (string.IsNullOrEmpty(nameof(releaseURL))) { ASF.ArchiLogger.LogNullError(nameof(releaseURL)); + return null; } WebBrowser.ObjectResponse> objectResponse = await Program.WebBrowser.UrlGetToJsonObject>(releaseURL).ConfigureAwait(false); + return objectResponse?.Content; } @@ -123,6 +132,7 @@ namespace ArchiSteamFarm { if (Changelog == null) { ASF.ArchiLogger.LogNullError(nameof(Changelog)); + return null; } @@ -144,6 +154,7 @@ namespace ArchiSteamFarm { if (Changelog == null) { ASF.ArchiLogger.LogNullError(nameof(Changelog)); + return null; } diff --git a/ArchiSteamFarm/GlobalConfig.cs b/ArchiSteamFarm/GlobalConfig.cs index a1d212a83..0e57d8e81 100644 --- a/ArchiSteamFarm/GlobalConfig.cs +++ b/ArchiSteamFarm/GlobalConfig.cs @@ -156,6 +156,7 @@ namespace ArchiSteamFarm { uri = new Uri(WebProxyText); } catch (UriFormatException e) { ASF.ArchiLogger.LogGenericException(e); + return null; } @@ -195,6 +196,7 @@ namespace ArchiSteamFarm { [JsonProperty] internal string WebProxyPassword { get => _WebProxyPassword; + set { IsWebProxyPasswordSet = true; _WebProxyPassword = value; @@ -208,9 +210,11 @@ namespace ArchiSteamFarm { [JsonProperty(PropertyName = SharedInfo.UlongCompatibilityStringPrefix + nameof(SteamOwnerID), Required = Required.DisallowNull)] private string SSteamOwnerID { get => SteamOwnerID.ToString(); + set { if (string.IsNullOrEmpty(value) || !ulong.TryParse(value, out ulong result)) { ASF.ArchiLogger.LogGenericError(string.Format(Strings.ErrorIsInvalid, nameof(SSteamOwnerID))); + return; } @@ -255,6 +259,7 @@ namespace ArchiSteamFarm { internal static async Task Load(string filePath) { if (string.IsNullOrEmpty(filePath)) { ASF.ArchiLogger.LogNullError(nameof(filePath)); + return null; } @@ -268,28 +273,34 @@ namespace ArchiSteamFarm { globalConfig = JsonConvert.DeserializeObject(await RuntimeCompatibility.File.ReadAllTextAsync(filePath).ConfigureAwait(false)); } catch (Exception e) { ASF.ArchiLogger.LogGenericException(e); + return null; } if (globalConfig == null) { ASF.ArchiLogger.LogNullError(nameof(globalConfig)); + return null; } (bool valid, string errorMessage) = globalConfig.CheckValidation(); + if (!valid) { ASF.ArchiLogger.LogGenericError(errorMessage); + return null; } globalConfig.ShouldSerializeEverything = false; globalConfig.ShouldSerializeSensitiveDetails = false; + return globalConfig; } internal static async Task Write(string filePath, GlobalConfig globalConfig) { if (string.IsNullOrEmpty(filePath) || (globalConfig == null)) { ASF.ArchiLogger.LogNullError(nameof(filePath) + " || " + nameof(globalConfig)); + return false; } @@ -308,6 +319,7 @@ namespace ArchiSteamFarm { } } catch (Exception e) { ASF.ArchiLogger.LogGenericException(e); + return false; } finally { WriteSemaphore.Release(); diff --git a/ArchiSteamFarm/GlobalDatabase.cs b/ArchiSteamFarm/GlobalDatabase.cs index 7e037ec3d..c550a4846 100644 --- a/ArchiSteamFarm/GlobalDatabase.cs +++ b/ArchiSteamFarm/GlobalDatabase.cs @@ -72,6 +72,7 @@ namespace ArchiSteamFarm { internal static async Task CreateOrLoad(string filePath) { if (string.IsNullOrEmpty(filePath)) { ASF.ArchiLogger.LogNullError(nameof(filePath)); + return null; } @@ -85,21 +86,25 @@ namespace ArchiSteamFarm { globalDatabase = JsonConvert.DeserializeObject(await RuntimeCompatibility.File.ReadAllTextAsync(filePath).ConfigureAwait(false)); } catch (Exception e) { ASF.ArchiLogger.LogGenericException(e); + return null; } if (globalDatabase == null) { ASF.ArchiLogger.LogNullError(nameof(globalDatabase)); + return null; } globalDatabase.FilePath = filePath; + return globalDatabase; } internal HashSet GetPackageIDs(uint appID) { if (appID == 0) { ASF.ArchiLogger.LogNullError(nameof(appID)); + return null; } @@ -109,6 +114,7 @@ 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; } @@ -122,6 +128,7 @@ namespace ArchiSteamFarm { } Dictionary AppIDs)> packagesData = await bot.GetPackagesData(packageIDs).ConfigureAwait(false); + if ((packagesData == null) || (packagesData.Count == 0)) { return; } @@ -149,8 +156,10 @@ namespace ArchiSteamFarm { private async Task Save() { string json = JsonConvert.SerializeObject(this); + if (string.IsNullOrEmpty(json)) { ASF.ArchiLogger.LogNullError(nameof(json)); + return; } diff --git a/ArchiSteamFarm/Helpers/ArchiCachable.cs b/ArchiSteamFarm/Helpers/ArchiCachable.cs index 17296d1ff..83a3dde7d 100644 --- a/ArchiSteamFarm/Helpers/ArchiCachable.cs +++ b/ArchiSteamFarm/Helpers/ArchiCachable.cs @@ -66,6 +66,7 @@ namespace ArchiSteamFarm.Helpers { } (bool success, T result) = await ResolveFunction().ConfigureAwait(false); + if (!success) { return (false, InitializedValue); } diff --git a/ArchiSteamFarm/IPC/ArchiKestrel.cs b/ArchiSteamFarm/IPC/ArchiKestrel.cs index d0e28efab..a7ef26984 100644 --- a/ArchiSteamFarm/IPC/ArchiKestrel.cs +++ b/ArchiSteamFarm/IPC/ArchiKestrel.cs @@ -62,6 +62,7 @@ namespace ArchiSteamFarm.IPC { IWebHostBuilder builder = new WebHostBuilder(); string customDirectory = Path.Combine(Directory.GetCurrentDirectory(), SharedInfo.WebsiteDirectory); + if (Directory.Exists(customDirectory)) { WebsiteDirectory = customDirectory; } @@ -106,6 +107,7 @@ namespace ArchiSteamFarm.IPC { } catch (Exception e) { ASF.ArchiLogger.LogGenericException(e); kestrelWebHost.Dispose(); + return; } diff --git a/ArchiSteamFarm/IPC/Controllers/Api/ASFController.cs b/ArchiSteamFarm/IPC/Controllers/Api/ASFController.cs index 37df1566c..bed0842ec 100644 --- a/ArchiSteamFarm/IPC/Controllers/Api/ASFController.cs +++ b/ArchiSteamFarm/IPC/Controllers/Api/ASFController.cs @@ -39,6 +39,7 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { uint memoryUsage = (uint) GC.GetTotalMemory(false) / 1024; ASFResponse result = new ASFResponse(SharedInfo.BuildInfo.Variant, Program.GlobalConfig, memoryUsage, RuntimeCompatibility.ProcessStartTime, SharedInfo.Version); + return Ok(new GenericResponse(result)); } @@ -51,10 +52,12 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { 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)))); } (bool valid, string errorMessage) = request.GlobalConfig.CheckValidation(); + if (!valid) { return BadRequest(new GenericResponse(false, errorMessage)); } @@ -69,6 +72,7 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { string filePath = Path.Combine(SharedInfo.ConfigDirectory, SharedInfo.GlobalConfigFileName); bool result = await GlobalConfig.Write(filePath, request.GlobalConfig).ConfigureAwait(false); + return Ok(new GenericResponse(result)); } @@ -79,6 +83,7 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { [ProducesResponseType(typeof(GenericResponse), 200)] public ActionResult ExitPost() { (bool success, string output) = Actions.Exit(); + return Ok(new GenericResponse(success, output)); } @@ -89,6 +94,7 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { [ProducesResponseType(typeof(GenericResponse), 200)] public ActionResult RestartPost() { (bool success, string output) = Actions.Restart(); + return Ok(new GenericResponse(success, output)); } diff --git a/ArchiSteamFarm/IPC/Controllers/Api/BotController.cs b/ArchiSteamFarm/IPC/Controllers/Api/BotController.cs index f097fe461..d125c79b3 100644 --- a/ArchiSteamFarm/IPC/Controllers/Api/BotController.cs +++ b/ArchiSteamFarm/IPC/Controllers/Api/BotController.cs @@ -41,15 +41,18 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { 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)))); } 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 => bot.DeleteAllRelatedFiles())).ConfigureAwait(false); + return Ok(new GenericResponse(results.All(result => result))); } @@ -61,10 +64,12 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { 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)))); } HashSet bots = Bot.GetBots(botNames); + if (bots == null) { return BadRequest(new GenericResponse>(false, string.Format(Strings.ErrorIsInvalid, nameof(bots)))); } @@ -81,10 +86,12 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { 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)))); } (bool valid, string errorMessage) = request.BotConfig.CheckValidation(); + if (!valid) { return BadRequest(new GenericResponse>(false, errorMessage)); } @@ -138,15 +145,18 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { 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)))); } 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.DeleteRedeemedKeysFiles))).ConfigureAwait(false); + return Ok(results.All(result => result) ? new GenericResponse(true) : new GenericResponse(false, Strings.WarningFailed)); } @@ -158,10 +168,12 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { 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)))); } HashSet bots = Bot.GetBots(botNames); + if ((bots == null) || (bots.Count == 0)) { return BadRequest(new GenericResponse>(false, string.Format(Strings.BotNotFound, botNames))); } @@ -187,6 +199,7 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { 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)))); } @@ -195,11 +208,13 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { } HashSet bots = Bot.GetBots(botNames); + if ((bots == null) || (bots.Count == 0)) { return BadRequest(new GenericResponse>(false, string.Format(Strings.BotNotFound, botNames))); } IOrderedDictionary validGamesToRedeemInBackground = Bot.ValidateGamesToRedeemInBackground(request.GamesToRedeemInBackground); + if ((validGamesToRedeemInBackground == null) || (validGamesToRedeemInBackground.Count == 0)) { return BadRequest(new GenericResponse>(false, string.Format(Strings.ErrorIsEmpty, nameof(validGamesToRedeemInBackground)))); } @@ -224,15 +239,18 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { 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)))); } 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 Output)> results = await Utilities.InParallel(bots.Select(bot => bot.Actions.Pause(request.Permanent, request.ResumeInSeconds))).ConfigureAwait(false); + return Ok(new GenericResponse(results.All(result => result.Success), string.Join(Environment.NewLine, results.Select(result => result.Output)))); } @@ -249,6 +267,7 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { 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)))); } @@ -257,6 +276,7 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { } HashSet bots = Bot.GetBots(botNames); + if ((bots == null) || (bots.Count == 0)) { return BadRequest(new GenericResponse>>(false, string.Format(Strings.BotNotFound, botNames))); } @@ -287,15 +307,18 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { 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)))); } 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 Output)> results = await Utilities.InParallel(bots.Select(bot => Task.Run(bot.Actions.Resume))).ConfigureAwait(false); + return Ok(new GenericResponse(results.All(result => result.Success), string.Join(Environment.NewLine, results.Select(result => result.Output)))); } @@ -307,15 +330,18 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { 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)))); } 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 Output)> results = await Utilities.InParallel(bots.Select(bot => Task.Run(bot.Actions.Start))).ConfigureAwait(false); + return Ok(new GenericResponse(results.All(result => result.Success), string.Join(Environment.NewLine, results.Select(result => result.Output)))); } @@ -327,15 +353,18 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { 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)))); } 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 Output)> results = await Utilities.InParallel(bots.Select(bot => Task.Run(bot.Actions.Stop))).ConfigureAwait(false); + return Ok(new GenericResponse(results.All(result => result.Success), string.Join(Environment.NewLine, results.Select(result => result.Output)))); } } diff --git a/ArchiSteamFarm/IPC/Controllers/Api/CommandController.cs b/ArchiSteamFarm/IPC/Controllers/Api/CommandController.cs index 5d807155e..ef7f350e9 100644 --- a/ArchiSteamFarm/IPC/Controllers/Api/CommandController.cs +++ b/ArchiSteamFarm/IPC/Controllers/Api/CommandController.cs @@ -41,6 +41,7 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { public async Task>> CommandPost(string command) { if (string.IsNullOrEmpty(command)) { ASF.ArchiLogger.LogNullError(nameof(command)); + return BadRequest(new GenericResponse(false, string.Format(Strings.ErrorIsEmpty, nameof(command)))); } @@ -49,6 +50,7 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { } Bot targetBot = Bot.Bots.OrderBy(bot => bot.Key).Select(bot => bot.Value).FirstOrDefault(); + if (targetBot == null) { return BadRequest(new GenericResponse(false, Strings.ErrorNoBotsDefined)); } @@ -58,6 +60,7 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { } string response = await targetBot.Commands.Response(Program.GlobalConfig.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 3003517f4..4a362d023 100644 --- a/ArchiSteamFarm/IPC/Controllers/Api/NLogController.cs +++ b/ArchiSteamFarm/IPC/Controllers/Api/NLogController.cs @@ -55,8 +55,10 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { try { using (WebSocket webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync().ConfigureAwait(false)) { SemaphoreSlim sendSemaphore = new SemaphoreSlim(1, 1); + if (!ActiveLogWebSockets.TryAdd(webSocket, sendSemaphore)) { sendSemaphore.Dispose(); + return new EmptyResult(); } @@ -72,10 +74,12 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { if (result.MessageType != WebSocketMessageType.Close) { await webSocket.CloseAsync(WebSocketCloseStatus.InvalidMessageType, "You're not supposed to be sending any message but Close!", CancellationToken.None).ConfigureAwait(false); + break; } await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None).ConfigureAwait(false); + break; } } finally { @@ -95,6 +99,7 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { internal static async void OnNewHistoryEntry(object sender, HistoryTarget.NewHistoryEntryArgs newHistoryEntryArgs) { if ((sender == null) || (newHistoryEntryArgs == null)) { ASF.ArchiLogger.LogNullError(nameof(sender) + " || " + nameof(newHistoryEntryArgs)); + return; } @@ -109,6 +114,7 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { 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; } @@ -134,6 +140,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; } diff --git a/ArchiSteamFarm/IPC/Controllers/Api/StructureController.cs b/ArchiSteamFarm/IPC/Controllers/Api/StructureController.cs index 35eb0a110..a101375c5 100644 --- a/ArchiSteamFarm/IPC/Controllers/Api/StructureController.cs +++ b/ArchiSteamFarm/IPC/Controllers/Api/StructureController.cs @@ -38,6 +38,7 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { 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)))); } diff --git a/ArchiSteamFarm/IPC/Controllers/Api/TypeController.cs b/ArchiSteamFarm/IPC/Controllers/Api/TypeController.cs index eb5edd182..86995ba54 100644 --- a/ArchiSteamFarm/IPC/Controllers/Api/TypeController.cs +++ b/ArchiSteamFarm/IPC/Controllers/Api/TypeController.cs @@ -42,6 +42,7 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { 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)))); } @@ -85,6 +86,7 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { TypeResponse.TypeProperties properties = new TypeResponse.TypeProperties(baseType, customAttributes.Count > 0 ? customAttributes : null, underlyingType); TypeResponse response = new TypeResponse(body, properties); + return Ok(new GenericResponse(response)); } } diff --git a/ArchiSteamFarm/IPC/Controllers/Api/WWWController.cs b/ArchiSteamFarm/IPC/Controllers/Api/WWWController.cs index fb90fa535..f9b33f1d5 100644 --- a/ArchiSteamFarm/IPC/Controllers/Api/WWWController.cs +++ b/ArchiSteamFarm/IPC/Controllers/Api/WWWController.cs @@ -43,10 +43,12 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { 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)))); } string directoryPath = Path.Combine(ArchiKestrel.WebsiteDirectory, directory); + if (!Directory.Exists(directoryPath)) { return BadRequest(new GenericResponse>(false, string.Format(Strings.ErrorIsInvalid, directory))); } @@ -60,6 +62,7 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { } HashSet result = files.Select(Path.GetFileName).ToHashSet(); + return Ok(new GenericResponse>(result)); } @@ -77,11 +80,13 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { } List response = await GitHub.GetReleases(count).ConfigureAwait(false); + if ((response == null) || (response.Count == 0)) { return BadRequest(new GenericResponse>(false, string.Format(Strings.ErrorRequestFailedTooManyTimes, WebBrowser.MaxTries))); } List result = response.Select(singleResponse => new GitHubReleaseResponse(singleResponse)).ToList(); + return Ok(new GenericResponse>(result)); } @@ -99,6 +104,7 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { } GitHub.ReleaseResponse releaseResponse = await GitHub.GetRelease(version).ConfigureAwait(false); + if (releaseResponse == null) { return BadRequest(new GenericResponse(false, string.Format(Strings.ErrorRequestFailedTooManyTimes, WebBrowser.MaxTries))); } @@ -118,6 +124,7 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { 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)))); } @@ -126,6 +133,7 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { } WebBrowser.StringResponse urlResponse = await Program.WebBrowser.UrlGetToString(request.URL).ConfigureAwait(false); + if (urlResponse?.Content == null) { return BadRequest(new GenericResponse(false, string.Format(Strings.ErrorRequestFailedTooManyTimes, WebBrowser.MaxTries))); } diff --git a/ArchiSteamFarm/IPC/Middleware/ApiAuthenticationMiddleware.cs b/ArchiSteamFarm/IPC/Middleware/ApiAuthenticationMiddleware.cs index ca2379f59..c2ad5f75e 100644 --- a/ArchiSteamFarm/IPC/Middleware/ApiAuthenticationMiddleware.cs +++ b/ArchiSteamFarm/IPC/Middleware/ApiAuthenticationMiddleware.cs @@ -57,6 +57,7 @@ namespace ArchiSteamFarm.IPC.Middleware { public async Task InvokeAsync(HttpContext context) { if (context == null) { ASF.ArchiLogger.LogNullError(nameof(context)); + return; } @@ -64,6 +65,7 @@ namespace ArchiSteamFarm.IPC.Middleware { if (authenticationStatus != HttpStatusCode.OK) { await context.Response.Generate(authenticationStatus).ConfigureAwait(false); + return; } @@ -73,6 +75,7 @@ namespace ArchiSteamFarm.IPC.Middleware { private static async Task GetAuthenticationStatus(HttpContext context) { if (context == null) { ASF.ArchiLogger.LogNullError(nameof(context)); + return HttpStatusCode.InternalServerError; } diff --git a/ArchiSteamFarm/IPC/Responses/GenericResponse.cs b/ArchiSteamFarm/IPC/Responses/GenericResponse.cs index d2bfe1726..cfbf34667 100644 --- a/ArchiSteamFarm/IPC/Responses/GenericResponse.cs +++ b/ArchiSteamFarm/IPC/Responses/GenericResponse.cs @@ -62,6 +62,7 @@ namespace ArchiSteamFarm.IPC.Responses { if (!string.IsNullOrEmpty(message)) { Message = message; + return; } diff --git a/ArchiSteamFarm/IPC/Startup.cs b/ArchiSteamFarm/IPC/Startup.cs index cc410d2bc..21e18d70c 100644 --- a/ArchiSteamFarm/IPC/Startup.cs +++ b/ArchiSteamFarm/IPC/Startup.cs @@ -41,6 +41,7 @@ namespace ArchiSteamFarm.IPC { public void Configure(IApplicationBuilder app, IHostingEnvironment env) { if ((app == null) || (env == null)) { ASF.ArchiLogger.LogNullError(nameof(app) + " || " + nameof(env)); + return; } @@ -48,6 +49,7 @@ namespace ArchiSteamFarm.IPC { // Add workaround for missing PathBase feature, https://github.com/aspnet/Hosting/issues/1120 PathString pathBase = Configuration.GetSection("Kestrel").GetValue("PathBase"); + if (!string.IsNullOrEmpty(pathBase) && (pathBase != "/")) { app.UsePathBase(pathBase); } @@ -87,6 +89,7 @@ namespace ArchiSteamFarm.IPC { public void ConfigureServices(IServiceCollection services) { if (services == null) { ASF.ArchiLogger.LogNullError(nameof(services)); + return; } @@ -127,6 +130,7 @@ namespace ArchiSteamFarm.IPC { c.DescribeAllEnumsAsStrings(); c.EnableAnnotations(); + c.SwaggerDoc( SharedInfo.ASF, new OpenApiInfo { Contact = new OpenApiContact { diff --git a/ArchiSteamFarm/IPC/WebUtilities.cs b/ArchiSteamFarm/IPC/WebUtilities.cs index 48425bafc..f1709415d 100644 --- a/ArchiSteamFarm/IPC/WebUtilities.cs +++ b/ArchiSteamFarm/IPC/WebUtilities.cs @@ -30,6 +30,7 @@ namespace ArchiSteamFarm.IPC { internal static async Task Generate(this HttpResponse httpResponse, HttpStatusCode statusCode) { if (httpResponse == null) { ASF.ArchiLogger.LogNullError(nameof(httpResponse)); + return; } @@ -42,6 +43,7 @@ namespace ArchiSteamFarm.IPC { internal static string GetUnifiedName(this Type type) { if (type == null) { ASF.ArchiLogger.LogNullError(nameof(type)); + return null; } @@ -51,10 +53,12 @@ namespace ArchiSteamFarm.IPC { internal static Type ParseType(string typeText) { if (string.IsNullOrEmpty(typeText)) { ASF.ArchiLogger.LogNullError(nameof(typeText)); + return null; } Type targetType = Type.GetType(typeText); + if (targetType != null) { return targetType; } diff --git a/ArchiSteamFarm/Json/Steam.cs b/ArchiSteamFarm/Json/Steam.cs index 844cccf2a..8e26b4308 100644 --- a/ArchiSteamFarm/Json/Steam.cs +++ b/ArchiSteamFarm/Json/Steam.cs @@ -54,11 +54,13 @@ namespace ArchiSteamFarm.Json { set { if (string.IsNullOrEmpty(value)) { ASF.ArchiLogger.LogNullError(nameof(value)); + return; } if (!uint.TryParse(value, out uint amount) || (amount == 0)) { ASF.ArchiLogger.LogNullError(nameof(amount)); + return; } @@ -73,11 +75,13 @@ namespace ArchiSteamFarm.Json { set { if (string.IsNullOrEmpty(value)) { ASF.ArchiLogger.LogNullError(nameof(value)); + return; } if (!ulong.TryParse(value, out ulong assetID) || (assetID == 0)) { ASF.ArchiLogger.LogNullError(nameof(assetID)); + return; } @@ -92,6 +96,7 @@ namespace ArchiSteamFarm.Json { set { if (string.IsNullOrEmpty(value)) { ASF.ArchiLogger.LogNullError(nameof(value)); + return; } @@ -110,11 +115,13 @@ namespace ArchiSteamFarm.Json { set { if (string.IsNullOrEmpty(value)) { ASF.ArchiLogger.LogNullError(nameof(value)); + return; } if (!ulong.TryParse(value, out ulong contextID) || (contextID == 0)) { ASF.ArchiLogger.LogNullError(nameof(contextID)); + return; } @@ -176,12 +183,15 @@ namespace ArchiSteamFarm.Json { set { if (string.IsNullOrEmpty(value)) { ASF.ArchiLogger.LogNullError(nameof(value)); + return; } HtmlDocument htmlDocument = WebBrowser.StringToHtmlDocument(value); + if (htmlDocument == null) { ASF.ArchiLogger.LogNullError(nameof(htmlDocument)); + return; } @@ -189,32 +199,42 @@ namespace ArchiSteamFarm.Json { Type = EType.Trade; HtmlNode tradeOfferNode = htmlDocument.DocumentNode.SelectSingleNode("//div[@class='tradeoffer']"); + if (tradeOfferNode == null) { ASF.ArchiLogger.LogNullError(nameof(tradeOfferNode)); + return; } string idText = tradeOfferNode.GetAttributeValue("id", null); + if (string.IsNullOrEmpty(idText)) { ASF.ArchiLogger.LogNullError(nameof(idText)); + return; } int index = idText.IndexOf('_'); + if (index < 0) { ASF.ArchiLogger.LogNullError(nameof(index)); + return; } index++; + if (idText.Length <= index) { ASF.ArchiLogger.LogNullError(nameof(idText.Length)); + return; } idText = idText.Substring(index); + if (!ulong.TryParse(idText, out ulong tradeOfferID) || (tradeOfferID == 0)) { ASF.ArchiLogger.LogNullError(nameof(tradeOfferID)); + return; } @@ -275,11 +295,13 @@ namespace ArchiSteamFarm.Json { set { if (string.IsNullOrEmpty(value)) { ASF.ArchiLogger.LogNullError(nameof(value)); + return; } if (!ulong.TryParse(value, out ulong lastAssetID) || (lastAssetID == 0)) { ASF.ArchiLogger.LogNullError(nameof(lastAssetID)); + return; } @@ -313,11 +335,13 @@ namespace ArchiSteamFarm.Json { set { if (string.IsNullOrEmpty(value)) { ASF.ArchiLogger.LogNullError(nameof(value)); + return; } if (!ulong.TryParse(value, out ulong classID) || (classID == 0)) { ASF.ArchiLogger.LogNullError(nameof(classID)); + return; } @@ -354,12 +378,15 @@ namespace ArchiSteamFarm.Json { switch (value) { case 0: Success = false; + break; case 1: Success = true; + break; default: ASF.ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(value), value)); + return; } } @@ -414,6 +441,7 @@ namespace ArchiSteamFarm.Json { internal bool IsValidSteamItemsRequest(IReadOnlyCollection acceptedTypes) { if ((acceptedTypes == null) || (acceptedTypes.Count == 0)) { ASF.ArchiLogger.LogNullError(nameof(acceptedTypes)); + return false; } @@ -471,11 +499,13 @@ namespace ArchiSteamFarm.Json { set { if (string.IsNullOrEmpty(value)) { ASF.ArchiLogger.LogNullError(nameof(value)); + return; } if (!ulong.TryParse(value, out ulong tradeOfferID) || (tradeOfferID == 0)) { ASF.ArchiLogger.LogNullError(nameof(tradeOfferID)); + return; } diff --git a/ArchiSteamFarm/MobileAuthenticator.cs b/ArchiSteamFarm/MobileAuthenticator.cs index 5ed32e39c..623b383eb 100644 --- a/ArchiSteamFarm/MobileAuthenticator.cs +++ b/ArchiSteamFarm/MobileAuthenticator.cs @@ -68,6 +68,7 @@ namespace ArchiSteamFarm { internal bool CorrectDeviceID(string deviceID) { if (string.IsNullOrEmpty(deviceID)) { Bot.ArchiLogger.LogNullError(nameof(deviceID)); + return false; } @@ -76,13 +77,16 @@ namespace ArchiSteamFarm { } DeviceID = deviceID; + return true; } internal async Task GenerateToken() { uint time = await GetSteamTime().ConfigureAwait(false); + if (time == 0) { Bot.ArchiLogger.LogNullError(nameof(time)); + return null; } @@ -92,45 +96,57 @@ namespace ArchiSteamFarm { internal async Task GetConfirmationDetails(Confirmation confirmation) { if (confirmation == null) { Bot.ArchiLogger.LogNullError(nameof(confirmation)); + return null; } if (!HasCorrectDeviceID) { Bot.ArchiLogger.LogGenericError(Strings.ErrorMobileAuthenticatorInvalidDeviceID); + return null; } uint time = await GetSteamTime().ConfigureAwait(false); + if (time == 0) { Bot.ArchiLogger.LogNullError(nameof(time)); + return null; } string confirmationHash = GenerateConfirmationHash(time, "conf"); + if (string.IsNullOrEmpty(confirmationHash)) { Bot.ArchiLogger.LogNullError(nameof(confirmationHash)); + return null; } Steam.ConfirmationDetails response = await Bot.ArchiWebHandler.GetConfirmationDetails(DeviceID, confirmationHash, time, confirmation).ConfigureAwait(false); + return response?.Success == true ? response : null; } internal async Task> GetConfirmations(Steam.ConfirmationDetails.EType acceptedType = Steam.ConfirmationDetails.EType.Unknown) { if (!HasCorrectDeviceID) { Bot.ArchiLogger.LogGenericError(Strings.ErrorMobileAuthenticatorInvalidDeviceID); + return null; } uint time = await GetSteamTime().ConfigureAwait(false); + if (time == 0) { Bot.ArchiLogger.LogNullError(nameof(time)); + return null; } string confirmationHash = GenerateConfirmationHash(time, "conf"); + if (string.IsNullOrEmpty(confirmationHash)) { Bot.ArchiLogger.LogNullError(nameof(confirmationHash)); + return null; } @@ -139,6 +155,7 @@ namespace ArchiSteamFarm { HtmlDocument htmlDocument = await Bot.ArchiWebHandler.GetConfirmations(DeviceID, confirmationHash, time).ConfigureAwait(false); HtmlNodeCollection confirmationNodes = htmlDocument?.DocumentNode.SelectNodes("//div[@class='mobileconf_list_entry']"); + if (confirmationNodes == null) { return null; } @@ -147,40 +164,50 @@ namespace ArchiSteamFarm { foreach (HtmlNode confirmationNode in confirmationNodes) { string idText = confirmationNode.GetAttributeValue("data-confid", null); + if (string.IsNullOrEmpty(idText)) { Bot.ArchiLogger.LogNullError(nameof(idText)); + return null; } if (!ulong.TryParse(idText, out ulong id) || (id == 0)) { Bot.ArchiLogger.LogNullError(nameof(id)); + return null; } string keyText = confirmationNode.GetAttributeValue("data-key", null); + if (string.IsNullOrEmpty(keyText)) { Bot.ArchiLogger.LogNullError(nameof(keyText)); + return null; } if (!ulong.TryParse(keyText, out ulong key) || (key == 0)) { Bot.ArchiLogger.LogNullError(nameof(key)); + return null; } string typeText = confirmationNode.GetAttributeValue("data-type", null); + if (string.IsNullOrEmpty(typeText)) { Bot.ArchiLogger.LogNullError(nameof(typeText)); + return null; } if (!Enum.TryParse(typeText, out Steam.ConfirmationDetails.EType type) || (type == Steam.ConfirmationDetails.EType.Unknown)) { Bot.ArchiLogger.LogNullError(nameof(type)); + return null; } if (!Enum.IsDefined(typeof(Steam.ConfirmationDetails.EType), type)) { Bot.ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(type), type)); + return null; } @@ -197,27 +224,34 @@ namespace ArchiSteamFarm { internal async Task HandleConfirmations(IReadOnlyCollection confirmations, bool accept) { if ((confirmations == null) || (confirmations.Count == 0)) { Bot.ArchiLogger.LogNullError(nameof(confirmations)); + return false; } if (!HasCorrectDeviceID) { Bot.ArchiLogger.LogGenericError(Strings.ErrorMobileAuthenticatorInvalidDeviceID); + return false; } uint time = await GetSteamTime().ConfigureAwait(false); + if (time == 0) { Bot.ArchiLogger.LogNullError(nameof(time)); + return false; } string confirmationHash = GenerateConfirmationHash(time, "conf"); + if (string.IsNullOrEmpty(confirmationHash)) { Bot.ArchiLogger.LogNullError(nameof(confirmationHash)); + return false; } bool? result = await Bot.ArchiWebHandler.HandleConfirmations(DeviceID, confirmationHash, time, confirmations, accept).ConfigureAwait(false); + if (!result.HasValue) { // Request timed out return false; @@ -233,6 +267,7 @@ namespace ArchiSteamFarm { // 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); + if (!confirmationResult.HasValue) { return false; } @@ -246,6 +281,7 @@ namespace ArchiSteamFarm { private string GenerateConfirmationHash(uint time, string tag = null) { if (time == 0) { Bot.ArchiLogger.LogNullError(nameof(time)); + return null; } @@ -256,15 +292,18 @@ namespace ArchiSteamFarm { } catch (FormatException e) { Bot.ArchiLogger.LogGenericException(e); Bot.ArchiLogger.LogGenericError(string.Format(Strings.ErrorIsInvalid, nameof(IdentitySecret))); + return null; } byte bufferSize = 8; + if (!string.IsNullOrEmpty(tag)) { bufferSize += (byte) Math.Min(32, tag.Length); } byte[] timeArray = BitConverter.GetBytes((long) time); + if (BitConverter.IsLittleEndian) { Array.Reverse(timeArray); } @@ -272,11 +311,13 @@ namespace ArchiSteamFarm { byte[] buffer = new byte[bufferSize]; Array.Copy(timeArray, buffer, 8); + if (!string.IsNullOrEmpty(tag)) { Array.Copy(Encoding.UTF8.GetBytes(tag), 0, buffer, 8, bufferSize - 8); } byte[] hash; + using (HMACSHA1 hmac = new HMACSHA1(identitySecret)) { hash = hmac.ComputeHash(buffer); } @@ -287,6 +328,7 @@ namespace ArchiSteamFarm { private string GenerateTokenForTime(uint time) { if (time == 0) { Bot.ArchiLogger.LogNullError(nameof(time)); + return null; } @@ -297,15 +339,18 @@ namespace ArchiSteamFarm { } catch (FormatException e) { Bot.ArchiLogger.LogGenericException(e); Bot.ArchiLogger.LogGenericError(string.Format(Strings.ErrorIsInvalid, nameof(SharedSecret))); + return null; } byte[] timeArray = BitConverter.GetBytes((long) time / CodeInterval); + if (BitConverter.IsLittleEndian) { Array.Reverse(timeArray); } byte[] hash; + using (HMACSHA1 hmac = new HMACSHA1(sharedSecret)) { hash = hmac.ComputeHash(timeArray); } @@ -348,6 +393,7 @@ namespace ArchiSteamFarm { } uint serverTime = await Bot.ArchiWebHandler.GetServerTime().ConfigureAwait(false); + if (serverTime == 0) { return Utilities.GetUnixTime(); } @@ -367,6 +413,7 @@ namespace ArchiSteamFarm { } await ConfirmationsSemaphore.WaitAsync().ConfigureAwait(false); + Utilities.InBackground( async () => { await Task.Delay(Program.GlobalConfig.ConfirmationsLimiterDelay * 1000).ConfigureAwait(false); diff --git a/ArchiSteamFarm/NLog/ArchiLogger.cs b/ArchiSteamFarm/NLog/ArchiLogger.cs index 2465d0bc0..040880b02 100644 --- a/ArchiSteamFarm/NLog/ArchiLogger.cs +++ b/ArchiSteamFarm/NLog/ArchiLogger.cs @@ -41,6 +41,7 @@ namespace ArchiSteamFarm.NLog { 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; } @@ -69,6 +70,7 @@ namespace ArchiSteamFarm.NLog { internal async Task LogFatalException(Exception exception, [CallerMemberName] string previousMethodName = null) { if (exception == null) { LogNullError(nameof(exception)); + return; } @@ -85,13 +87,13 @@ namespace ArchiSteamFarm.NLog { try { await RuntimeCompatibility.File.WriteAllTextAsync(SharedInfo.LogFile, message).ConfigureAwait(false); } catch { - // Ignored, we can't do anything with this + // Ignored, we can't do anything about this } try { Console.Write(message); } catch { - // Ignored, we can't do anything with this + // Ignored, we can't do anything about this } while (true) { @@ -100,17 +102,18 @@ namespace ArchiSteamFarm.NLog { try { await RuntimeCompatibility.File.AppendAllTextAsync(SharedInfo.LogFile, message).ConfigureAwait(false); } catch { - // Ignored, we can't do anything with this + // Ignored, we can't do anything about this } try { Console.Write(message); } catch { - // Ignored, we can't do anything with this + // Ignored, we can't do anything about this } if (exception.InnerException != null) { exception = exception.InnerException; + continue; } @@ -121,6 +124,7 @@ namespace ArchiSteamFarm.NLog { internal void LogGenericDebug(string message, [CallerMemberName] string previousMethodName = null) { if (string.IsNullOrEmpty(message)) { LogNullError(nameof(message)); + return; } @@ -130,6 +134,7 @@ namespace ArchiSteamFarm.NLog { internal void LogGenericDebuggingException(Exception exception, [CallerMemberName] string previousMethodName = null) { if (exception == null) { LogNullError(nameof(exception)); + return; } @@ -143,6 +148,7 @@ namespace ArchiSteamFarm.NLog { internal void LogGenericError(string message, [CallerMemberName] string previousMethodName = null) { if (string.IsNullOrEmpty(message)) { LogNullError(nameof(message)); + return; } @@ -152,6 +158,7 @@ namespace ArchiSteamFarm.NLog { internal void LogGenericException(Exception exception, [CallerMemberName] string previousMethodName = null) { if (exception == null) { LogNullError(nameof(exception)); + return; } @@ -161,6 +168,7 @@ namespace ArchiSteamFarm.NLog { internal void LogGenericInfo(string message, [CallerMemberName] string previousMethodName = null) { if (string.IsNullOrEmpty(message)) { LogNullError(nameof(message)); + return; } @@ -170,6 +178,7 @@ namespace ArchiSteamFarm.NLog { internal void LogGenericTrace(string message, [CallerMemberName] string previousMethodName = null) { if (string.IsNullOrEmpty(message)) { LogNullError(nameof(message)); + return; } @@ -179,6 +188,7 @@ namespace ArchiSteamFarm.NLog { internal void LogGenericWarning(string message, [CallerMemberName] string previousMethodName = null) { if (string.IsNullOrEmpty(message)) { LogNullError(nameof(message)); + return; } @@ -188,6 +198,7 @@ namespace ArchiSteamFarm.NLog { internal void LogGenericWarningException(Exception exception, [CallerMemberName] string previousMethodName = null) { if (exception == null) { LogNullError(nameof(exception)); + return; } diff --git a/ArchiSteamFarm/NLog/HistoryTarget.cs b/ArchiSteamFarm/NLog/HistoryTarget.cs index 695a13a06..bd16e4b34 100644 --- a/ArchiSteamFarm/NLog/HistoryTarget.cs +++ b/ArchiSteamFarm/NLog/HistoryTarget.cs @@ -41,9 +41,11 @@ namespace ArchiSteamFarm.NLog { [SuppressMessage("ReSharper", "UnusedMember.Global")] public byte MaxCount { get => HistoryQueue.MaxCount; + set { if (value == 0) { ASF.ArchiLogger.LogNullError(nameof(value)); + return; } @@ -61,6 +63,7 @@ namespace ArchiSteamFarm.NLog { protected override void Write(LogEventInfo logEvent) { if (logEvent == null) { ASF.ArchiLogger.LogNullError(nameof(logEvent)); + return; } diff --git a/ArchiSteamFarm/NLog/Logging.cs b/ArchiSteamFarm/NLog/Logging.cs index d14f8fed6..7bcef113f 100644 --- a/ArchiSteamFarm/NLog/Logging.cs +++ b/ArchiSteamFarm/NLog/Logging.cs @@ -42,6 +42,7 @@ namespace ArchiSteamFarm.NLog { } bool reload = false; + foreach (LoggingRule rule in LogManager.Configuration.LoggingRules.Where(rule => rule.IsLoggingEnabledForLevel(LogLevel.Debug) && !rule.IsLoggingEnabledForLevel(LogLevel.Trace))) { rule.EnableLoggingForLevel(LogLevel.Trace); reload = true; @@ -57,6 +58,7 @@ namespace ArchiSteamFarm.NLog { IsUsingCustomConfiguration = true; InitConsoleLoggers(); LogManager.ConfigurationChanged += OnConfigurationChanged; + return; } @@ -89,7 +91,6 @@ namespace ArchiSteamFarm.NLog { HistoryTarget historyTarget = LogManager.Configuration.AllTargets.OfType().FirstOrDefault(); if ((historyTarget == null) && !IsUsingCustomConfiguration) { - // TODO: We could use some nice HTML layout for this historyTarget = new HistoryTarget("History") { Layout = GeneralLayout, MaxCount = 20 @@ -154,6 +155,7 @@ namespace ArchiSteamFarm.NLog { private static void OnConfigurationChanged(object sender, LoggingConfigurationChangedEventArgs e) { if ((sender == null) || (e == null)) { ASF.ArchiLogger.LogNullError(nameof(sender) + " || " + nameof(e)); + return; } diff --git a/ArchiSteamFarm/NLog/SteamTarget.cs b/ArchiSteamFarm/NLog/SteamTarget.cs index 1323e2b3f..c6004857b 100644 --- a/ArchiSteamFarm/NLog/SteamTarget.cs +++ b/ArchiSteamFarm/NLog/SteamTarget.cs @@ -56,6 +56,7 @@ namespace ArchiSteamFarm.NLog { protected override async void Write(LogEventInfo logEvent) { if (logEvent == null) { ASF.ArchiLogger.LogNullError(nameof(logEvent)); + return; } @@ -89,6 +90,7 @@ namespace ArchiSteamFarm.NLog { private async Task SendGroupMessage(string message, Bot bot = null) { if (string.IsNullOrEmpty(message)) { ASF.ArchiLogger.LogNullError(nameof(message)); + return; } @@ -106,6 +108,7 @@ namespace ArchiSteamFarm.NLog { private async Task SendPrivateMessage(string message, Bot bot = null) { if (string.IsNullOrEmpty(message)) { ASF.ArchiLogger.LogNullError(nameof(message)); + return; } diff --git a/ArchiSteamFarm/OS.cs b/ArchiSteamFarm/OS.cs index ff5df2cbd..f4c0b2af9 100644 --- a/ArchiSteamFarm/OS.cs +++ b/ArchiSteamFarm/OS.cs @@ -41,14 +41,18 @@ namespace ArchiSteamFarm { switch (optimizationMode) { case GlobalConfig.EOptimizationMode.MaxPerformance: + // No specific tuning required for now, ASF is optimized for max performance by default break; case GlobalConfig.EOptimizationMode.MinMemoryUsage: + // We can disable regex cache which will slightly lower memory usage (for a huge performance hit) Regex.CacheSize = 0; + break; default: ASF.ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(optimizationMode), optimizationMode)); + return; } } @@ -56,6 +60,7 @@ namespace ArchiSteamFarm { internal static void UnixSetFileAccessExecutable(string path) { if (string.IsNullOrEmpty(path) || !File.Exists(path)) { ASF.ArchiLogger.LogNullError(nameof(path)); + return; } @@ -75,6 +80,7 @@ namespace ArchiSteamFarm { if (!NativeMethods.GetConsoleMode(consoleHandle, out uint consoleMode)) { ASF.ArchiLogger.LogGenericError(Strings.WarningFailed); + return; } diff --git a/ArchiSteamFarm/Program.cs b/ArchiSteamFarm/Program.cs index 3abb57e88..281a4fc5f 100644 --- a/ArchiSteamFarm/Program.cs +++ b/ArchiSteamFarm/Program.cs @@ -72,6 +72,7 @@ namespace ArchiSteamFarm { if (GlobalConfig.Headless) { ASF.ArchiLogger.LogGenericWarning(Strings.ErrorUserInputRunningInHeadlessMode); + return null; } @@ -85,31 +86,38 @@ namespace ArchiSteamFarm { case ASF.EUserInputType.DeviceID: Console.Write(Bot.FormatBotResponse(Strings.UserInputDeviceID, botName)); result = Console.ReadLine(); + break; case ASF.EUserInputType.Login: Console.Write(Bot.FormatBotResponse(Strings.UserInputSteamLogin, botName)); result = Console.ReadLine(); + break; case ASF.EUserInputType.Password: Console.Write(Bot.FormatBotResponse(Strings.UserInputSteamPassword, botName)); result = Utilities.ReadLineMasked(); + break; case ASF.EUserInputType.SteamGuard: Console.Write(Bot.FormatBotResponse(Strings.UserInputSteamGuard, botName)); result = Console.ReadLine(); + break; case ASF.EUserInputType.SteamParentalCode: Console.Write(Bot.FormatBotResponse(Strings.UserInputSteamParentalCode, botName)); result = Utilities.ReadLineMasked(); + break; case ASF.EUserInputType.TwoFactorAuthentication: Console.Write(Bot.FormatBotResponse(Strings.UserInputSteam2FA, botName)); result = Console.ReadLine(); + break; default: ASF.ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(userInputType), userInputType)); Console.Write(Bot.FormatBotResponse(string.Format(Strings.UserInputUnknown, userInputType), botName)); result = Console.ReadLine(); + break; } @@ -119,6 +127,7 @@ namespace ArchiSteamFarm { } catch (Exception e) { Logging.OnUserInputEnd(); ASF.ArchiLogger.LogGenericException(e); + return null; } @@ -134,8 +143,10 @@ namespace ArchiSteamFarm { } string executableName = Path.GetFileNameWithoutExtension(ProcessFileName); + if (string.IsNullOrEmpty(executableName)) { ASF.ArchiLogger.LogNullError(nameof(executableName)); + return; } @@ -157,6 +168,7 @@ namespace ArchiSteamFarm { private static void HandleCryptKeyArgument(string cryptKey) { if (string.IsNullOrEmpty(cryptKey)) { ASF.ArchiLogger.LogNullError(nameof(cryptKey)); + return; } @@ -166,6 +178,7 @@ namespace ArchiSteamFarm { private static void HandlePathArgument(string path) { if (string.IsNullOrEmpty(path)) { ASF.ArchiLogger.LogNullError(nameof(path)); + return; } @@ -217,6 +230,7 @@ namespace ArchiSteamFarm { // Common structure is bin/(x64/)Debug/ArchiSteamFarm.exe, so we allow up to 4 directories up for (byte i = 0; i < 4; i++) { Directory.SetCurrentDirectory(".."); + if (Directory.Exists(SharedInfo.ConfigDirectory)) { break; } @@ -241,10 +255,12 @@ namespace ArchiSteamFarm { if (File.Exists(globalConfigFile)) { GlobalConfig = await GlobalConfig.Load(globalConfigFile).ConfigureAwait(false); + if (GlobalConfig == null) { ASF.ArchiLogger.LogGenericError(string.Format(Strings.ErrorGlobalConfigNotLoaded, globalConfigFile)); await Task.Delay(5 * 1000).ConfigureAwait(false); await Exit(1).ConfigureAwait(false); + return; } } else { @@ -270,24 +286,31 @@ namespace ArchiSteamFarm { } ResourceSet defaultResourceSet = Strings.ResourceManager.GetResourceSet(CultureInfo.GetCultureInfo("en-US"), true, true); + if (defaultResourceSet == null) { ASF.ArchiLogger.LogNullError(nameof(defaultResourceSet)); + return; } HashSet defaultStringObjects = defaultResourceSet.Cast().ToHashSet(); + if (defaultStringObjects.Count == 0) { ASF.ArchiLogger.LogNullError(nameof(defaultStringObjects)); + return; } ResourceSet currentResourceSet = Strings.ResourceManager.GetResourceSet(CultureInfo.CurrentUICulture, true, true); + if (currentResourceSet == null) { ASF.ArchiLogger.LogNullError(nameof(currentResourceSet)); + return; } HashSet currentStringObjects = currentResourceSet.Cast().ToHashSet(); + if (currentStringObjects.Count >= defaultStringObjects.Count) { // Either we have 100% finished translation, or we're missing it entirely and using en-US HashSet testStringObjects = currentStringObjects.ToHashSet(); @@ -317,10 +340,12 @@ namespace ArchiSteamFarm { } GlobalDatabase = await GlobalDatabase.CreateOrLoad(globalDatabaseFile).ConfigureAwait(false); + if (GlobalDatabase == null) { ASF.ArchiLogger.LogGenericError(string.Format(Strings.ErrorDatabaseInvalid, globalDatabaseFile)); await Task.Delay(5 * 1000).ConfigureAwait(false); await Exit(1).ConfigureAwait(false); + return; } @@ -372,6 +397,7 @@ namespace ArchiSteamFarm { } LogManager.Flush(); + return true; } @@ -388,6 +414,7 @@ namespace ArchiSteamFarm { private static async void OnUnhandledException(object sender, UnhandledExceptionEventArgs e) { if (e?.ExceptionObject == null) { ASF.ArchiLogger.LogNullError(nameof(e) + " || " + nameof(e.ExceptionObject)); + return; } @@ -398,6 +425,7 @@ namespace ArchiSteamFarm { private static async void OnUnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e) { if (e?.Exception == null) { ASF.ArchiLogger.LogNullError(nameof(e) + " || " + nameof(e.Exception)); + return; } @@ -411,6 +439,7 @@ namespace ArchiSteamFarm { private static void ParsePostInitArgs(IReadOnlyCollection args) { if (args == null) { ASF.ArchiLogger.LogNullError(nameof(args)); + return; } @@ -420,17 +449,22 @@ namespace ArchiSteamFarm { switch (arg) { case "--cryptkey" when !cryptKeyNext: cryptKeyNext = true; + break; case "--no-restart" when !cryptKeyNext: RestartAllowed = false; + break; case "--process-required" when !cryptKeyNext: ProcessRequired = true; + break; case "--system-required" when !cryptKeyNext: SystemRequired = true; + break; default: + if (cryptKeyNext) { cryptKeyNext = false; HandleCryptKeyArgument(arg); @@ -446,6 +480,7 @@ namespace ArchiSteamFarm { private static void ParsePreInitArgs(IReadOnlyCollection args) { if (args == null) { ASF.ArchiLogger.LogNullError(nameof(args)); + return; } @@ -455,8 +490,10 @@ namespace ArchiSteamFarm { switch (arg) { case "--path" when !pathNext: pathNext = true; + break; default: + if (pathNext) { pathNext = false; HandlePathArgument(arg); diff --git a/ArchiSteamFarm/Statistics.cs b/ArchiSteamFarm/Statistics.cs index cdc70d7b7..8195c5825 100644 --- a/ArchiSteamFarm/Statistics.cs +++ b/ArchiSteamFarm/Statistics.cs @@ -94,6 +94,7 @@ namespace ArchiSteamFarm { } const string request = URL + "/Api/HeartBeat"; + Dictionary data = new Dictionary(2) { { "SteamID", Bot.SteamID.ToString() }, { "Guid", Program.GlobalDatabase.Guid.ToString("N") } @@ -123,17 +124,21 @@ namespace ArchiSteamFarm { // Don't announce if we don't meet conditions string tradeToken; + if (!await IsEligibleForMatching().ConfigureAwait(false) || string.IsNullOrEmpty(tradeToken = await Bot.ArchiHandler.GetTradeToken().ConfigureAwait(false))) { LastAnnouncementCheck = DateTime.UtcNow; ShouldSendHeartBeats = false; + return; } HashSet acceptedMatchableTypes = Bot.BotConfig.MatchableTypes.Where(type => AcceptedMatchableTypes.Contains(type)).ToHashSet(); + if (acceptedMatchableTypes.Count == 0) { Bot.ArchiLogger.LogNullError(nameof(acceptedMatchableTypes)); LastAnnouncementCheck = DateTime.UtcNow; ShouldSendHeartBeats = false; + return; } @@ -142,6 +147,7 @@ namespace ArchiSteamFarm { // This is actually inventory failure, so we'll stop sending heartbeats but not record it as valid check if (inventory == null) { ShouldSendHeartBeats = false; + return; } @@ -149,10 +155,12 @@ namespace ArchiSteamFarm { if (inventory.Count < MinItemsCount) { LastAnnouncementCheck = DateTime.UtcNow; ShouldSendHeartBeats = false; + return; } const string request = URL + "/Api/Announce"; + Dictionary data = new Dictionary(9) { { "SteamID", Bot.SteamID.ToString() }, { "Guid", Program.GlobalDatabase.Guid.ToString("N") }, @@ -179,6 +187,7 @@ namespace ArchiSteamFarm { const string request = URL + "/Api/Bots"; WebBrowser.ObjectResponse> objectResponse = await Program.WebBrowser.UrlGetToJsonObject>(request).ConfigureAwait(false); + return objectResponse?.Content; } @@ -186,30 +195,35 @@ namespace ArchiSteamFarm { // Bot must have ASF 2FA if (!Bot.HasMobileAuthenticator) { Bot.ArchiLogger.LogGenericTrace(string.Format(Strings.WarningFailedWithError, nameof(Bot.HasMobileAuthenticator) + ": " + Bot.HasMobileAuthenticator)); + return false; } // Bot must have STM enable in TradingPreferences if (!Bot.BotConfig.TradingPreferences.HasFlag(BotConfig.ETradingPreferences.SteamTradeMatcher)) { Bot.ArchiLogger.LogGenericTrace(string.Format(Strings.WarningFailedWithError, nameof(Bot.BotConfig.TradingPreferences) + ": " + Bot.BotConfig.TradingPreferences)); + return false; } // Bot must have at least one accepted matchable type set if ((Bot.BotConfig.MatchableTypes.Count == 0) || Bot.BotConfig.MatchableTypes.All(type => !AcceptedMatchableTypes.Contains(type))) { Bot.ArchiLogger.LogGenericTrace(string.Format(Strings.WarningFailedWithError, nameof(Bot.BotConfig.MatchableTypes) + ": " + Bot.BotConfig.MatchableTypes)); + return false; } // Bot must have public inventory if (!await Bot.ArchiWebHandler.HasPublicInventory().ConfigureAwait(false)) { Bot.ArchiLogger.LogGenericTrace(string.Format(Strings.WarningFailedWithError, nameof(Bot.ArchiWebHandler.HasPublicInventory) + ": " + false)); + return false; } // Bot must have valid API key (e.g. not being restricted account) if (!await Bot.ArchiWebHandler.HasValidApiKey().ConfigureAwait(false)) { Bot.ArchiLogger.LogGenericTrace(string.Format(Strings.WarningFailedWithError, nameof(Bot.ArchiWebHandler.HasValidApiKey) + ": " + false)); + return false; } @@ -219,17 +233,21 @@ namespace ArchiSteamFarm { private async Task MatchActively() { if (!Bot.IsConnectedAndLoggedOn || Bot.BotConfig.TradingPreferences.HasFlag(BotConfig.ETradingPreferences.MatchEverything) || !Bot.BotConfig.TradingPreferences.HasFlag(BotConfig.ETradingPreferences.MatchActively) || !await IsEligibleForMatching().ConfigureAwait(false)) { Bot.ArchiLogger.LogGenericTrace(Strings.ErrorAborted); + return; } HashSet acceptedMatchableTypes = Bot.BotConfig.MatchableTypes.Where(type => AcceptedMatchableTypes.Contains(type)).ToHashSet(); + if (acceptedMatchableTypes.Count == 0) { Bot.ArchiLogger.LogGenericTrace(Strings.ErrorAborted); + return; } if (!await MatchActivelySemaphore.WaitAsync(0).ConfigureAwait(false)) { Bot.ArchiLogger.LogGenericTrace(Strings.ErrorAborted); + return; } @@ -248,6 +266,7 @@ namespace ArchiSteamFarm { if (!Bot.IsConnectedAndLoggedOn || Bot.BotConfig.TradingPreferences.HasFlag(BotConfig.ETradingPreferences.MatchEverything) || !Bot.BotConfig.TradingPreferences.HasFlag(BotConfig.ETradingPreferences.MatchActively) || !await IsEligibleForMatching().ConfigureAwait(false)) { Bot.ArchiLogger.LogGenericTrace(Strings.ErrorAborted); + break; } @@ -268,12 +287,15 @@ namespace ArchiSteamFarm { private async Task MatchActivelyRound(IReadOnlyCollection acceptedMatchableTypes, IDictionary GivenAssetIDs, ISet ReceivedAssetIDs)> triedSteamIDs) { if ((acceptedMatchableTypes == null) || (acceptedMatchableTypes.Count == 0) || (triedSteamIDs == null)) { Bot.ArchiLogger.LogNullError(nameof(acceptedMatchableTypes) + " || " + nameof(triedSteamIDs)); + return false; } HashSet ourInventory = await Bot.ArchiWebHandler.GetInventory(Bot.SteamID, wantedTypes: acceptedMatchableTypes).ConfigureAwait(false); + if ((ourInventory == null) || (ourInventory.Count == 0)) { Bot.ArchiLogger.LogGenericTrace(string.Format(Strings.ErrorIsEmpty, nameof(ourInventory))); + return false; } @@ -282,12 +304,15 @@ namespace ArchiSteamFarm { if (Trading.IsEmptyForMatching(fullState, tradableState)) { // User doesn't have any more dupes in the inventory Bot.ArchiLogger.LogGenericTrace(string.Format(Strings.ErrorIsEmpty, nameof(fullState) + " || " + nameof(tradableState))); + return false; } HashSet listedUsers = await GetListedUsers().ConfigureAwait(false); + if ((listedUsers == null) || (listedUsers.Count == 0)) { Bot.ArchiLogger.LogGenericTrace(string.Format(Strings.ErrorIsEmpty, nameof(listedUsers))); + return false; } @@ -298,8 +323,10 @@ namespace ArchiSteamFarm { Bot.ArchiLogger.LogGenericTrace(listedUser.SteamID + "..."); HashSet theirInventory = await Bot.ArchiWebHandler.GetInventory(listedUser.SteamID, tradable: true, wantedSets: fullState.Keys, skippedSets: skippedSetsThisRound).ConfigureAwait(false); + if ((theirInventory == null) || (theirInventory.Count == 0)) { Bot.ArchiLogger.LogGenericTrace(string.Format(Strings.ErrorIsEmpty, nameof(theirInventory))); + continue; } @@ -333,6 +360,7 @@ namespace ArchiSteamFarm { foreach (KeyValuePair pastChange in pastChanges) { if (!ourFullSet.TryGetValue(pastChange.Key, out uint fullAmount) || (fullAmount == 0) || (fullAmount < pastChange.Value)) { Bot.ArchiLogger.LogNullError(nameof(fullAmount)); + return false; } @@ -344,6 +372,7 @@ namespace ArchiSteamFarm { if (!ourTradableSet.TryGetValue(pastChange.Key, out uint tradableAmount) || (tradableAmount == 0) || (tradableAmount < pastChange.Value)) { Bot.ArchiLogger.LogNullError(nameof(tradableAmount)); + return false; } @@ -406,6 +435,7 @@ namespace ArchiSteamFarm { // Update their state based on taken items if (!theirItems.TryGetValue(theirItem.Key, out uint theirAmount) || (theirAmount == 0)) { Bot.ArchiLogger.LogNullError(nameof(theirAmount)); + return false; } @@ -418,6 +448,7 @@ namespace ArchiSteamFarm { itemsInTrade += 2; match = true; + break; } @@ -434,6 +465,7 @@ namespace ArchiSteamFarm { if (skippedSetsThisTrade.Count == 0) { Bot.ArchiLogger.LogGenericTrace(string.Format(Strings.ErrorIsEmpty, nameof(skippedSetsThisTrade))); + break; } @@ -443,6 +475,7 @@ namespace ArchiSteamFarm { if ((itemsToGive.Count != itemsToReceive.Count) || !Trading.IsFairTypesExchange(itemsToGive, itemsToReceive)) { // Failsafe Bot.ArchiLogger.LogGenericError(string.Format(Strings.WarningFailedWithError, Strings.ErrorAborted)); + return false; } @@ -450,6 +483,7 @@ namespace ArchiSteamFarm { if (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); + break; } @@ -471,12 +505,14 @@ namespace ArchiSteamFarm { if ((mobileTradeOfferIDs != null) && (mobileTradeOfferIDs.Count > 0) && Bot.HasMobileAuthenticator) { if (!await Bot.Actions.AcceptConfirmations(true, Steam.ConfirmationDetails.EType.Trade, mobileTradeOfferIDs, true).ConfigureAwait(false)) { Bot.ArchiLogger.LogGenericTrace(Strings.WarningFailed); + return false; } } if (!success) { Bot.ArchiLogger.LogGenericTrace(Strings.WarningFailed); + break; } @@ -514,6 +550,7 @@ namespace ArchiSteamFarm { } Bot.ArchiLogger.LogGenericInfo(string.Format(Strings.ActivelyMatchingItemsRound, skippedSetsThisRound.Count)); + return skippedSetsThisRound.Count > 0; } @@ -550,12 +587,15 @@ namespace ArchiSteamFarm { switch (value) { case 0: MatchableTypes.Remove(Steam.Asset.EType.ProfileBackground); + break; case 1: MatchableTypes.Add(Steam.Asset.EType.ProfileBackground); + break; default: ASF.ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(value), value)); + return; } } @@ -567,12 +607,15 @@ namespace ArchiSteamFarm { switch (value) { case 0: MatchableTypes.Remove(Steam.Asset.EType.TradingCard); + break; case 1: MatchableTypes.Add(Steam.Asset.EType.TradingCard); + break; default: ASF.ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(value), value)); + return; } } @@ -584,12 +627,15 @@ namespace ArchiSteamFarm { switch (value) { case 0: MatchableTypes.Remove(Steam.Asset.EType.Emoticon); + break; case 1: MatchableTypes.Add(Steam.Asset.EType.Emoticon); + break; default: ASF.ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(value), value)); + return; } } @@ -601,12 +647,15 @@ namespace ArchiSteamFarm { switch (value) { case 0: MatchableTypes.Remove(Steam.Asset.EType.FoilTradingCard); + break; case 1: MatchableTypes.Add(Steam.Asset.EType.FoilTradingCard); + break; default: ASF.ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(value), value)); + return; } } @@ -618,12 +667,15 @@ namespace ArchiSteamFarm { switch (value) { case 0: MatchEverything = false; + break; case 1: MatchEverything = true; + break; default: ASF.ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(value), value)); + return; } } diff --git a/ArchiSteamFarm/SteamKit2/InMemoryServerListProvider.cs b/ArchiSteamFarm/SteamKit2/InMemoryServerListProvider.cs index 695d74fad..7b31fbbcd 100644 --- a/ArchiSteamFarm/SteamKit2/InMemoryServerListProvider.cs +++ b/ArchiSteamFarm/SteamKit2/InMemoryServerListProvider.cs @@ -37,6 +37,7 @@ namespace ArchiSteamFarm.SteamKit2 { public Task UpdateServerListAsync(IEnumerable endpoints) { if (endpoints == null) { ASF.ArchiLogger.LogNullError(nameof(endpoints)); + return Task.CompletedTask; } @@ -47,6 +48,7 @@ namespace ArchiSteamFarm.SteamKit2 { } ServerListUpdated?.Invoke(this, EventArgs.Empty); + return Task.CompletedTask; } diff --git a/ArchiSteamFarm/SteamKit2/ServerRecordEndPoint.cs b/ArchiSteamFarm/SteamKit2/ServerRecordEndPoint.cs index 8b95d195c..c406cb925 100644 --- a/ArchiSteamFarm/SteamKit2/ServerRecordEndPoint.cs +++ b/ArchiSteamFarm/SteamKit2/ServerRecordEndPoint.cs @@ -24,7 +24,7 @@ using Newtonsoft.Json; using SteamKit2; namespace ArchiSteamFarm.SteamKit2 { - internal sealed class ServerRecordEndPoint { + internal sealed class ServerRecordEndPoint : IEquatable { [JsonProperty(Required = Required.Always)] internal readonly string Host; @@ -46,9 +46,8 @@ namespace ArchiSteamFarm.SteamKit2 { private ServerRecordEndPoint() { } + public bool Equals(ServerRecordEndPoint other) => (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() => (Host, Port, ProtocolTypes).GetHashCode(); - - private bool Equals(ServerRecordEndPoint other) => string.Equals(Host, other.Host) && (Port == other.Port) && (ProtocolTypes == other.ProtocolTypes); + public override int GetHashCode() => RuntimeCompatibility.HashCode.Combine(Host, Port, ProtocolTypes); } } diff --git a/ArchiSteamFarm/SteamSaleEvent.cs b/ArchiSteamFarm/SteamSaleEvent.cs index 65ece8816..9c83145b0 100644 --- a/ArchiSteamFarm/SteamSaleEvent.cs +++ b/ArchiSteamFarm/SteamSaleEvent.cs @@ -55,8 +55,10 @@ namespace ArchiSteamFarm { for (byte i = 0; (i < MaxSingleQueuesDaily) && (await IsDiscoveryQueueAvailable().ConfigureAwait(false)).GetValueOrDefault(); i++) { HashSet queue = await Bot.ArchiWebHandler.GenerateNewDiscoveryQueue().ConfigureAwait(false); + if ((queue == null) || (queue.Count == 0)) { Bot.ArchiLogger.LogGenericTrace(string.Format(Strings.ErrorIsEmpty, nameof(queue))); + break; } @@ -69,6 +71,7 @@ namespace ArchiSteamFarm { } Bot.ArchiLogger.LogGenericWarning(Strings.WarningFailed); + return; } @@ -80,19 +83,23 @@ namespace ArchiSteamFarm { private async Task IsDiscoveryQueueAvailable() { HtmlDocument htmlDocument = await Bot.ArchiWebHandler.GetDiscoveryQueuePage().ConfigureAwait(false); + if (htmlDocument == null) { return null; } HtmlNode htmlNode = htmlDocument.DocumentNode.SelectSingleNode("//div[@class='subtext']"); + if (htmlNode == null) { // Valid, no cards for exploring the queue available return false; } string text = htmlNode.InnerText; + if (string.IsNullOrEmpty(text)) { Bot.ArchiLogger.LogNullError(nameof(text)); + return null; } @@ -110,6 +117,7 @@ namespace ArchiSteamFarm { HtmlDocument htmlDocument = await Bot.ArchiWebHandler.GetSteamAwardsPage().ConfigureAwait(false); HtmlNodeCollection nominationNodes = htmlDocument?.DocumentNode.SelectNodes("//div[@class='vote_nominations store_horizontal_autoslider']"); + if (nominationNodes == null) { // Event ended, error or likewise return; @@ -117,25 +125,31 @@ namespace ArchiSteamFarm { foreach (HtmlNode nominationNode in nominationNodes) { HtmlNode myVoteNode = nominationNode.SelectSingleNode("./div[@class='vote_nomination your_vote']"); + if (myVoteNode != null) { // Already voted continue; } string voteIDText = nominationNode.GetAttributeValue("data-voteid", null); + if (string.IsNullOrEmpty(voteIDText)) { Bot.ArchiLogger.LogNullError(nameof(voteIDText)); + return; } if (!byte.TryParse(voteIDText, out byte voteID) || (voteID == 0)) { Bot.ArchiLogger.LogNullError(nameof(voteID)); + return; } HtmlNodeCollection voteNodes = nominationNode.SelectNodes("./div[starts-with(@class, 'vote_nomination')]"); + if (voteNodes == null) { Bot.ArchiLogger.LogNullError(nameof(voteNodes)); + return; } @@ -143,13 +157,16 @@ namespace ArchiSteamFarm { HtmlNode voteNode = voteNodes[Utilities.RandomNext(voteNodes.Count)]; string appIDText = voteNode.GetAttributeValue("data-vote-appid", null); + if (string.IsNullOrEmpty(appIDText)) { Bot.ArchiLogger.LogNullError(nameof(appIDText)); + return; } if (!uint.TryParse(appIDText, out uint appID) || (appID == 0)) { Bot.ArchiLogger.LogNullError(nameof(appID)); + return; } diff --git a/ArchiSteamFarm/Trading.cs b/ArchiSteamFarm/Trading.cs index 0999634d7..28e000974 100644 --- a/ArchiSteamFarm/Trading.cs +++ b/ArchiSteamFarm/Trading.cs @@ -47,6 +47,7 @@ namespace ArchiSteamFarm { internal static (Dictionary<(uint AppID, Steam.Asset.EType Type), Dictionary> FullState, Dictionary<(uint AppID, Steam.Asset.EType Type), Dictionary> TradableState) GetDividedInventoryState(IReadOnlyCollection inventory) { if ((inventory == null) || (inventory.Count == 0)) { ASF.ArchiLogger.LogNullError(nameof(inventory)); + return (null, null); } @@ -87,6 +88,7 @@ namespace ArchiSteamFarm { internal static Dictionary<(uint AppID, Steam.Asset.EType Type), Dictionary> GetInventoryState(IReadOnlyCollection inventory) { if ((inventory == null) || (inventory.Count == 0)) { ASF.ArchiLogger.LogNullError(nameof(inventory)); + return null; } @@ -112,6 +114,7 @@ namespace ArchiSteamFarm { internal static HashSet GetTradableItemsFromInventory(IReadOnlyCollection inventory, IDictionary classIDs) { if ((inventory == null) || (inventory.Count == 0) || (classIDs == null) || (classIDs.Count == 0)) { ASF.ArchiLogger.LogNullError(nameof(inventory) + " || " + nameof(classIDs)); + return null; } @@ -141,12 +144,14 @@ namespace ArchiSteamFarm { internal static bool IsEmptyForMatching(IReadOnlyDictionary<(uint AppID, Steam.Asset.EType Type), Dictionary> fullState, IReadOnlyDictionary<(uint AppID, Steam.Asset.EType Type), Dictionary> tradableState) { if ((fullState == null) || (tradableState == null)) { ASF.ArchiLogger.LogNullError(nameof(fullState) + " || " + nameof(tradableState)); + return false; } foreach (KeyValuePair<(uint AppID, Steam.Asset.EType Type), Dictionary> tradableSet in tradableState) { if (!fullState.TryGetValue(tradableSet.Key, out Dictionary fullSet) || (fullSet == null) || (fullSet.Count == 0)) { ASF.ArchiLogger.LogNullError(nameof(fullSet)); + return false; } @@ -162,19 +167,24 @@ namespace ArchiSteamFarm { internal static bool IsEmptyForMatching(IReadOnlyDictionary fullSet, IReadOnlyDictionary tradableSet) { if ((fullSet == null) || (tradableSet == null)) { ASF.ArchiLogger.LogNullError(nameof(fullSet) + " || " + nameof(tradableSet)); + return false; } foreach (KeyValuePair tradableItem in tradableSet) { switch (tradableItem.Value) { 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(tradableItem.Value), tradableItem.Value)); + return false; case 1: + // Single tradable item, can be matchable or not depending on the rest of the inventory if (!fullSet.TryGetValue(tradableItem.Key, out uint fullAmount) || (fullAmount == 0) || (fullAmount < tradableItem.Value)) { ASF.ArchiLogger.LogNullError(nameof(fullAmount)); + return false; } @@ -186,6 +196,7 @@ namespace ArchiSteamFarm { // A single exclusive tradable item is not matchable, continue continue; default: + // Any other combination of tradable items is always matchable return false; } @@ -198,10 +209,12 @@ namespace ArchiSteamFarm { internal static bool IsFairTypesExchange(IReadOnlyCollection itemsToGive, IReadOnlyCollection itemsToReceive) { if ((itemsToGive == null) || (itemsToGive.Count == 0) || (itemsToReceive == null) || (itemsToReceive.Count == 0)) { ASF.ArchiLogger.LogNullError(nameof(itemsToGive) + " || " + nameof(itemsToReceive)); + return false; } Dictionary> itemsToGivePerGame = new Dictionary>(); + foreach (Steam.Asset item in itemsToGive) { if (itemsToGivePerGame.TryGetValue(item.RealAppID, out Dictionary itemsPerType)) { itemsPerType[item.Type] = itemsPerType.TryGetValue(item.Type, out uint amount) ? amount + item.Amount : item.Amount; @@ -212,6 +225,7 @@ namespace ArchiSteamFarm { } Dictionary> itemsToReceivePerGame = new Dictionary>(); + foreach (Steam.Asset item in itemsToReceive) { if (itemsToReceivePerGame.TryGetValue(item.RealAppID, out Dictionary itemsPerType)) { itemsPerType[item.Type] = itemsPerType.TryGetValue(item.Type, out uint amount) ? amount + item.Amount : item.Amount; @@ -275,16 +289,19 @@ namespace ArchiSteamFarm { private static Dictionary<(uint AppID, Steam.Asset.EType Type), List> GetInventorySets(IReadOnlyCollection inventory) { if ((inventory == null) || (inventory.Count == 0)) { ASF.ArchiLogger.LogNullError(nameof(inventory)); + return null; } Dictionary<(uint AppID, Steam.Asset.EType Type), Dictionary> sets = GetInventoryState(inventory); + return sets.ToDictionary(set => set.Key, set => set.Value.Values.OrderBy(amount => amount).ToList()); } private static bool IsTradeNeutralOrBetter(HashSet inventory, IReadOnlyCollection itemsToGive, IReadOnlyCollection 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; } @@ -319,6 +336,7 @@ namespace ArchiSteamFarm { if (amountToGive > 0) { ASF.ArchiLogger.LogNullError(nameof(amountToGive)); + return false; } @@ -385,6 +403,7 @@ namespace ArchiSteamFarm { private async Task ParseActiveTrades() { HashSet tradeOffers = await Bot.ArchiWebHandler.GetActiveTradeOffers().ConfigureAwait(false); + if ((tradeOffers == null) || (tradeOffers.Count == 0)) { return; } @@ -402,6 +421,7 @@ namespace ArchiSteamFarm { 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(); + if (mobileTradeOfferIDs.Count > 0) { if (!await Bot.Actions.AcceptConfirmations(true, Steam.ConfirmationDetails.EType.Trade, mobileTradeOfferIDs, true).ConfigureAwait(false)) { return; @@ -418,11 +438,13 @@ namespace ArchiSteamFarm { private async Task<(ParseTradeResult TradeResult, bool RequiresMobileConfirmation)> ParseTrade(Steam.TradeOffer tradeOffer) { if (tradeOffer == null) { Bot.ArchiLogger.LogNullError(nameof(tradeOffer)); + return (null, false); } if (tradeOffer.State != Steam.TradeOffer.ETradeOfferState.Active) { Bot.ArchiLogger.LogGenericError(string.Format(Strings.ErrorIsInvalid, tradeOffer.State)); + return (null, false); } @@ -430,12 +452,15 @@ namespace ArchiSteamFarm { // We've already seen this trade IgnoredTrades.Add(tradeOffer.TradeOfferID); Bot.ArchiLogger.LogGenericInfo(string.Format(Strings.IgnoringTrade, tradeOffer.TradeOfferID)); + return (new ParseTradeResult(tradeOffer.TradeOfferID, ParseTradeResult.EResult.RejectedPermanently), false); } ParseTradeResult result = await ShouldAcceptTrade(tradeOffer).ConfigureAwait(false); + if (result == null) { Bot.ArchiLogger.LogNullError(nameof(result)); + return (null, false); } @@ -456,15 +481,18 @@ namespace ArchiSteamFarm { case ParseTradeResult.EResult.RejectedPermanently when Bot.BotConfig.BotBehaviour.HasFlag(BotConfig.EBotBehaviour.RejectInvalidTrades): Bot.ArchiLogger.LogGenericInfo(string.Format(Strings.RejectingTrade, tradeOffer.TradeOfferID)); await Bot.ArchiWebHandler.DeclineTradeOffer(tradeOffer.TradeOfferID).ConfigureAwait(false); + return (result, false); case ParseTradeResult.EResult.RejectedPermanently: IgnoredTrades.Add(tradeOffer.TradeOfferID); goto case ParseTradeResult.EResult.RejectedTemporarily; case ParseTradeResult.EResult.RejectedTemporarily: Bot.ArchiLogger.LogGenericInfo(string.Format(Strings.IgnoringTrade, tradeOffer.TradeOfferID)); + return (result, false); default: Bot.ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(result.Result), result.Result)); + return (null, false); } } @@ -472,6 +500,7 @@ namespace ArchiSteamFarm { private async Task ShouldAcceptTrade(Steam.TradeOffer tradeOffer) { if (tradeOffer == null) { Bot.ArchiLogger.LogNullError(nameof(tradeOffer)); + return null; } @@ -490,9 +519,11 @@ namespace ArchiSteamFarm { // Check if it's donation trade switch (tradeOffer.ItemsToGive.Count) { case 0 when tradeOffer.ItemsToReceive.Count == 0: + // If it's steam issue, temporarily ignore it return new ParseTradeResult(tradeOffer.TradeOfferID, ParseTradeResult.EResult.RejectedTemporarily, tradeOffer.ItemsToReceive); case 0: + // Otherwise react accordingly, depending on our preference bool acceptDonations = Bot.BotConfig.TradingPreferences.HasFlag(BotConfig.ETradingPreferences.AcceptDonations); bool acceptBotTrades = !Bot.BotConfig.TradingPreferences.HasFlag(BotConfig.ETradingPreferences.DontAcceptBotTrades); @@ -509,6 +540,7 @@ namespace ArchiSteamFarm { // Otherwise we either accept donations but not bot trades, or we accept bot trades but not donations bool isBotTrade = (tradeOffer.OtherSteamID64 != 0) && Bot.Bots.Values.Any(bot => bot.SteamID == tradeOffer.OtherSteamID64); + return new ParseTradeResult(tradeOffer.TradeOfferID, (acceptDonations && !isBotTrade) || (acceptBotTrades && isBotTrade) ? ParseTradeResult.EResult.Accepted : ParseTradeResult.EResult.RejectedPermanently, tradeOffer.ItemsToReceive); } @@ -531,6 +563,7 @@ namespace ArchiSteamFarm { // Fetch trade hold duration byte? holdDuration = await Bot.GetTradeHoldDuration(tradeOffer.OtherSteamID64, tradeOffer.TradeOfferID).ConfigureAwait(false); + if (!holdDuration.HasValue) { // If we can't get trade hold duration, reject trade temporarily return new ParseTradeResult(tradeOffer.TradeOfferID, ParseTradeResult.EResult.RejectedTemporarily, tradeOffer.ItemsToReceive); @@ -558,9 +591,11 @@ namespace ArchiSteamFarm { // Now check if it's worth for us to do the trade HashSet inventory = await Bot.ArchiWebHandler.GetInventory(Bot.SteamID, wantedSets: wantedSets).ConfigureAwait(false); + if ((inventory == null) || (inventory.Count == 0)) { // If we can't check our inventory when not using MatchEverything, this is a temporary failure Bot.ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorIsEmpty, nameof(inventory))); + return new ParseTradeResult(tradeOffer.TradeOfferID, ParseTradeResult.EResult.RejectedTemporarily, tradeOffer.ItemsToReceive); } diff --git a/ArchiSteamFarm/Utilities.cs b/ArchiSteamFarm/Utilities.cs index d4e172241..082798546 100644 --- a/ArchiSteamFarm/Utilities.cs +++ b/ArchiSteamFarm/Utilities.cs @@ -39,6 +39,7 @@ namespace ArchiSteamFarm { internal 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; } @@ -48,16 +49,19 @@ namespace ArchiSteamFarm { internal static string GetArgsAsText(string text, byte argsToSkip) { if (string.IsNullOrEmpty(text)) { ASF.ArchiLogger.LogNullError(nameof(text)); + return null; } string[] args = text.Split((char[]) null, argsToSkip + 1, StringSplitOptions.RemoveEmptyEntries); + return args[args.Length - 1]; } 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; } @@ -67,10 +71,12 @@ namespace ArchiSteamFarm { uri = new Uri(url); } catch (UriFormatException e) { ASF.ArchiLogger.LogGenericException(e); + return null; } CookieCollection cookies = cookieContainer.GetCookies(uri); + return cookies.Count > 0 ? (from Cookie cookie in cookies where cookie.Name.Equals(name) select cookie.Value).FirstOrDefault() : null; } @@ -79,6 +85,7 @@ namespace ArchiSteamFarm { internal static void InBackground(Action action, bool longRunning = false) { if (action == null) { ASF.ArchiLogger.LogNullError(nameof(action)); + return; } @@ -94,6 +101,7 @@ namespace ArchiSteamFarm { internal static void InBackground(Func function, bool longRunning = false) { if (function == null) { ASF.ArchiLogger.LogNullError(nameof(function)); + return; } @@ -109,6 +117,7 @@ namespace ArchiSteamFarm { internal static async Task> InParallel(IEnumerable> tasks) { if (tasks == null) { ASF.ArchiLogger.LogNullError(nameof(tasks)); + return null; } @@ -125,6 +134,7 @@ namespace ArchiSteamFarm { break; default: results = await Task.WhenAll(tasks).ConfigureAwait(false); + break; } @@ -134,11 +144,13 @@ namespace ArchiSteamFarm { internal static async Task InParallel(IEnumerable tasks) { if (tasks == null) { ASF.ArchiLogger.LogNullError(nameof(tasks)); + return; } switch (Program.GlobalConfig.OptimizationMode) { case GlobalConfig.EOptimizationMode.MinMemoryUsage: + foreach (Task task in tasks) { await task.ConfigureAwait(false); } @@ -146,6 +158,7 @@ namespace ArchiSteamFarm { break; default: await Task.WhenAll(tasks).ConfigureAwait(false); + break; } } @@ -153,6 +166,7 @@ namespace ArchiSteamFarm { internal static bool IsValidCdKey(string key) { if (string.IsNullOrEmpty(key)) { ASF.ArchiLogger.LogNullError(nameof(key)); + return false; } @@ -162,10 +176,12 @@ namespace ArchiSteamFarm { internal static bool IsValidHexadecimalString(string text) { if (string.IsNullOrEmpty(text)) { ASF.ArchiLogger.LogNullError(nameof(text)); + return false; } const byte split = 16; + for (byte i = 0; i < text.Length; i += split) { string textPart = string.Join("", text.Skip(i).Take(split)); @@ -186,6 +202,7 @@ namespace ArchiSteamFarm { internal static int RandomNext(int maxWithout) { if (maxWithout <= 0) { ASF.ArchiLogger.LogNullError(nameof(maxWithout)); + return -1; } @@ -202,6 +219,7 @@ namespace ArchiSteamFarm { StringBuilder result = new StringBuilder(); ConsoleKeyInfo keyInfo; + while ((keyInfo = Console.ReadKey(true)).Key != ConsoleKey.Enter) { if (!char.IsControl(keyInfo.KeyChar)) { result.Append(keyInfo.KeyChar); @@ -221,6 +239,7 @@ namespace ArchiSteamFarm { } Console.WriteLine(); + return result.ToString(); } diff --git a/ArchiSteamFarm/WebBrowser.cs b/ArchiSteamFarm/WebBrowser.cs index 8882f8867..8ce77cb59 100644 --- a/ArchiSteamFarm/WebBrowser.cs +++ b/ArchiSteamFarm/WebBrowser.cs @@ -89,6 +89,7 @@ namespace ArchiSteamFarm { internal static HtmlDocument StringToHtmlDocument(string html) { if (html == null) { ASF.ArchiLogger.LogNullError(nameof(html)); + return null; } @@ -101,6 +102,7 @@ namespace ArchiSteamFarm { internal async Task UrlGetToBinaryWithProgress(string request, string referer = null, byte maxTries = MaxTries) { if (string.IsNullOrEmpty(request) || (maxTries == 0)) { ArchiLogger.LogNullError(nameof(request) + " || " + nameof(maxTries)); + return null; } @@ -126,6 +128,7 @@ namespace ArchiSteamFarm { while (contentStream.CanRead) { int read = await contentStream.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false); + if (read == 0) { break; } @@ -148,10 +151,12 @@ namespace ArchiSteamFarm { } } catch (Exception e) { ArchiLogger.LogGenericDebuggingException(e); + return null; } ArchiLogger.LogGenericDebug("100%"); + return new BinaryResponse(response, ms.ToArray()); } } @@ -168,16 +173,19 @@ namespace ArchiSteamFarm { internal async Task UrlGetToHtmlDocument(string request, string referer = null, byte maxTries = MaxTries) { if (string.IsNullOrEmpty(request) || (maxTries == 0)) { ArchiLogger.LogNullError(nameof(request) + " || " + nameof(maxTries)); + return null; } StringResponse response = await UrlGetToString(request, referer, maxTries).ConfigureAwait(false); + return response != null ? new HtmlDocumentResponse(response) : null; } internal async Task> UrlGetToJsonObject(string request, string referer = null, byte maxTries = MaxTries) where T : class { if (string.IsNullOrEmpty(request) || (maxTries == 0)) { ArchiLogger.LogNullError(nameof(request) + " || " + nameof(maxTries)); + return null; } @@ -216,6 +224,7 @@ namespace ArchiSteamFarm { internal async Task UrlGetToString(string request, string referer = null, byte maxTries = MaxTries) { if (string.IsNullOrEmpty(request) || (maxTries == 0)) { ArchiLogger.LogNullError(nameof(request) + " || " + nameof(maxTries)); + return null; } @@ -240,6 +249,7 @@ namespace ArchiSteamFarm { internal async Task UrlGetToXmlDocument(string request, string referer = null, byte maxTries = MaxTries) { if (string.IsNullOrEmpty(request) || (maxTries == 0)) { ArchiLogger.LogNullError(nameof(request) + " || " + nameof(maxTries)); + return null; } @@ -256,6 +266,7 @@ namespace ArchiSteamFarm { xmlDocument.LoadXml(response.Content); } catch (XmlException e) { ArchiLogger.LogGenericWarningException(e); + continue; } @@ -273,6 +284,7 @@ namespace ArchiSteamFarm { internal async Task UrlHead(string request, string referer = null, byte maxTries = MaxTries) { if (string.IsNullOrEmpty(request) || (maxTries == 0)) { ArchiLogger.LogNullError(nameof(request) + " || " + nameof(maxTries)); + return null; } @@ -297,6 +309,7 @@ namespace ArchiSteamFarm { internal async Task UrlPost(string request, IReadOnlyCollection> data = null, string referer = null, byte maxTries = MaxTries) { if (string.IsNullOrEmpty(request) || (maxTries == 0)) { ArchiLogger.LogNullError(nameof(request) + " || " + nameof(maxTries)); + return null; } @@ -321,16 +334,19 @@ namespace ArchiSteamFarm { internal async Task UrlPostToHtmlDocument(string request, IReadOnlyCollection> data = null, string referer = null, byte maxTries = MaxTries) { if (string.IsNullOrEmpty(request) || (maxTries == 0)) { ArchiLogger.LogNullError(nameof(request) + " || " + nameof(maxTries)); + return null; } StringResponse response = await UrlPostToString(request, data, referer, maxTries).ConfigureAwait(false); + return response != null ? new HtmlDocumentResponse(response) : null; } internal async Task> UrlPostToJsonObject(string request, IReadOnlyCollection> data = null, string referer = null, byte maxTries = MaxTries) where T : class { if (string.IsNullOrEmpty(request) || (maxTries == 0)) { ArchiLogger.LogNullError(nameof(request) + " || " + nameof(maxTries)); + return null; } @@ -369,6 +385,7 @@ namespace ArchiSteamFarm { private async Task InternalGet(string request, string referer = null, HttpCompletionOption httpCompletionOptions = HttpCompletionOption.ResponseContentRead) { if (string.IsNullOrEmpty(request)) { ArchiLogger.LogNullError(nameof(request)); + return null; } @@ -378,6 +395,7 @@ namespace ArchiSteamFarm { private async Task InternalHead(string request, string referer = null) { if (string.IsNullOrEmpty(request)) { ArchiLogger.LogNullError(nameof(request)); + return null; } @@ -387,6 +405,7 @@ namespace ArchiSteamFarm { private async Task InternalPost(string request, IReadOnlyCollection> data = null, string referer = null) { if (string.IsNullOrEmpty(request)) { ArchiLogger.LogNullError(nameof(request)); + return null; } @@ -396,6 +415,7 @@ namespace ArchiSteamFarm { private async Task InternalRequest(Uri requestUri, HttpMethod httpMethod, IReadOnlyCollection> data = null, string referer = null, HttpCompletionOption httpCompletionOption = HttpCompletionOption.ResponseContentRead, byte maxRedirections = MaxTries) { if ((requestUri == null) || (httpMethod == null)) { ArchiLogger.LogNullError(nameof(requestUri) + " || " + nameof(httpMethod)); + return null; } @@ -407,6 +427,7 @@ namespace ArchiSteamFarm { request.Content = new FormUrlEncodedContent(data); } catch (UriFormatException e) { ArchiLogger.LogGenericException(e); + return null; } } @@ -423,6 +444,7 @@ namespace ArchiSteamFarm { response = await HttpClient.SendAsync(request, httpCompletionOption).ConfigureAwait(false); } catch (Exception e) { ArchiLogger.LogGenericDebuggingException(e); + return null; } } @@ -451,13 +473,17 @@ namespace ArchiSteamFarm { switch (redirectUri.Scheme) { case "http": case "https": + break; case "steammobile": + // Those redirections are invalid, but we're aware of that and we have extra logic for them 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)); + break; } } else { @@ -465,6 +491,7 @@ namespace ArchiSteamFarm { } response.Dispose(); + return await InternalRequest(redirectUri, httpMethod, data, referer, httpCompletionOption, --maxRedirections).ConfigureAwait(false); } @@ -480,6 +507,7 @@ namespace ArchiSteamFarm { private async Task UrlPostToString(string request, IReadOnlyCollection> data = null, string referer = null, byte maxTries = MaxTries) { if (string.IsNullOrEmpty(request) || (maxTries == 0)) { ArchiLogger.LogNullError(nameof(request) + " || " + nameof(maxTries)); + return null; }