R# cleanup, small code improvements

This commit is contained in:
JustArchi 2019-05-19 15:38:06 +02:00
parent ec95dc41ce
commit b98268a465
19 changed files with 143 additions and 296 deletions

View file

@ -55,8 +55,8 @@ namespace ArchiSteamFarm.CustomPlugins.ExamplePlugin {
// ReSharper disable once UseDeconstruction - deconstruction is not available in .NET Framework
foreach (KeyValuePair<string, JToken> configProperty in additionalConfigProperties) {
// It's a good idea to prefix your custom properties with the name of your plugin, so there will be no possible conflict of ASF or other plugins using the same name, neither now or in the future
switch (configProperty.Key) {
// It's a good idea to prefix your custom properties with the name of your plugin, so there will be no possible conflict of ASF or other plugins using the same name, neither now or in the future
case nameof(ExamplePlugin) + "TestProperty" when configProperty.Value.Type == JTokenType.Boolean:
bool exampleBooleanValue = configProperty.Value.Value<bool>();
ASF.ArchiLogger.LogGenericInfo(nameof(ExamplePlugin) + "TestProperty boolean property has been found with a value of: " + exampleBooleanValue);
@ -75,21 +75,15 @@ namespace ArchiSteamFarm.CustomPlugins.ExamplePlugin {
// If you do not recognize the command, just return null/empty and allow ASF to gracefully return "unknown command" to user on usual basis
public async Task<string> OnBotCommand(Bot bot, ulong steamID, string message, string[] args) {
// In comparison with OnBotMessage(), we're using asynchronous CatAPI call here, so we declare our method as async and return the message as usual
// Notice how we handle access here as well, it'll work only for FamilySharing+
switch (args[0].ToUpperInvariant()) {
// Notice how we handle access here as well, it'll work only for FamilySharing+
case "CAT" when bot.HasPermission(steamID, BotConfig.EPermission.FamilySharing):
// Notice how we can decide whether to use bot's AWH WebBrowser or ASF's one. For Steam-related requests, AWH's one should always be used, for third-party requests like those it doesn't really matter
// Still, it makes sense to pass AWH's one, so in case you get some errors or alike, you know from which bot instance they come from. It's similar to using Bot's ArchiLogger compared to ASF's one
string randomCatURL = await CatAPI.GetRandomCatURL(bot.ArchiWebHandler.WebBrowser).ConfigureAwait(false);
if (string.IsNullOrEmpty(randomCatURL)) {
return "God damn it, we're out of cats, care to notify my master? Thanks!";
}
return randomCatURL;
return !string.IsNullOrEmpty(randomCatURL) ? randomCatURL : "God damn it, we're out of cats, care to notify my master? Thanks!";
default:
return null;
}
}

View file

@ -567,6 +567,8 @@ limitations under the License.</s:String>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002EXml_002ECodeStyle_002EFormatSettingsUpgrade_002EXmlMoveToCommonFormatterSettingsUpgrade/@EntryIndexedValue">True</s:Boolean>
<s:Int64 x:Key="/Default/Environment/UnitTesting/ParallelProcessesCount/@EntryValue">8</s:Int64>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Archi/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=BGR_0027s/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=bot_0027s/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=broadcasted/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Domeradzki/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=splitted/@EntryIndexedValue">True</s:Boolean>

View file

@ -81,10 +81,8 @@ namespace ArchiSteamFarm {
switch (fileType) {
case EFileType.Config:
return Path.Combine(SharedInfo.ConfigDirectory, SharedInfo.GlobalConfigFileName);
case EFileType.Database:
return Path.Combine(SharedInfo.ConfigDirectory, SharedInfo.GlobalDatabaseFileName);
default:
ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(fileType), fileType));
@ -350,10 +348,8 @@ namespace ArchiSteamFarm {
switch (botName) {
case SharedInfo.ASF:
return false;
default:
return true;
}
}
@ -687,28 +683,22 @@ namespace ArchiSteamFarm {
ArchiLogger.LogNullError(nameof(relativeDirectoryName));
return false;
// No directory, root folder
case "":
// No directory, root folder
switch (fileName) {
// Files with those names in root directory we want to keep
case SharedInfo.LogFile:
case "NLog.config":
// Files with those names in root directory we want to keep
continue;
}
break;
// Files in those directories we want to keep in their current place
case SharedInfo.ConfigDirectory:
case SharedInfo.PluginsDirectory:
case SharedInfo.UpdateDirectory:
// Files in those directories we want to keep in their current place
continue;
default:
// Files in subdirectories of those directories we want to keep as well
if (Utilities.RelativeDirectoryStartsWith(relativeDirectoryName, SharedInfo.ConfigDirectory, SharedInfo.PluginsDirectory, SharedInfo.UpdateDirectory)) {
continue;
@ -758,7 +748,6 @@ namespace ArchiSteamFarm {
// We're not interested in extracting placeholder files (but we still want directories created for them, done above)
switch (zipFile.Name) {
case ".gitkeep":
continue;
}
}

View file

@ -38,13 +38,10 @@ namespace ArchiSteamFarm {
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));
@ -62,13 +59,10 @@ namespace ArchiSteamFarm {
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));

View file

@ -851,7 +851,6 @@ namespace ArchiSteamFarm {
case EUserNotification.Items:
case EUserNotification.ModeratorMessages:
case EUserNotification.Trading:
break;
default:
ASF.ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(type), type));

View file

@ -2463,12 +2463,10 @@ 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)) {
@ -2491,16 +2489,13 @@ 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));
@ -2554,17 +2549,13 @@ namespace ArchiSteamFarm {
switch (userPrivacy.Settings.Profile) {
case Steam.UserPrivacy.PrivacySettings.EPrivacySetting.FriendsOnly:
case Steam.UserPrivacy.PrivacySettings.EPrivacySetting.Private:
return (true, false);
case Steam.UserPrivacy.PrivacySettings.EPrivacySetting.Public:
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));

