Files
ArchiSteamFarm/ArchiSteamFarm/Steam/Storage/BotConfig.cs

574 lines
23 KiB
C#
Raw Normal View History

2019-02-16 17:34:17 +01:00
// _ _ _ ____ _ _____
2017-11-18 17:27:06 +01:00
// / \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
// / _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
2019-01-14 19:11:17 +01:00
// |
// Copyright 2015-2021 Łukasz "JustArchi" Domeradzki
2018-07-27 04:52:14 +02:00
// Contact: JustArchi@JustArchi.net
2019-01-14 19:11:17 +01:00
// |
2018-07-27 04:52:14 +02:00
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
2019-01-14 19:11:17 +01:00
// |
2018-07-27 04:52:14 +02:00
// http://www.apache.org/licenses/LICENSE-2.0
2019-01-14 19:11:17 +01:00
// |
2018-07-27 04:52:14 +02:00
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
2016-03-06 02:20:41 +01:00
#if NETFRAMEWORK
using ArchiSteamFarm.Compatibility;
using File = System.IO.File;
#else
using System.IO;
#endif
2016-03-06 02:20:41 +01:00
using System;
using System.Collections.Generic;
2018-08-06 01:21:36 +02:00
using System.Collections.Immutable;
2019-10-13 17:21:40 +02:00
using System.Diagnostics.CodeAnalysis;
2020-11-14 22:37:00 +01:00
using System.Globalization;
2018-08-06 01:21:36 +02:00
using System.Linq;
2017-12-06 08:14:12 +01:00
using System.Threading.Tasks;
using ArchiSteamFarm.Core;
using ArchiSteamFarm.Helpers;
2017-01-06 16:29:34 +01:00
using ArchiSteamFarm.Localization;
using ArchiSteamFarm.Steam.Data;
using ArchiSteamFarm.Steam.Integration;
using JetBrains.Annotations;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
2018-06-13 00:48:33 +02:00
using SteamKit2;
2016-03-06 02:20:41 +01:00
namespace ArchiSteamFarm.Steam.Storage {
2019-10-13 17:21:40 +02:00
[SuppressMessage("ReSharper", "ClassCannotBeInstantiated")]
2018-10-06 05:06:29 +02:00
public sealed class BotConfig {
[PublicAPI]
public const bool DefaultAcceptGifts = false;
[PublicAPI]
public const bool DefaultAutoSteamSaleEvent = false;
[PublicAPI]
public const EBotBehaviour DefaultBotBehaviour = EBotBehaviour.None;
[PublicAPI]
2021-04-11 00:33:32 +02:00
public const string? DefaultCustomGamePlayedWhileFarming = null;
[PublicAPI]
2021-04-11 00:33:32 +02:00
public const string? DefaultCustomGamePlayedWhileIdle = null;
[PublicAPI]
public const bool DefaultEnabled = false;
[PublicAPI]
public const bool DefaultFarmNonRefundableGamesOnly = false;
[PublicAPI]
public const bool DefaultFarmPriorityQueueOnly = false;
[PublicAPI]
public const byte DefaultHoursUntilCardDrops = 3;
[PublicAPI]
public const EPersonaState DefaultOnlineStatus = EPersonaState.Online;
[PublicAPI]
public const ArchiCryptoHelper.ECryptoMethod DefaultPasswordFormat = ArchiCryptoHelper.ECryptoMethod.PlainText;
[PublicAPI]
public const bool DefaultPaused = false;
[PublicAPI]
public const ERedeemingPreferences DefaultRedeemingPreferences = ERedeemingPreferences.None;
[PublicAPI]
public const bool DefaultSendOnFarmingFinished = false;
[PublicAPI]
public const byte DefaultSendTradePeriod = 0;
[PublicAPI]
public const bool DefaultShutdownOnFarmingFinished = false;
[PublicAPI]
2021-04-11 00:33:32 +02:00
public const string? DefaultSteamLogin = null;
[PublicAPI]
public const ulong DefaultSteamMasterClanID = 0;
[PublicAPI]
2021-04-11 00:33:32 +02:00
public const string? DefaultSteamParentalCode = null;
[PublicAPI]
2021-04-11 00:33:32 +02:00
public const string? DefaultSteamPassword = null;
[PublicAPI]
2021-04-11 00:33:32 +02:00
public const string? DefaultSteamTradeToken = null;
[PublicAPI]
public const ETradingPreferences DefaultTradingPreferences = ETradingPreferences.None;
[PublicAPI]
public const bool DefaultUseLoginKeys = true;
2021-03-27 15:40:37 +01:00
[PublicAPI]
public const ArchiHandler.EUserInterfaceMode DefaultUserInterfaceMode = ArchiHandler.EUserInterfaceMode.Default;
2019-07-20 02:56:14 +02:00
internal const byte SteamParentalCodeLength = 4;
private const byte SteamTradeTokenLength = 8;
2018-08-06 01:21:36 +02:00
Implement #852 (#2004) * Update Actions.cs, Bot.cs, and BotConfig.cs * First round of refactoring * Check all badge pages * Update Bot.cs * Make sure multiple pages of badges work with foil badges and Make item selection algorithm work with all kinds of amount values we could get from Valve * Change order of params in Actions.SendInventory(...), Cache amount of cards for game ids, Count card elements on page when fetching card count per game, Calculate items to send for all games in parallel * Add unit tests * Make sure only one real app id and one asset type are present while computing cards to send and Test it * Update ArchiWebHandler.cs * Update Bot.cs * Fix iteration over badge pages * Update Bot.cs * Make stackable item stacks smaller if possible * Simplify code based on changing stack size of stackable items and Adapt tests * Consider only cards of the same rarity to be of one set and Add handling of already crafted level 5 badges * Implement feedback * Update Bot.cs * Update Bot.cs * Implement feedback from review * Adapt tests * Improve XPath efficiency * Check real result for additional values in unit tests * Implement feedback * Add additional test combining classID, type and rarity * Make collections readonly wherever you can * Optimize misc. code and Add SetTypesToComplete to BotConfig * Throw exception if we have more card types than cards per set * Remove SendSetsOnCompleted and Make CompleteTypesToSend empty per default * Fix existing unit tests and add new ones due to new exception * Please nitpicky Archi * Update Bot.cs * Change expected exception type * Make appID constants local * Update Bot.cs and BotConfig.cs * Do as JetBrains Rider says * Only fetch card count for badge if we have cards for it * Improve naming and fix handling of URIs for foil badges * Add Bot.LoadCardsPerSet(...), Bot.GetItemsForFullSets(...) and Trading.GetInventorySets(...) to public API * Let AWH do its job * Make Bot.GetPossiblyCompletedBadgeAppIDs() part of public API as well
2020-10-21 18:41:20 +02:00
[PublicAPI]
2021-05-06 20:16:06 +02:00
public static readonly ImmutableHashSet<Asset.EType> AllowedCompleteTypesToSend = ImmutableHashSet.Create(Asset.EType.TradingCard, Asset.EType.FoilTradingCard);
Implement #852 (#2004) * Update Actions.cs, Bot.cs, and BotConfig.cs * First round of refactoring * Check all badge pages * Update Bot.cs * Make sure multiple pages of badges work with foil badges and Make item selection algorithm work with all kinds of amount values we could get from Valve * Change order of params in Actions.SendInventory(...), Cache amount of cards for game ids, Count card elements on page when fetching card count per game, Calculate items to send for all games in parallel * Add unit tests * Make sure only one real app id and one asset type are present while computing cards to send and Test it * Update ArchiWebHandler.cs * Update Bot.cs * Fix iteration over badge pages * Update Bot.cs * Make stackable item stacks smaller if possible * Simplify code based on changing stack size of stackable items and Adapt tests * Consider only cards of the same rarity to be of one set and Add handling of already crafted level 5 badges * Implement feedback * Update Bot.cs * Update Bot.cs * Implement feedback from review * Adapt tests * Improve XPath efficiency * Check real result for additional values in unit tests * Implement feedback * Add additional test combining classID, type and rarity * Make collections readonly wherever you can * Optimize misc. code and Add SetTypesToComplete to BotConfig * Throw exception if we have more card types than cards per set * Remove SendSetsOnCompleted and Make CompleteTypesToSend empty per default * Fix existing unit tests and add new ones due to new exception * Please nitpicky Archi * Update Bot.cs * Change expected exception type * Make appID constants local * Update Bot.cs and BotConfig.cs * Do as JetBrains Rider says * Only fetch card count for badge if we have cards for it * Improve naming and fix handling of URIs for foil badges * Add Bot.LoadCardsPerSet(...), Bot.GetItemsForFullSets(...) and Trading.GetInventorySets(...) to public API * Let AWH do its job * Make Bot.GetPossiblyCompletedBadgeAppIDs() part of public API as well
2020-10-21 18:41:20 +02:00
2020-10-21 18:48:13 +02:00
[PublicAPI]
2021-05-06 20:16:06 +02:00
public static readonly ImmutableHashSet<Asset.EType> DefaultCompleteTypesToSend = ImmutableHashSet<Asset.EType>.Empty;
2020-10-21 18:48:13 +02:00
[PublicAPI]
public static readonly ImmutableList<EFarmingOrder> DefaultFarmingOrders = ImmutableList<EFarmingOrder>.Empty;
[PublicAPI]
public static readonly ImmutableHashSet<uint> DefaultGamesPlayedWhileIdle = ImmutableHashSet<uint>.Empty;
[PublicAPI]
2021-05-06 20:16:06 +02:00
public static readonly ImmutableHashSet<Asset.EType> DefaultLootableTypes = ImmutableHashSet.Create(Asset.EType.BoosterPack, Asset.EType.FoilTradingCard, Asset.EType.TradingCard);
[PublicAPI]
2021-05-06 20:16:06 +02:00
public static readonly ImmutableHashSet<Asset.EType> DefaultMatchableTypes = ImmutableHashSet.Create(Asset.EType.TradingCard);
[PublicAPI]
2020-11-14 22:37:00 +01:00
public static readonly ImmutableDictionary<ulong, EAccess> DefaultSteamUserPermissions = ImmutableDictionary<ulong, EAccess>.Empty;
[PublicAPI]
2021-05-06 20:16:06 +02:00
public static readonly ImmutableHashSet<Asset.EType> DefaultTransferableTypes = ImmutableHashSet.Create(Asset.EType.BoosterPack, Asset.EType.FoilTradingCard, Asset.EType.TradingCard);
2018-08-06 01:21:36 +02:00
2016-03-06 02:20:41 +01:00
[JsonProperty(Required = Required.DisallowNull)]
2020-11-15 00:30:20 +01:00
public bool AcceptGifts { get; private set; } = DefaultAcceptGifts;
2016-03-06 02:20:41 +01:00
2017-07-23 01:34:02 +02:00
[JsonProperty(Required = Required.DisallowNull)]
2020-11-15 00:30:20 +01:00
public bool AutoSteamSaleEvent { get; private set; } = DefaultAutoSteamSaleEvent;
2017-07-23 01:34:02 +02:00
2018-07-14 16:29:33 +02:00
[JsonProperty(Required = Required.DisallowNull)]
2020-11-15 00:30:20 +01:00
public EBotBehaviour BotBehaviour { get; private set; } = DefaultBotBehaviour;
2018-07-14 16:29:33 +02:00
2020-10-21 18:48:13 +02:00
[JsonProperty(Required = Required.DisallowNull)]
2021-05-06 20:16:06 +02:00
public ImmutableHashSet<Asset.EType> CompleteTypesToSend { get; private set; } = DefaultCompleteTypesToSend;
2020-10-21 18:48:13 +02:00
2016-03-06 02:20:41 +01:00
[JsonProperty]
2020-11-15 00:30:20 +01:00
public string? CustomGamePlayedWhileFarming { get; private set; } = DefaultCustomGamePlayedWhileFarming;
2016-03-06 02:20:41 +01:00
[JsonProperty]
2020-11-15 00:30:20 +01:00
public string? CustomGamePlayedWhileIdle { get; private set; } = DefaultCustomGamePlayedWhileIdle;
2016-03-06 02:20:41 +01:00
[JsonProperty(Required = Required.DisallowNull)]
2020-11-15 00:30:20 +01:00
public bool Enabled { get; private set; } = DefaultEnabled;
2016-03-06 02:20:41 +01:00
2020-09-08 20:17:27 +02:00
[JsonProperty(Required = Required.DisallowNull)]
2020-11-15 00:30:20 +01:00
public ImmutableList<EFarmingOrder> FarmingOrders { get; private set; } = DefaultFarmingOrders;
2016-03-06 02:20:41 +01:00
2020-09-08 20:17:27 +02:00
[JsonProperty(Required = Required.DisallowNull)]
public bool FarmNonRefundableGamesOnly { get; private set; } = DefaultFarmNonRefundableGamesOnly;
2016-03-06 02:20:41 +01:00
[JsonProperty(Required = Required.DisallowNull)]
public bool FarmPriorityQueueOnly { get; private set; } = DefaultFarmPriorityQueueOnly;
2017-07-23 10:27:20 +02:00
[JsonProperty(Required = Required.DisallowNull)]
public ImmutableHashSet<uint> GamesPlayedWhileIdle { get; private set; } = DefaultGamesPlayedWhileIdle;
2018-02-28 02:14:29 +01:00
[JsonProperty(Required = Required.DisallowNull)]
public byte HoursUntilCardDrops { get; private set; } = DefaultHoursUntilCardDrops;
2017-07-23 10:27:20 +02:00
2020-09-08 20:17:27 +02:00
[JsonProperty(Required = Required.DisallowNull)]
2021-05-06 20:16:06 +02:00
public ImmutableHashSet<Asset.EType> LootableTypes { get; private set; } = DefaultLootableTypes;
2016-12-25 05:52:17 +01:00
2020-09-08 20:17:27 +02:00
[JsonProperty(Required = Required.DisallowNull)]
2021-05-06 20:16:06 +02:00
public ImmutableHashSet<Asset.EType> MatchableTypes { get; private set; } = DefaultMatchableTypes;
2018-07-04 19:13:51 +02:00
[JsonProperty(Required = Required.DisallowNull)]
2020-11-15 00:30:20 +01:00
public EPersonaState OnlineStatus { get; private set; } = DefaultOnlineStatus;
2018-07-04 19:13:51 +02:00
2016-06-04 22:02:38 +02:00
[JsonProperty(Required = Required.DisallowNull)]
2020-11-15 00:30:20 +01:00
public ArchiCryptoHelper.ECryptoMethod PasswordFormat { get; private set; } = DefaultPasswordFormat;
2016-06-04 22:02:38 +02:00
2016-03-06 02:20:41 +01:00
[JsonProperty(Required = Required.DisallowNull)]
2020-11-15 00:30:20 +01:00
public bool Paused { get; private set; } = DefaultPaused;
2016-03-06 02:20:41 +01:00
[JsonProperty(Required = Required.DisallowNull)]
2020-11-15 00:30:20 +01:00
public ERedeemingPreferences RedeemingPreferences { get; private set; } = DefaultRedeemingPreferences;
2016-03-06 02:20:41 +01:00
[JsonProperty(Required = Required.DisallowNull)]
2020-11-15 00:30:20 +01:00
public bool SendOnFarmingFinished { get; private set; } = DefaultSendOnFarmingFinished;
2016-03-06 02:20:41 +01:00
[JsonProperty(Required = Required.DisallowNull)]
2020-11-15 00:30:20 +01:00
public byte SendTradePeriod { get; private set; } = DefaultSendTradePeriod;
2016-03-06 02:20:41 +01:00
[JsonProperty(Required = Required.DisallowNull)]
2020-11-15 00:30:20 +01:00
public bool ShutdownOnFarmingFinished { get; private set; } = DefaultShutdownOnFarmingFinished;
2016-03-06 02:20:41 +01:00
2020-12-18 22:06:15 +01:00
[JsonProperty]
public string? SteamLogin {
2021-05-06 21:18:39 +02:00
get => BackingSteamLogin;
2020-12-18 22:06:15 +01:00
set {
IsSteamLoginSet = true;
BackingSteamLogin = value;
}
}
2020-11-29 16:15:20 +01:00
[JsonProperty(Required = Required.DisallowNull)]
public ulong SteamMasterClanID { get; private set; } = DefaultSteamMasterClanID;
2020-12-18 22:06:15 +01:00
[JsonProperty]
public string? SteamParentalCode {
2021-05-06 21:18:39 +02:00
get => BackingSteamParentalCode;
2020-12-18 22:06:15 +01:00
set {
IsSteamParentalCodeSet = true;
BackingSteamParentalCode = value;
}
}
[JsonProperty]
public string? SteamPassword {
2021-05-06 21:18:39 +02:00
get => BackingSteamPassword;
2020-12-18 22:06:15 +01:00
set {
IsSteamPasswordSet = true;
BackingSteamPassword = value;
}
}
[JsonProperty]
2020-11-15 00:30:20 +01:00
public string? SteamTradeToken { get; private set; } = DefaultSteamTradeToken;
2020-09-08 20:17:27 +02:00
[JsonProperty(Required = Required.DisallowNull)]
2020-11-15 00:30:20 +01:00
public ImmutableDictionary<ulong, EAccess> SteamUserPermissions { get; private set; } = DefaultSteamUserPermissions;
[JsonProperty(Required = Required.DisallowNull)]
2020-11-15 00:30:20 +01:00
public ETradingPreferences TradingPreferences { get; private set; } = DefaultTradingPreferences;
2016-03-10 21:17:48 +01:00
2020-09-08 20:17:27 +02:00
[JsonProperty(Required = Required.DisallowNull)]
2021-05-06 20:16:06 +02:00
public ImmutableHashSet<Asset.EType> TransferableTypes { get; private set; } = DefaultTransferableTypes;
2017-11-28 21:31:45 +01:00
[JsonProperty(Required = Required.DisallowNull)]
2020-11-15 00:30:20 +01:00
public bool UseLoginKeys { get; private set; } = DefaultUseLoginKeys;
2018-08-06 01:21:36 +02:00
2021-03-27 15:40:37 +01:00
[JsonProperty(Required = Required.DisallowNull)]
public ArchiHandler.EUserInterfaceMode UserInterfaceMode { get; private set; } = DefaultUserInterfaceMode;
[JsonExtensionData]
internal Dictionary<string, JToken>? AdditionalProperties {
2020-03-06 11:30:53 +01:00
get;
[UsedImplicitly]
set;
}
internal string? DecryptedSteamPassword {
2018-09-18 19:58:17 +02:00
get {
if (string.IsNullOrEmpty(SteamPassword)) {
return null;
}
2017-07-01 13:39:53 +02:00
2018-09-18 19:58:17 +02:00
if (PasswordFormat == ArchiCryptoHelper.ECryptoMethod.PlainText) {
return SteamPassword;
}
2016-03-08 03:18:50 +01:00
string? result = ArchiCryptoHelper.Decrypt(PasswordFormat, SteamPassword!);
2018-12-15 00:27:15 +01:00
2019-03-23 16:16:57 +01:00
if (string.IsNullOrEmpty(result)) {
2020-11-14 22:37:00 +01:00
ASF.ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsInvalid, nameof(SteamPassword)));
2018-12-15 00:27:15 +01:00
return null;
}
2019-03-23 16:16:57 +01:00
return result;
}
set {
2019-03-23 16:16:57 +01:00
if (!string.IsNullOrEmpty(value) && (PasswordFormat != ArchiCryptoHelper.ECryptoMethod.PlainText)) {
value = ArchiCryptoHelper.Encrypt(PasswordFormat, value!);
}
2019-03-23 16:16:57 +01:00
SteamPassword = value;
}
}
internal bool IsSteamLoginSet { get; private set; }
internal bool IsSteamParentalCodeSet { get; private set; }
internal bool IsSteamPasswordSet { get; private set; }
2021-05-08 22:19:27 +02:00
internal bool Saving { get; set; }
2018-09-18 19:58:17 +02:00
private string? BackingSteamLogin = DefaultSteamLogin;
private string? BackingSteamParentalCode = DefaultSteamParentalCode;
private string? BackingSteamPassword = DefaultSteamPassword;
2018-09-18 19:58:17 +02:00
[Obsolete]
[JsonProperty(Required = Required.DisallowNull)]
private bool IdlePriorityQueueOnly {
set {
ASF.ArchiLogger.LogGenericWarning(string.Format(CultureInfo.CurrentCulture, Strings.WarningDeprecated, nameof(IdlePriorityQueueOnly), nameof(FarmPriorityQueueOnly)));
FarmPriorityQueueOnly = value;
}
}
[Obsolete]
[JsonProperty(Required = Required.DisallowNull)]
private bool IdleRefundableGames {
set {
ASF.ArchiLogger.LogGenericWarning(string.Format(CultureInfo.CurrentCulture, Strings.WarningDeprecated, nameof(IdleRefundableGames), nameof(FarmNonRefundableGamesOnly)));
FarmNonRefundableGamesOnly = !value;
}
}
2018-02-11 11:11:38 +01:00
[JsonProperty(PropertyName = SharedInfo.UlongCompatibilityStringPrefix + nameof(SteamMasterClanID), Required = Required.DisallowNull)]
2018-02-11 09:58:25 +01:00
private string SSteamMasterClanID {
2020-11-14 22:37:00 +01:00
get => SteamMasterClanID.ToString(CultureInfo.InvariantCulture);
2018-12-15 00:27:15 +01:00
2018-02-11 09:58:25 +01:00
set {
if (string.IsNullOrEmpty(value) || !ulong.TryParse(value, out ulong result)) {
2020-11-14 22:37:00 +01:00
ASF.ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsInvalid, nameof(SSteamMasterClanID)));
2018-12-15 00:27:15 +01:00
2018-02-11 09:58:25 +01:00
return;
}
SteamMasterClanID = result;
}
}
2019-01-14 21:50:23 +01:00
[JsonConstructor]
2020-12-05 20:27:10 +01:00
internal BotConfig() { }
2019-01-14 21:50:23 +01:00
2020-12-18 22:06:15 +01:00
[PublicAPI]
public static async Task<bool> Write(string filePath, BotConfig botConfig) {
if (string.IsNullOrEmpty(filePath)) {
throw new ArgumentNullException(nameof(filePath));
}
if (botConfig == null) {
throw new ArgumentNullException(nameof(botConfig));
}
string json = JsonConvert.SerializeObject(botConfig, Formatting.Indented);
return await SerializableFile.Write(filePath, json).ConfigureAwait(false);
2020-12-18 22:06:15 +01:00
}
internal (bool Valid, string? ErrorMessage) CheckValidation() {
if (BotBehaviour > EBotBehaviour.All) {
2020-11-14 22:37:00 +01:00
return (false, string.Format(CultureInfo.CurrentCulture, Strings.ErrorConfigPropertyInvalid, nameof(BotBehaviour), BotBehaviour));
}
2019-08-10 17:38:49 +02:00
foreach (EFarmingOrder farmingOrder in FarmingOrders.Where(farmingOrder => !Enum.IsDefined(typeof(EFarmingOrder), farmingOrder))) {
2020-11-14 22:37:00 +01:00
return (false, string.Format(CultureInfo.CurrentCulture, Strings.ErrorConfigPropertyInvalid, nameof(FarmingOrders), farmingOrder));
}
if (GamesPlayedWhileIdle.Count > ArchiHandler.MaxGamesPlayedConcurrently) {
2020-11-14 22:37:00 +01:00
return (false, string.Format(CultureInfo.CurrentCulture, Strings.ErrorConfigPropertyInvalid, nameof(GamesPlayedWhileIdle), GamesPlayedWhileIdle.Count + " > " + ArchiHandler.MaxGamesPlayedConcurrently));
}
2021-05-06 20:16:06 +02:00
foreach (Asset.EType lootableType in LootableTypes.Where(lootableType => !Enum.IsDefined(typeof(Asset.EType), lootableType))) {
2020-11-14 22:37:00 +01:00
return (false, string.Format(CultureInfo.CurrentCulture, Strings.ErrorConfigPropertyInvalid, nameof(LootableTypes), lootableType));
}
2021-05-06 20:16:06 +02:00
foreach (Asset.EType completableType in CompleteTypesToSend.Where(completableType => !Enum.IsDefined(typeof(Asset.EType), completableType) || !AllowedCompleteTypesToSend.Contains(completableType))) {
2020-11-14 22:37:00 +01:00
return (false, string.Format(CultureInfo.CurrentCulture, Strings.ErrorConfigPropertyInvalid, nameof(CompleteTypesToSend), completableType));
Implement #852 (#2004) * Update Actions.cs, Bot.cs, and BotConfig.cs * First round of refactoring * Check all badge pages * Update Bot.cs * Make sure multiple pages of badges work with foil badges and Make item selection algorithm work with all kinds of amount values we could get from Valve * Change order of params in Actions.SendInventory(...), Cache amount of cards for game ids, Count card elements on page when fetching card count per game, Calculate items to send for all games in parallel * Add unit tests * Make sure only one real app id and one asset type are present while computing cards to send and Test it * Update ArchiWebHandler.cs * Update Bot.cs * Fix iteration over badge pages * Update Bot.cs * Make stackable item stacks smaller if possible * Simplify code based on changing stack size of stackable items and Adapt tests * Consider only cards of the same rarity to be of one set and Add handling of already crafted level 5 badges * Implement feedback * Update Bot.cs * Update Bot.cs * Implement feedback from review * Adapt tests * Improve XPath efficiency * Check real result for additional values in unit tests * Implement feedback * Add additional test combining classID, type and rarity * Make collections readonly wherever you can * Optimize misc. code and Add SetTypesToComplete to BotConfig * Throw exception if we have more card types than cards per set * Remove SendSetsOnCompleted and Make CompleteTypesToSend empty per default * Fix existing unit tests and add new ones due to new exception * Please nitpicky Archi * Update Bot.cs * Change expected exception type * Make appID constants local * Update Bot.cs and BotConfig.cs * Do as JetBrains Rider says * Only fetch card count for badge if we have cards for it * Improve naming and fix handling of URIs for foil badges * Add Bot.LoadCardsPerSet(...), Bot.GetItemsForFullSets(...) and Trading.GetInventorySets(...) to public API * Let AWH do its job * Make Bot.GetPossiblyCompletedBadgeAppIDs() part of public API as well
2020-10-21 18:41:20 +02:00
}
2021-05-06 20:16:06 +02:00
foreach (Asset.EType matchableType in MatchableTypes.Where(matchableType => !Enum.IsDefined(typeof(Asset.EType), matchableType))) {
2020-11-14 22:37:00 +01:00
return (false, string.Format(CultureInfo.CurrentCulture, Strings.ErrorConfigPropertyInvalid, nameof(MatchableTypes), matchableType));
}
if (!Enum.IsDefined(typeof(EPersonaState), OnlineStatus)) {
2020-11-14 22:37:00 +01:00
return (false, string.Format(CultureInfo.CurrentCulture, Strings.ErrorConfigPropertyInvalid, nameof(OnlineStatus), OnlineStatus));
}
if (!Enum.IsDefined(typeof(ArchiCryptoHelper.ECryptoMethod), PasswordFormat)) {
2020-11-14 22:37:00 +01:00
return (false, string.Format(CultureInfo.CurrentCulture, Strings.ErrorConfigPropertyInvalid, nameof(PasswordFormat), PasswordFormat));
}
if (RedeemingPreferences > ERedeemingPreferences.All) {
2020-11-14 22:37:00 +01:00
return (false, string.Format(CultureInfo.CurrentCulture, Strings.ErrorConfigPropertyInvalid, nameof(RedeemingPreferences), RedeemingPreferences));
}
if ((SteamMasterClanID != 0) && !new SteamID(SteamMasterClanID).IsClanAccount) {
2020-11-14 22:37:00 +01:00
return (false, string.Format(CultureInfo.CurrentCulture, Strings.ErrorConfigPropertyInvalid, nameof(SteamMasterClanID), SteamMasterClanID));
}
if (!string.IsNullOrEmpty(SteamParentalCode) && (SteamParentalCode != "0") && (SteamParentalCode!.Length != SteamParentalCodeLength)) {
2020-11-14 22:37:00 +01:00
return (false, string.Format(CultureInfo.CurrentCulture, Strings.ErrorConfigPropertyInvalid, nameof(SteamParentalCode), SteamParentalCode));
}
if (!string.IsNullOrEmpty(SteamTradeToken) && (SteamTradeToken!.Length != SteamTradeTokenLength)) {
2020-11-14 22:37:00 +01:00
return (false, string.Format(CultureInfo.CurrentCulture, Strings.ErrorConfigPropertyInvalid, nameof(SteamTradeToken), SteamTradeToken));
}
2020-11-14 22:37:00 +01:00
foreach ((ulong steamID, EAccess permission) in SteamUserPermissions) {
2019-02-01 22:41:25 +01:00
if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) {
2020-11-14 22:37:00 +01:00
return (false, string.Format(CultureInfo.CurrentCulture, Strings.ErrorConfigPropertyInvalid, nameof(SteamUserPermissions), steamID));
2019-02-01 22:41:25 +01:00
}
2020-11-14 22:37:00 +01:00
if (!Enum.IsDefined(typeof(EAccess), permission)) {
return (false, string.Format(CultureInfo.CurrentCulture, Strings.ErrorConfigPropertyInvalid, nameof(SteamUserPermissions), permission));
2019-02-01 22:41:25 +01:00
}
}
2021-03-27 15:40:37 +01:00
if (TradingPreferences > ETradingPreferences.All) {
return (false, string.Format(CultureInfo.CurrentCulture, Strings.ErrorConfigPropertyInvalid, nameof(TradingPreferences), TradingPreferences));
}
return !Enum.IsDefined(typeof(ArchiHandler.EUserInterfaceMode), UserInterfaceMode) ? (false, string.Format(CultureInfo.CurrentCulture, Strings.ErrorConfigPropertyInvalid, nameof(UserInterfaceMode), UserInterfaceMode)) : (true, null);
}
internal static async Task<(BotConfig? BotConfig, string? LatestJson)> Load(string filePath) {
2016-05-30 01:57:06 +02:00
if (string.IsNullOrEmpty(filePath)) {
throw new ArgumentNullException(nameof(filePath));
2016-05-30 01:57:06 +02:00
}
if (!File.Exists(filePath)) {
return (null, null);
2016-03-06 02:20:41 +01:00
}
string json;
2020-12-25 23:12:42 +01:00
BotConfig? botConfig;
2016-05-30 01:57:06 +02:00
2016-03-06 02:20:41 +01:00
try {
json = await Compatibility.File.ReadAllTextAsync(filePath).ConfigureAwait(false);
2019-04-04 22:34:58 +02:00
if (string.IsNullOrEmpty(json)) {
2020-11-14 22:37:00 +01:00
ASF.ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsEmpty, nameof(json)));
2019-04-04 22:34:58 +02:00
return (null, null);
2019-04-04 22:34:58 +02:00
}
botConfig = JsonConvert.DeserializeObject<BotConfig>(json);
2016-03-06 02:20:41 +01:00
} catch (Exception e) {
ASF.ArchiLogger.LogGenericException(e);
2018-12-15 00:27:15 +01:00
return (null, null);
2016-03-06 02:20:41 +01:00
}
2016-07-31 17:38:14 +02:00
if (botConfig == null) {
ASF.ArchiLogger.LogNullError(nameof(botConfig));
2018-12-15 00:27:15 +01:00
return (null, null);
2016-07-31 17:38:14 +02:00
}
(bool valid, string? errorMessage) = botConfig.CheckValidation();
2018-12-15 00:27:15 +01:00
if (!valid) {
if (!string.IsNullOrEmpty(errorMessage)) {
ASF.ArchiLogger.LogGenericError(errorMessage!);
}
2018-12-15 00:27:15 +01:00
return (null, null);
2018-07-04 19:13:51 +02:00
}
2021-05-08 22:19:27 +02:00
botConfig.Saving = true;
string latestJson = JsonConvert.SerializeObject(botConfig, Formatting.Indented);
2021-05-08 22:19:27 +02:00
botConfig.Saving = false;
return (botConfig, json != latestJson ? latestJson : null);
2016-03-06 02:20:41 +01:00
}
2020-11-14 22:37:00 +01:00
public enum EAccess : byte {
None,
FamilySharing,
Operator,
Master
}
2018-04-23 23:51:34 +02:00
[Flags]
2019-01-14 21:50:23 +01:00
public enum EBotBehaviour : byte {
2018-04-23 23:51:34 +02:00
None = 0,
RejectInvalidFriendInvites = 1,
RejectInvalidTrades = 2,
RejectInvalidGroupInvites = 4,
2018-07-04 19:13:51 +02:00
DismissInventoryNotifications = 8,
MarkReceivedMessagesAsRead = 16,
MarkBotMessagesAsRead = 32,
2019-11-06 20:25:32 +01:00
All = RejectInvalidFriendInvites | RejectInvalidTrades | RejectInvalidGroupInvites | DismissInventoryNotifications | MarkReceivedMessagesAsRead | MarkBotMessagesAsRead
2018-04-23 23:51:34 +02:00
}
2019-01-14 21:50:23 +01:00
public enum EFarmingOrder : byte {
Unordered,
AppIDsAscending,
AppIDsDescending,
CardDropsAscending,
CardDropsDescending,
HoursAscending,
HoursDescending,
NamesAscending,
2017-07-02 10:00:02 +02:00
NamesDescending,
2017-07-23 05:06:11 +02:00
Random,
BadgeLevelsAscending,
BadgeLevelsDescending,
RedeemDateTimesAscending,
2018-07-04 19:13:51 +02:00
RedeemDateTimesDescending,
MarketableAscending,
MarketableDescending
}
[Flags]
2019-01-14 21:50:23 +01:00
public enum ERedeemingPreferences : byte {
None = 0,
Forwarding = 1,
2017-02-18 01:03:13 +01:00
Distributing = 2,
2018-07-04 19:13:51 +02:00
KeepMissingGames = 4,
AssumeWalletKeyOnBadActivationCode = 8,
All = Forwarding | Distributing | KeepMissingGames | AssumeWalletKeyOnBadActivationCode
}
[Flags]
2019-01-14 21:50:23 +01:00
public enum ETradingPreferences : byte {
None = 0,
AcceptDonations = 1,
SteamTradeMatcher = 2,
2017-02-05 07:51:04 +01:00
MatchEverything = 4,
2018-07-04 19:13:51 +02:00
DontAcceptBotTrades = 8,
Implement ETradingPreferences.MatchActively This will probably need a lot more tests, tweaking and bugfixing, but basic logic is: - MatchActively added to TradingPreferences with value of 16 - User must also use SteamTradeMatcher, can't use MatchEverything - User must have statistics enabled and be eligible for being listed (no requirement of having 100 items minimum) Once all requirements are passed, statistics module will communicate with the listing and fetch match everything bots: - The matching will start in 1h since ASF start and will repeat every day (right now it starts in 1 minute to aid debugging). - Each matching is composed of up to 10 rounds maximum. - In each round ASF will fetch our inventory and inventory of listed bots in order to find MatchableTypes items to be matched. If match is found, offer is being sent and confirmed automatically. - Each set (composition of item type + appID it's from) can be matched in a single round only once, this is to minimize "items no longer available" as much as possible and also avoid a need to wait for each bot to react before sending all trades. - Round ends when we try to match a total of 20 bots, or we hit no items to match in consecutive 10 tries with 10 different bots. - If last round resulted in at least a single trade being sent, next round starts within 5 minutes since last one, otherwise matching ends and repeats the next day. We'll see how it works in practice, expect a lot of follow-up commits, unless I won't have anything to fix or improve.
2018-11-29 18:35:58 +01:00
MatchActively = 16,
All = AcceptDonations | SteamTradeMatcher | MatchEverything | DontAcceptBotTrades | MatchActively
}
2018-08-06 01:21:36 +02:00
// ReSharper disable UnusedMember.Global
2021-05-08 22:19:27 +02:00
public bool ShouldSerializeAcceptGifts() => !Saving || (AcceptGifts != DefaultAcceptGifts);
public bool ShouldSerializeAutoSteamSaleEvent() => !Saving || (AutoSteamSaleEvent != DefaultAutoSteamSaleEvent);
public bool ShouldSerializeBotBehaviour() => !Saving || (BotBehaviour != DefaultBotBehaviour);
public bool ShouldSerializeCompleteTypesToSend() => !Saving || ((CompleteTypesToSend != DefaultCompleteTypesToSend) && !CompleteTypesToSend.SetEquals(DefaultCompleteTypesToSend));
public bool ShouldSerializeCustomGamePlayedWhileFarming() => !Saving || (CustomGamePlayedWhileFarming != DefaultCustomGamePlayedWhileFarming);
public bool ShouldSerializeCustomGamePlayedWhileIdle() => !Saving || (CustomGamePlayedWhileIdle != DefaultCustomGamePlayedWhileIdle);
public bool ShouldSerializeEnabled() => !Saving || (Enabled != DefaultEnabled);
public bool ShouldSerializeFarmingOrders() => !Saving || ((FarmingOrders != DefaultFarmingOrders) && !FarmingOrders.SequenceEqual(DefaultFarmingOrders));
public bool ShouldSerializeFarmNonRefundableGamesOnly() => !Saving || (FarmNonRefundableGamesOnly != DefaultFarmNonRefundableGamesOnly);
public bool ShouldSerializeFarmPriorityQueueOnly() => !Saving || (FarmPriorityQueueOnly != DefaultFarmPriorityQueueOnly);
public bool ShouldSerializeGamesPlayedWhileIdle() => !Saving || ((GamesPlayedWhileIdle != DefaultGamesPlayedWhileIdle) && !GamesPlayedWhileIdle.SetEquals(DefaultGamesPlayedWhileIdle));
public bool ShouldSerializeHoursUntilCardDrops() => !Saving || (HoursUntilCardDrops != DefaultHoursUntilCardDrops);
public bool ShouldSerializeLootableTypes() => !Saving || ((LootableTypes != DefaultLootableTypes) && !LootableTypes.SetEquals(DefaultLootableTypes));
public bool ShouldSerializeMatchableTypes() => !Saving || ((MatchableTypes != DefaultMatchableTypes) && !MatchableTypes.SetEquals(DefaultMatchableTypes));
public bool ShouldSerializeOnlineStatus() => !Saving || (OnlineStatus != DefaultOnlineStatus);
public bool ShouldSerializePasswordFormat() => !Saving || (PasswordFormat != DefaultPasswordFormat);
public bool ShouldSerializePaused() => !Saving || (Paused != DefaultPaused);
public bool ShouldSerializeRedeemingPreferences() => !Saving || (RedeemingPreferences != DefaultRedeemingPreferences);
public bool ShouldSerializeSendOnFarmingFinished() => !Saving || (SendOnFarmingFinished != DefaultSendOnFarmingFinished);
public bool ShouldSerializeSendTradePeriod() => !Saving || (SendTradePeriod != DefaultSendTradePeriod);
public bool ShouldSerializeShutdownOnFarmingFinished() => !Saving || (ShutdownOnFarmingFinished != DefaultShutdownOnFarmingFinished);
public bool ShouldSerializeSSteamMasterClanID() => !Saving;
public bool ShouldSerializeSteamLogin() => Saving && (SteamLogin != DefaultSteamLogin);
public bool ShouldSerializeSteamMasterClanID() => !Saving || (SteamMasterClanID != DefaultSteamMasterClanID);
public bool ShouldSerializeSteamParentalCode() => Saving && (SteamParentalCode != DefaultSteamParentalCode);
public bool ShouldSerializeSteamPassword() => Saving && (SteamPassword != DefaultSteamPassword);
public bool ShouldSerializeSteamTradeToken() => !Saving || (SteamTradeToken != DefaultSteamTradeToken);
public bool ShouldSerializeSteamUserPermissions() => !Saving || ((SteamUserPermissions != DefaultSteamUserPermissions) && ((SteamUserPermissions.Count != DefaultSteamUserPermissions.Count) || SteamUserPermissions.Except(DefaultSteamUserPermissions).Any()));
public bool ShouldSerializeTradingPreferences() => !Saving || (TradingPreferences != DefaultTradingPreferences);
public bool ShouldSerializeTransferableTypes() => !Saving || ((TransferableTypes != DefaultTransferableTypes) && !TransferableTypes.SetEquals(DefaultTransferableTypes));
public bool ShouldSerializeUseLoginKeys() => !Saving || (UseLoginKeys != DefaultUseLoginKeys);
public bool ShouldSerializeUserInterfaceMode() => !Saving || (UserInterfaceMode != DefaultUserInterfaceMode);
2018-08-06 01:21:36 +02:00
// ReSharper restore UnusedMember.Global
2016-03-06 02:20:41 +01:00
}
2018-08-06 01:21:36 +02:00
}