View file

@ -452,10 +452,8 @@ namespace ArchiSteamFarm {
switch (permission) {
case BotConfig.EPermission.FamilySharing when SteamFamilySharingIDs.Contains(steamID):
return true;
default:
return BotConfig.SteamUserPermissions.TryGetValue(steamID, out BotConfig.EPermission realPermission) && (realPermission >= permission);
}
}
@ -620,11 +618,9 @@ namespace ArchiSteamFarm {
// 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));
@ -641,7 +637,6 @@ namespace ArchiSteamFarm {
// We must convert this to uppercase, since Valve doesn't stick to any convention and we can have a case mismatch
switch (type.ToUpperInvariant()) {
// Types that can be idled
case "APPLICATION":
case "EPISODE":
case "GAME":
@ -650,16 +645,14 @@ namespace ArchiSteamFarm {
case "SERIES":
case "TOOL":
case "VIDEO":
// Types that can be idled
return (appID, DateTime.MinValue);
// Types that can't be idled
case "ADVERTISING":
case "DEMO":
case "DLC":
case "GUIDE":
case "HARDWARE":
// Types that can't be idled
break;
default:
ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(type), type));
@ -710,25 +703,18 @@ namespace ArchiSteamFarm {
switch (fileType) {
case EFileType.Config:
return botPath + SharedInfo.ConfigExtension;
case EFileType.Database:
return botPath + SharedInfo.DatabaseExtension;
case EFileType.KeysToRedeem:
return botPath + SharedInfo.KeysExtension;
case EFileType.KeysToRedeemUnused:
return botPath + SharedInfo.KeysExtension + SharedInfo.KeysUnusedExtension;
case EFileType.KeysToRedeemUsed:
return botPath + SharedInfo.KeysExtension + SharedInfo.KeysUsedExtension;
case EFileType.MobileAuthenticator:
return botPath + SharedInfo.MobileAuthenticatorExtension;
case EFileType.SentryFile:
return botPath + SharedInfo.SentryHashExtension;
default:
ASF.ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(fileType), fileType));
@ -1326,14 +1312,12 @@ namespace ArchiSteamFarm {
break;
case ASF.EUserInputType.Login:
if (BotConfig != null) {
BotConfig.SteamLogin = inputValue;
}
break;
case ASF.EUserInputType.Password:
if (BotConfig != null) {
BotConfig.DecryptedSteamPassword = inputValue;
}
@ -1344,7 +1328,6 @@ namespace ArchiSteamFarm {
break;
case ASF.EUserInputType.SteamParentalCode:
if (BotConfig != null) {
BotConfig.SteamParentalCode = inputValue;
}
@ -1844,27 +1827,21 @@ namespace ArchiSteamFarm {
return steamID == BotConfig.SteamMasterClanID;
}
private static bool IsRefundable(EPaymentMethod method) {
if (method == EPaymentMethod.None) {
ASF.ArchiLogger.LogNullError(nameof(method));
private static bool IsRefundable(EPaymentMethod paymentMethod) {
if (paymentMethod == EPaymentMethod.None) {
ASF.ArchiLogger.LogNullError(nameof(paymentMethod));
return false;
}
switch (method) {
switch (paymentMethod) {
case EPaymentMethod.ActivationCode:
case EPaymentMethod.Complimentary: // This is also a flag
case EPaymentMethod.GuestPass:
case EPaymentMethod.HardwarePromo:
return false;
default:
if (method.HasFlag(EPaymentMethod.Complimentary)) {
return false;
}
return true;
return !paymentMethod.HasFlag(EPaymentMethod.Complimentary);
}
}
@ -1889,7 +1866,9 @@ namespace ArchiSteamFarm {
return;
}
await ArchiHandler.JoinChatRoomGroup(MasterChatGroupID).ConfigureAwait(false);
if (!await ArchiHandler.JoinChatRoomGroup(MasterChatGroupID).ConfigureAwait(false)) {
ArchiLogger.LogGenericWarning(string.Format(Strings.WarningFailedWithError, nameof(ArchiHandler.JoinChatRoomGroup)));
}
}
private static async Task LimitLoginRequestsAsync() {
@ -2052,18 +2031,15 @@ 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;
@ -2144,9 +2120,10 @@ namespace ArchiSteamFarm {
break;
default:
if (HasPermission(friend.SteamID, BotConfig.EPermission.FamilySharing)) {
await ArchiHandler.AddFriend(friend.SteamID).ConfigureAwait(false);
if (!await ArchiHandler.AddFriend(friend.SteamID).ConfigureAwait(false)) {
ArchiLogger.LogGenericWarning(string.Format(Strings.WarningFailedWithError, nameof(ArchiHandler.AddFriend)));
}
break;
}
@ -2154,13 +2131,17 @@ namespace ArchiSteamFarm {
bool acceptFriendRequest = await PluginsCore.OnBotFriendRequest(this, friend.SteamID).ConfigureAwait(false);
if (acceptFriendRequest) {
await ArchiHandler.AddFriend(friend.SteamID).ConfigureAwait(false);
if (!await ArchiHandler.AddFriend(friend.SteamID).ConfigureAwait(false)) {
ArchiLogger.LogGenericWarning(string.Format(Strings.WarningFailedWithError, nameof(ArchiHandler.AddFriend)));
}
break;
}
if (BotConfig.BotBehaviour.HasFlag(BotConfig.EBotBehaviour.RejectInvalidFriendInvites)) {
await ArchiHandler.RemoveFriend(friend.SteamID).ConfigureAwait(false);
if (!await ArchiHandler.RemoveFriend(friend.SteamID).ConfigureAwait(false)) {
ArchiLogger.LogGenericWarning(string.Format(Strings.WarningFailedWithError, nameof(ArchiHandler.RemoveFriend)));
}
}
break;
@ -2326,7 +2307,6 @@ namespace ArchiSteamFarm {
switch (callback.Result) {
case EResult.LoggedInElsewhere:
// This result directly indicates that playing was blocked when we got (forcefully) disconnected
PlayingWasBlocked = true;
@ -2368,7 +2348,6 @@ 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();
@ -2387,7 +2366,6 @@ namespace ArchiSteamFarm {
break;
case EResult.AccountLoginDeniedNeedTwoFactor:
if (!HasMobileAuthenticator) {
string twoFactorCode = await Logging.GetUserInput(ASF.EUserInputType.TwoFactorAuthentication, BotName).ConfigureAwait(false);
@ -2485,7 +2463,10 @@ namespace ArchiSteamFarm {
if (BotConfig.SteamMasterClanID != 0) {
Utilities.InBackground(
async () => {
await ArchiWebHandler.JoinGroup(BotConfig.SteamMasterClanID).ConfigureAwait(false);
if (!await ArchiWebHandler.JoinGroup(BotConfig.SteamMasterClanID).ConfigureAwait(false)) {
ArchiLogger.LogGenericWarning(string.Format(Strings.WarningFailedWithError, nameof(ArchiWebHandler.JoinGroup)));
}
await JoinMasterChatGroupID().ConfigureAwait(false);
}
);
@ -2514,7 +2495,6 @@ namespace ArchiSteamFarm {
break;
default:
// Unexpected result, shutdown immediately
ArchiLogger.LogGenericError(string.Format(Strings.BotUnableToLogin, callback.Result, callback.ExtendedResult));
Stop();
@ -2817,7 +2797,6 @@ namespace ArchiSteamFarm {
case EPurchaseResultDetail.DoesNotOwnRequiredApp:
case EPurchaseResultDetail.RestrictedCountry:
case EPurchaseResultDetail.Timeout:
break;
case EPurchaseResultDetail.BadActivationCode:
case EPurchaseResultDetail.DuplicateActivationCode:

View file

@ -572,6 +572,12 @@ namespace ArchiSteamFarm {
name = WebUtility.HtmlDecode(name.Substring(nameStartIndex, nameEndIndex - nameStartIndex));
if (string.IsNullOrEmpty(name)) {
Bot.ArchiLogger.LogNullError(nameof(name));
continue;
}
// Levels
byte badgeLevel = 0;
@ -626,7 +632,6 @@ namespace ArchiSteamFarm {
break;
default:
if (backgroundTasks == null) {
backgroundTasks = new List<Task>();
}
@ -1058,7 +1063,6 @@ 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);

View file

@ -89,85 +89,58 @@ namespace ArchiSteamFarm {
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 "BGR":
return ResponseBackgroundGamesRedeemer(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:
string pluginsResponse = await PluginsCore.OnBotCommand(Bot, steamID, message, args).ConfigureAwait(false);
@ -175,191 +148,130 @@ namespace ArchiSteamFarm {
return !string.IsNullOrEmpty(pluginsResponse) ? pluginsResponse : 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 "BGR":
return await ResponseBackgroundGamesRedeemer(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:
string pluginsResponse = await PluginsCore.OnBotCommand(Bot, steamID, message, args).ConfigureAwait(false);
@ -381,7 +293,10 @@ namespace ArchiSteamFarm {
string pluginsResponse = await PluginsCore.OnBotMessage(Bot, steamID, message).ConfigureAwait(false);
if (!string.IsNullOrEmpty(pluginsResponse)) {
await Bot.SendMessage(steamID, pluginsResponse).ConfigureAwait(false);
if (!await Bot.SendMessage(steamID, pluginsResponse).ConfigureAwait(false)) {
Bot.ArchiLogger.LogGenericWarning(string.Format(Strings.WarningFailedWithError, nameof(Bot.SendMessage)));
Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.Content, pluginsResponse));
}
}
return;
@ -395,10 +310,14 @@ namespace ArchiSteamFarm {
bool feedback = Bot.HasPermission(steamID, BotConfig.EPermission.FamilySharing);
if (feedback && !responseTask.IsCompleted) {
await Bot.SendTypingMessage(steamID).ConfigureAwait(false);
if (!await Bot.SendTypingMessage(steamID).ConfigureAwait(false)) {
Bot.ArchiLogger.LogGenericWarning(string.Format(Strings.WarningFailedWithError, nameof(Bot.SendTypingMessage)));
}
while (!responseTask.IsCompleted && (await Task.WhenAny(responseTask, Task.Delay(SteamTypingStatusDelay)).ConfigureAwait(false) != responseTask)) {
await Bot.SendTypingMessage(steamID).ConfigureAwait(false);
if (!await Bot.SendTypingMessage(steamID).ConfigureAwait(false)) {
Bot.ArchiLogger.LogGenericWarning(string.Format(Strings.WarningFailedWithError, nameof(Bot.SendTypingMessage)));
}
}
}
@ -413,7 +332,10 @@ namespace ArchiSteamFarm {
response = FormatBotResponse(Strings.UnknownCommand);
}
await Bot.SendMessage(steamID, response).ConfigureAwait(false);
if (!await Bot.SendMessage(steamID, response).ConfigureAwait(false)) {
Bot.ArchiLogger.LogGenericWarning(string.Format(Strings.WarningFailedWithError, nameof(Bot.SendMessage)));
Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.Content, response));
}
}
internal async Task HandleMessage(ulong chatGroupID, ulong chatID, ulong steamID, string message) {
@ -428,7 +350,10 @@ namespace ArchiSteamFarm {
string pluginsResponse = await PluginsCore.OnBotMessage(Bot, steamID, message).ConfigureAwait(false);
if (!string.IsNullOrEmpty(pluginsResponse)) {
await Bot.SendMessage(chatGroupID, chatID, pluginsResponse).ConfigureAwait(false);
if (!await Bot.SendMessage(chatGroupID, chatID, pluginsResponse).ConfigureAwait(false)) {
Bot.ArchiLogger.LogGenericWarning(string.Format(Strings.WarningFailedWithError, nameof(Bot.SendMessage)));
Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.Content, pluginsResponse));
}
}
return;
@ -444,10 +369,14 @@ namespace ArchiSteamFarm {
if (feedback && !responseTask.IsCompleted) {
string pleaseWaitMessage = FormatBotResponse(Strings.PleaseWait);
await Bot.SendMessage(chatGroupID, chatID, pleaseWaitMessage).ConfigureAwait(false);
if (!await Bot.SendMessage(chatGroupID, chatID, pleaseWaitMessage).ConfigureAwait(false)) {
Bot.ArchiLogger.LogGenericWarning(string.Format(Strings.WarningFailedWithError, nameof(Bot.SendMessage)));
}
while (!responseTask.IsCompleted && (await Task.WhenAny(responseTask, Task.Delay(SteamTypingStatusDelay)).ConfigureAwait(false) != responseTask)) {
await Bot.SendMessage(chatGroupID, chatID, pleaseWaitMessage).ConfigureAwait(false);
if (!await Bot.SendMessage(chatGroupID, chatID, pleaseWaitMessage).ConfigureAwait(false)) {
Bot.ArchiLogger.LogGenericWarning(string.Format(Strings.WarningFailedWithError, nameof(Bot.SendMessage)));
}
}
}
@ -462,7 +391,10 @@ namespace ArchiSteamFarm {
response = FormatBotResponse(Strings.UnknownCommand);
}
await Bot.SendMessage(chatGroupID, chatID, response).ConfigureAwait(false);
if (!await Bot.SendMessage(chatGroupID, chatID, response).ConfigureAwait(false)) {
Bot.ArchiLogger.LogGenericWarning(string.Format(Strings.WarningFailedWithError, nameof(Bot.SendMessage)));
Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.Content, response));
}
}
internal void OnNewLicenseList() {
@ -759,7 +691,6 @@ namespace ArchiSteamFarm {
break;
default:
return FormatBotResponse(string.Format(Strings.ErrorIsInvalid, flag));
}
}
@ -1981,12 +1912,13 @@ namespace ArchiSteamFarm {
// Child setting can't be less restrictive than its parent
switch (index) {
case 0: // Profile
case 0:
// Profile
profile = privacySetting;
break;
case 1: // OwnedGames, child of Profile
case 1:
// OwnedGames, child of Profile
if (profile < privacySetting) {
return FormatBotResponse(string.Format(Strings.ErrorIsInvalid, nameof(ownedGames)));
}
@ -1994,8 +1926,8 @@ namespace ArchiSteamFarm {
ownedGames = privacySetting;
break;
case 2: // Playtime, child of OwnedGames
case 2:
// Playtime, child of OwnedGames
if (ownedGames < privacySetting) {
return FormatBotResponse(string.Format(Strings.ErrorIsInvalid, nameof(playtime)));
}
@ -2003,8 +1935,8 @@ namespace ArchiSteamFarm {
playtime = privacySetting;
break;
case 3: // FriendsList, child of Profile
case 3:
// FriendsList, child of Profile
if (profile < privacySetting) {
return FormatBotResponse(string.Format(Strings.ErrorIsInvalid, nameof(ownedGames)));
}
@ -2012,8 +1944,8 @@ namespace ArchiSteamFarm {
friendsList = privacySetting;
break;
case 4: // Inventory, child of Profile
case 4:
// Inventory, child of Profile
if (profile < privacySetting) {
return FormatBotResponse(string.Format(Strings.ErrorIsInvalid, nameof(inventory)));
}
@ -2021,8 +1953,8 @@ namespace ArchiSteamFarm {
inventory = privacySetting;
break;
case 5: // InventoryGifts, child of Inventory
case 5:
// InventoryGifts, child of Inventory
if (inventory < privacySetting) {
return FormatBotResponse(string.Format(Strings.ErrorIsInvalid, nameof(inventoryGifts)));
}
@ -2030,8 +1962,8 @@ namespace ArchiSteamFarm {
inventoryGifts = privacySetting;
break;
case 6: // Comments, child of Profile
case 6:
// Comments, child of Profile
if (profile < privacySetting) {
return FormatBotResponse(string.Format(Strings.ErrorIsInvalid, nameof(comments)));
}
@ -2124,7 +2056,8 @@ namespace ArchiSteamFarm {
StringBuilder response = new StringBuilder();
using (HashSet<string>.Enumerator keysEnumerator = pendingKeys.GetEnumerator()) {
string key = keysEnumerator.MoveNext() ? keysEnumerator.Current : null; // Initial key
// Initial key
string key = keysEnumerator.MoveNext() ? keysEnumerator.Current : null;
while (!string.IsNullOrEmpty(key)) {
string startingKey = key;
@ -2134,19 +2067,24 @@ namespace ArchiSteamFarm {
while (!string.IsNullOrEmpty(key) && (currentBot != null)) {
if (redeemFlags.HasFlag(ERedeemFlags.Validate) && !Utilities.IsValidCdKey(key)) {
key = keysEnumerator.MoveNext() ? keysEnumerator.Current : null; // Next key
// Next key
key = keysEnumerator.MoveNext() ? keysEnumerator.Current : null;
continue; // Keep current bot
// Keep current bot
continue;
}
if ((currentBot == Bot) && (redeemFlags.HasFlag(ERedeemFlags.SkipInitial) || rateLimitedBots.Contains(currentBot))) {
currentBot = null; // Either bot will be changed, or loop aborted
// Either bot will be changed, or loop aborted
currentBot = null;
} 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
// Either bot will be changed, or loop aborted
currentBot = null;
} else {
if ((result.PurchaseResultDetail == EPurchaseResultDetail.CannotRedeemCodeFromClient) && (Bot.WalletCurrency != ECurrencyCode.Invalid)) {
// If it's a wallet code, we try to redeem it first, then handle the inner result as our primary one
@ -2154,7 +2092,9 @@ namespace ArchiSteamFarm {
if (walletResult != null) {
result.Result = walletResult.Value.Result;
result.PurchaseResultDetail = walletResult.Value.PurchaseResult.GetValueOrDefault(walletResult.Value.Result == EResult.OK ? EPurchaseResultDetail.NoDetail : EPurchaseResultDetail.BadActivationCode); // BadActivationCode is our smart guess in this case
// BadActivationCode is our smart guess in this case
result.PurchaseResultDetail = walletResult.Value.PurchaseResult.GetValueOrDefault(walletResult.Value.Result == EResult.OK ? EPurchaseResultDetail.NoDetail : EPurchaseResultDetail.BadActivationCode);
} else {
result.Result = EResult.Timeout;
result.PurchaseResultDetail = EPurchaseResultDetail.Timeout;
@ -2167,7 +2107,6 @@ 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 {
@ -2178,18 +2117,20 @@ namespace ArchiSteamFarm {
unusedKeys.Remove(key);
}
key = keysEnumerator.MoveNext() ? keysEnumerator.Current : null; // Next key
// Next key
key = keysEnumerator.MoveNext() ? keysEnumerator.Current : null;
if (result.PurchaseResultDetail == EPurchaseResultDetail.NoDetail) {
break; // Next bot (if needed)
// Next bot (if needed)
break;
}
continue; // Keep current bot
// Keep current bot
continue;
case EPurchaseResultDetail.AccountLocked:
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 {
@ -2197,13 +2138,16 @@ namespace ArchiSteamFarm {
}
if (!forward || (keepMissingGames && (result.PurchaseResultDetail != EPurchaseResultDetail.AlreadyPurchased))) {
key = keysEnumerator.MoveNext() ? keysEnumerator.Current : null; // Next key
// Next key
key = keysEnumerator.MoveNext() ? keysEnumerator.Current : null;
break; // Next bot (if needed)
// Next bot (if needed)
break;
}
if (distribute) {
break; // Next bot, without changing key
// Next bot, without changing key
break;
}
Dictionary<uint, string> items = result.Items ?? new Dictionary<uint, string>();
@ -2223,7 +2167,8 @@ namespace ArchiSteamFarm {
case EPurchaseResultDetail.BadActivationCode:
case EPurchaseResultDetail.DuplicateActivationCode:
case EPurchaseResultDetail.NoDetail: // OK
alreadyHandled = true; // This key is already handled, as we either redeemed it or we're sure it's dupe/invalid
// This key is already handled, as we either redeemed it or we're sure it's dupe/invalid
alreadyHandled = true;
unusedKeys.Remove(key);
break;
@ -2252,9 +2197,11 @@ namespace ArchiSteamFarm {
}
}
key = keysEnumerator.MoveNext() ? keysEnumerator.Current : null; // Next key
// Next key
key = keysEnumerator.MoveNext() ? keysEnumerator.Current : null;
break; // Next bot (if needed)
// Next bot (if needed)
break;
case EPurchaseResultDetail.RateLimited:
rateLimitedBots.Add(currentBot);
goto case EPurchaseResultDetail.AccountLocked;
@ -2269,9 +2216,11 @@ namespace ArchiSteamFarm {
unusedKeys.Remove(key);
key = keysEnumerator.MoveNext() ? keysEnumerator.Current : null; // Next key
// Next key
key = keysEnumerator.MoveNext() ? keysEnumerator.Current : null;
break; // Next bot (if needed)
// Next bot (if needed)
break;
}
}
}
@ -2286,8 +2235,8 @@ namespace ArchiSteamFarm {
}
if (key == startingKey) {
// We ran out of bots to try for this key, so change it to avoid infinite loop
key = keysEnumerator.MoveNext() ? keysEnumerator.Current : null; // Next key
// We ran out of bots to try for this key, so change it to avoid infinite loop, next key
key = keysEnumerator.MoveNext() ? keysEnumerator.Current : null;
}
}
}
@ -2737,12 +2686,16 @@ namespace ArchiSteamFarm {
}
// It'd make sense here to actually check return code of ArchiWebHandler.UnpackBooster(), but it lies most of the time | https://github.com/JustArchi/ArchiSteamFarm/issues/704
bool completeSuccess = true;
// It'd also make sense to run all of this in parallel, but it seems that Steam has a lot of problems with inventory-related parallel requests | https://steamcommunity.com/groups/ascfarm/discussions/1/3559414588264550284/
foreach (Steam.Asset item in inventory) {
await Bot.ArchiWebHandler.UnpackBooster(item.RealAppID, item.AssetID).ConfigureAwait(false);
if (!await Bot.ArchiWebHandler.UnpackBooster(item.RealAppID, item.AssetID).ConfigureAwait(false)) {
completeSuccess = false;
}
}
return FormatBotResponse(Strings.Done);
return FormatBotResponse(completeSuccess ? Strings.Success : Strings.Done);
}
[ItemCanBeNull]

View file

@ -79,16 +79,14 @@ namespace ArchiSteamFarm.Helpers {
if (!success) {
switch (fallback) {
case EFallback.DefaultForType:
return (false, default);
case EFallback.FailedNow:
return (false, result);
case EFallback.SuccessPreviously:
return (false, InitializedValue);
default:
ASF.ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(fallback), fallback));
goto case EFallback.DefaultForType;
}
}

View file

@ -515,7 +515,6 @@ namespace ArchiSteamFarm.Json {
foreach (Tag tag in tags) {
switch (tag.Identifier) {
case "cardborder":
switch (tag.Value) {
case "cardborder_0":
type = Asset.EType.TradingCard;
@ -533,7 +532,6 @@ namespace ArchiSteamFarm.Json {
break;
case "droprate":
switch (tag.Value) {
case "droprate_0":
rarity = Asset.ERarity.Common;
@ -555,7 +553,6 @@ namespace ArchiSteamFarm.Json {
break;
case "Game":
if ((tag.Value.Length <= 4) || !tag.Value.StartsWith("app_", StringComparison.Ordinal)) {
ASF.ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(tag.Value), tag.Value));
@ -574,12 +571,10 @@ namespace ArchiSteamFarm.Json {
break;
case "item_class":
switch (tag.Value) {
case "item_class_2":
// This is a fallback in case we'd have no cardborder available to interpret
if (type == Asset.EType.Unknown) {
// This is a fallback in case we'd have no cardborder available to interpret
type = Asset.EType.TradingCard;
}

View file

@ -22,6 +22,7 @@
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Threading.Tasks;
using ArchiSteamFarm.Localization;
using JetBrains.Annotations;
using NLog;
using NLog.Config;
@ -102,7 +103,9 @@ namespace ArchiSteamFarm.NLog {
}
}
await bot.SendMessage(ChatGroupID, SteamID, message).ConfigureAwait(false);
if (!await bot.SendMessage(ChatGroupID, SteamID, message).ConfigureAwait(false)) {
bot.ArchiLogger.LogGenericTrace(string.Format(Strings.WarningFailedWithError, nameof(Bot.SendMessage)));
}
}
private async Task SendPrivateMessage(string message, Bot bot = null) {
@ -120,7 +123,9 @@ namespace ArchiSteamFarm.NLog {
}
}
await bot.SendMessage(SteamID, message).ConfigureAwait(false);
if (!await bot.SendMessage(SteamID, message).ConfigureAwait(false)) {
bot.ArchiLogger.LogGenericTrace(string.Format(Strings.WarningFailedWithError, nameof(Bot.SendMessage)));
}
}
}
}

View file

@ -48,11 +48,9 @@ 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;

View file

@ -43,7 +43,7 @@ namespace ArchiSteamFarm {
internal static bool ShutdownSequenceInitialized { get; private set; }
// We need to keep this one assigned and not calculated on-demand
private static readonly string ProcessFileName = Process.GetCurrentProcess().MainModule.FileName;
private static readonly string ProcessFileName = Process.GetCurrentProcess().MainModule?.FileName ?? throw new ArgumentNullException(nameof(ProcessFileName));
private static readonly TaskCompletionSource<byte> ShutdownResetEvent = new TaskCompletionSource<byte>();
private static bool SystemRequired;
@ -430,7 +430,6 @@ namespace ArchiSteamFarm {
break;
default:
if (cryptKeyNext) {
cryptKeyNext = false;
HandleCryptKeyArgument(arg);
@ -459,7 +458,6 @@ namespace ArchiSteamFarm {
break;
default:
if (pathNext) {
pathNext = false;
HandlePathArgument(arg);

View file

@ -56,13 +56,14 @@ namespace ArchiSteamFarm {
internal const string UpdateDirectory = "_old";
internal const string WebsiteDirectory = "www";
internal static string HomeDirectory => Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
internal static Guid ModuleVersion => Assembly.GetEntryAssembly().ManifestModule.ModuleVersionId;
internal static string HomeDirectory => Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location ?? throw new ArgumentNullException(nameof(HomeDirectory)));
internal static Guid ModuleVersion => Assembly.GetEntryAssembly()?.ManifestModule.ModuleVersionId ?? throw new ArgumentNullException(nameof(ModuleVersion));
[NotNull]
internal static string PublicIdentifier => AssemblyName + (BuildInfo.IsCustomBuild ? "-custom" : "");
internal static Version Version => Assembly.GetEntryAssembly().GetName().Version;
[NotNull]
internal static Version Version => Assembly.GetEntryAssembly()?.GetName().Version ?? throw new ArgumentNullException(nameof(Version));
[SuppressMessage("ReSharper", "ConvertToConstant.Global")]
internal static class BuildInfo {

View file

@ -120,7 +120,11 @@ namespace ArchiSteamFarm {
}
}
internal async Task OnLoggedOn() => await Bot.ArchiWebHandler.JoinGroup(SharedInfo.ASFGroupSteamID).ConfigureAwait(false);
internal async Task OnLoggedOn() {
if (!await Bot.ArchiWebHandler.JoinGroup(SharedInfo.ASFGroupSteamID).ConfigureAwait(false)) {
Bot.ArchiLogger.LogGenericWarning(string.Format(Strings.WarningFailedWithError, nameof(ArchiWebHandler.JoinGroup)));
}
}
internal async Task OnPersonaState(string nickname = null, string avatarHash = null) {
if ((DateTime.UtcNow < LastAnnouncementCheck.AddHours(MinAnnouncementCheckTTL)) && (ShouldSendHeartBeats || (LastHeartBeat == DateTime.MinValue))) {

View file

@ -302,13 +302,11 @@ namespace ArchiSteamFarm {
foreach ((ulong classID, uint amount) in tradableSet) {
switch (amount) {
case 0:
// No tradable items, this should never happen, dictionary should not have this key to begin with
ASF.ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(amount), amount));
return false;
case 1:
// Single tradable item, can be matchable or not depending on the rest of the inventory
if (!fullSet.TryGetValue(classID, out uint fullAmount) || (fullAmount == 0) || (fullAmount < amount)) {
ASF.ArchiLogger.LogNullError(nameof(fullAmount));
@ -324,7 +322,6 @@ namespace ArchiSteamFarm {
// A single exclusive tradable item is not matchable, continue
continue;
default:
// Any other combination of tradable items is always matchable
return false;
}
@ -518,11 +515,9 @@ namespace ArchiSteamFarm {
// Check if it's donation trade
switch (tradeOffer.ItemsToGive.Count) {
case 0 when tradeOffer.ItemsToReceive.Count == 0:
// If it's steam issue, try again later
return ParseTradeResult.EResult.TryAgain;
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);

View file

@ -137,7 +137,6 @@ namespace ArchiSteamFarm {
switch (ASF.GlobalConfig.OptimizationMode) {
case GlobalConfig.EOptimizationMode.MinMemoryUsage:
foreach (Task task in tasks) {
await task.ConfigureAwait(false);
}
@ -183,55 +182,7 @@ namespace ArchiSteamFarm {
return false;
}
if (text.Length % 2 != 0) {
return false;
}
// ulong is 64-bits wide, each hexadecimal character is 4-bits wide, so we split each 16
const byte split = 16;
string lastHex;
if (text.Length >= split) {
StringBuilder hex = new StringBuilder(split, split);
foreach (char character in text) {
hex.Append(character);
if (hex.Length < split) {
continue;
}
if (!ulong.TryParse(hex.ToString(), NumberStyles.HexNumber, null, out _)) {
return false;
}
hex.Clear();
}
if (hex.Length == 0) {
return true;
}
lastHex = hex.ToString();
} else {
lastHex = text;
}
switch (lastHex.Length) {
case 2:
return byte.TryParse(lastHex, NumberStyles.HexNumber, null, out _);
case 4:
return ushort.TryParse(lastHex, NumberStyles.HexNumber, null, out _);
case 8:
return uint.TryParse(lastHex, NumberStyles.HexNumber, null, out _);
default:
return false;
}
return (text.Length % 2 == 0) && text.All(Uri.IsHexDigit);
}
[PublicAPI]

View file

@ -591,14 +591,11 @@ 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));