Compare commits

..

21 Commits

Author SHA1 Message Date
JustArchi
1ad1e8b792 Use better format for API 2016-06-24 01:55:39 +02:00
JustArchi
c3dde4c822 Final fix 2016-06-24 01:44:58 +02:00
JustArchi
b40dc2e572 Calculate proper dupe values for the same cards 2016-06-24 01:27:11 +02:00
JustArchi
70bdd34d66 Increase default LoginLimiterDelay to 10
Seems to fix semi-rare 429 rate-limiting errors. Maybe GabeN increased limits?
2016-06-23 16:00:15 +02:00
JustArchi
56a6e10189 Correct retry logic of API requests
Timeout will result in WebClient does not support concurrent I/O operations for concurrent calls
Therefore, retry with new WebClient instead
2016-06-22 02:02:43 +02:00
JustArchi
41f8a0a474 Add !api 2016-06-20 21:20:38 +02:00
JustArchi
92f347e28b Bump 2016-06-20 20:47:09 +02:00
JustArchi
1a1914540c Fix !2faok with many confirmations 2016-06-20 20:29:12 +02:00
JustArchi
449e4f9511 Bump 2016-06-20 17:04:07 +02:00
JustArchi
959bf98039 Divide !pause into !pause and !resume 2016-06-20 17:03:55 +02:00
JustArchi
017c5eb7ef Fix misc issue with steam gifts in trades 2016-06-20 15:02:26 +02:00
JustArchi
9e575584a8 Bump 2016-06-20 14:41:14 +02:00
JustArchi
19da8c6d11 Try to solve language problem in non-invasive way 2016-06-20 14:40:14 +02:00
JustArchi
3d19a69c60 Let's hope this is the last one 2016-06-20 13:51:17 +02:00
JustArchi
f6a8d16c85 More fixes 2016-06-20 13:45:12 +02:00
JustArchi
f13991c2da Bump... 2016-06-20 13:26:43 +02:00
JustArchi
40a3d6558d GabeN broke more than I thought 2016-06-20 13:26:30 +02:00
JustArchi
fea76a3dda Derp 2016-06-20 13:18:06 +02:00
JustArchi
1087c01a2c And don't you dare to break again 2016-06-20 13:15:35 +02:00
JustArchi
fd49ff5483 Thanks GabeN 2016-06-20 13:09:27 +02:00
JustArchi
0f5d9a665c Bump 2016-06-20 13:01:42 +02:00
10 changed files with 129 additions and 50 deletions

View File

@@ -47,6 +47,8 @@ namespace ArchiSteamFarm {
private readonly SemaphoreSlim SessionSemaphore = new SemaphoreSlim(1); private readonly SemaphoreSlim SessionSemaphore = new SemaphoreSlim(1);
private readonly WebBrowser WebBrowser; private readonly WebBrowser WebBrowser;
internal bool Ready { get; private set; }
private ulong SteamID; private ulong SteamID;
private DateTime LastSessionRefreshCheck = DateTime.MinValue; private DateTime LastSessionRefreshCheck = DateTime.MinValue;
@@ -67,12 +69,7 @@ namespace ArchiSteamFarm {
} }
uint appID; uint appID;
if (uint.TryParse(hashName.Substring(0, index), out appID)) { return uint.TryParse(hashName.Substring(0, index), out appID) ? appID : 0;
return appID;
}
Logging.LogNullError(nameof(appID));
return 0;
} }
private static Steam.Item.EType GetItemType(string name) { private static Steam.Item.EType GetItemType(string name) {
@@ -117,7 +114,9 @@ namespace ArchiSteamFarm {
WebBrowser = new WebBrowser(bot.BotName); WebBrowser = new WebBrowser(bot.BotName);
} }
internal bool Init(SteamClient steamClient, string webAPIUserNonce, string parentalPin) { internal void OnDisconnected() => Ready = false;
internal async Task<bool> Init(SteamClient steamClient, string webAPIUserNonce, string parentalPin) {
if ((steamClient == null) || string.IsNullOrEmpty(webAPIUserNonce)) { if ((steamClient == null) || string.IsNullOrEmpty(webAPIUserNonce)) {
Logging.LogNullError(nameof(steamClient) + " || " + nameof(webAPIUserNonce), Bot.BotName); Logging.LogNullError(nameof(steamClient) + " || " + nameof(webAPIUserNonce), Bot.BotName);
return false; return false;
@@ -179,10 +178,12 @@ namespace ArchiSteamFarm {
string steamLoginSecure = authResult["tokensecure"].Value; string steamLoginSecure = authResult["tokensecure"].Value;
WebBrowser.CookieContainer.Add(new Cookie("steamLoginSecure", steamLoginSecure, "/", "." + SteamCommunityHost)); WebBrowser.CookieContainer.Add(new Cookie("steamLoginSecure", steamLoginSecure, "/", "." + SteamCommunityHost));
if (!UnlockParentalAccount(parentalPin).Result) { // Unlock Steam Parental if needed
if (!await UnlockParentalAccount(parentalPin).ConfigureAwait(false)) {
return false; return false;
} }
Ready = true;
LastSessionRefreshCheck = DateTime.Now; LastSessionRefreshCheck = DateTime.Now;
return true; return true;
} }
@@ -246,7 +247,7 @@ namespace ArchiSteamFarm {
return null; return null;
} }
string request = SteamCommunityURL + "/mobileconf/conf?p=" + deviceID + "&a=" + SteamID + "&k=" + WebUtility.UrlEncode(confirmationHash) + "&t=" + time + "&m=android&tag=conf"; string request = SteamCommunityURL + "/mobileconf/conf?l=english&p=" + deviceID + "&a=" + SteamID + "&k=" + WebUtility.UrlEncode(confirmationHash) + "&t=" + time + "&m=android&tag=conf";
return await WebBrowser.UrlGetToHtmlDocumentRetry(request).ConfigureAwait(false); return await WebBrowser.UrlGetToHtmlDocumentRetry(request).ConfigureAwait(false);
} }
@@ -369,10 +370,10 @@ namespace ArchiSteamFarm {
} }
KeyValue response = null; KeyValue response = null;
for (byte i = 0; (i < WebBrowser.MaxRetries) && (response == null); i++) {
using (dynamic iPlayerService = WebAPI.GetInterface("IPlayerService", Bot.BotConfig.SteamApiKey)) { using (dynamic iPlayerService = WebAPI.GetInterface("IPlayerService", Bot.BotConfig.SteamApiKey)) {
iPlayerService.Timeout = Timeout; iPlayerService.Timeout = Timeout;
for (byte i = 0; (i < WebBrowser.MaxRetries) && (response == null); i++) {
try { try {
response = iPlayerService.GetOwnedGames( response = iPlayerService.GetOwnedGames(
steamid: steamID, steamid: steamID,
@@ -406,10 +407,10 @@ namespace ArchiSteamFarm {
internal uint GetServerTime() { internal uint GetServerTime() {
KeyValue response = null; KeyValue response = null;
for (byte i = 0; (i < WebBrowser.MaxRetries) && (response == null); i++) {
using (dynamic iTwoFactorService = WebAPI.GetInterface("ITwoFactorService")) { using (dynamic iTwoFactorService = WebAPI.GetInterface("ITwoFactorService")) {
iTwoFactorService.Timeout = Timeout; iTwoFactorService.Timeout = Timeout;
for (byte i = 0; (i < WebBrowser.MaxRetries) && (response == null); i++) {
try { try {
response = iTwoFactorService.QueryTime( response = iTwoFactorService.QueryTime(
method: WebRequestMethods.Http.Post, method: WebRequestMethods.Http.Post,
@@ -435,7 +436,7 @@ namespace ArchiSteamFarm {
return null; return null;
} }
string request = SteamCommunityURL + "/tradeoffer/" + tradeID; string request = SteamCommunityURL + "/tradeoffer/" + tradeID + "?l=english";
HtmlDocument htmlDocument = await WebBrowser.UrlGetToHtmlDocumentRetry(request).ConfigureAwait(false); HtmlDocument htmlDocument = await WebBrowser.UrlGetToHtmlDocumentRetry(request).ConfigureAwait(false);
if (htmlDocument == null) { if (htmlDocument == null) {
@@ -487,10 +488,10 @@ namespace ArchiSteamFarm {
} }
KeyValue response = null; KeyValue response = null;
for (byte i = 0; (i < WebBrowser.MaxRetries) && (response == null); i++) {
using (dynamic iEconService = WebAPI.GetInterface("IEconService", Bot.BotConfig.SteamApiKey)) { using (dynamic iEconService = WebAPI.GetInterface("IEconService", Bot.BotConfig.SteamApiKey)) {
iEconService.Timeout = Timeout; iEconService.Timeout = Timeout;
for (byte i = 0; (i < WebBrowser.MaxRetries) && (response == null); i++) {
try { try {
response = iEconService.GetTradeOffers( response = iEconService.GetTradeOffers(
get_received_offers: 1, get_received_offers: 1,
@@ -624,10 +625,10 @@ namespace ArchiSteamFarm {
} }
KeyValue response = null; KeyValue response = null;
for (byte i = 0; (i < WebBrowser.MaxRetries) && (response == null); i++) {
using (dynamic iEconService = WebAPI.GetInterface("IEconService", Bot.BotConfig.SteamApiKey)) { using (dynamic iEconService = WebAPI.GetInterface("IEconService", Bot.BotConfig.SteamApiKey)) {
iEconService.Timeout = Timeout; iEconService.Timeout = Timeout;
for (byte i = 0; (i < WebBrowser.MaxRetries) && (response == null); i++) {
try { try {
response = iEconService.DeclineTradeOffer( response = iEconService.DeclineTradeOffer(
tradeofferid: tradeID.ToString(), tradeofferid: tradeID.ToString(),
@@ -837,7 +838,7 @@ namespace ArchiSteamFarm {
return null; return null;
} }
string request = SteamCommunityURL + "/my/badges?p=" + page; string request = SteamCommunityURL + "/my/badges?l=english&p=" + page;
return await WebBrowser.UrlGetToHtmlDocumentRetry(request).ConfigureAwait(false); return await WebBrowser.UrlGetToHtmlDocumentRetry(request).ConfigureAwait(false);
} }
@@ -852,7 +853,7 @@ namespace ArchiSteamFarm {
return null; return null;
} }
string request = SteamCommunityURL + "/my/gamecards/" + appID; string request = SteamCommunityURL + "/my/gamecards/" + appID + "?l=english";
return await WebBrowser.UrlGetToHtmlDocumentRetry(request).ConfigureAwait(false); return await WebBrowser.UrlGetToHtmlDocumentRetry(request).ConfigureAwait(false);
} }

View File

@@ -56,14 +56,19 @@ namespace ArchiSteamFarm {
private readonly string SentryFile; private readonly string SentryFile;
private readonly BotDatabase BotDatabase; private readonly BotDatabase BotDatabase;
private readonly CallbackManager CallbackManager; private readonly CallbackManager CallbackManager;
[JsonProperty]
private readonly CardsFarmer CardsFarmer; private readonly CardsFarmer CardsFarmer;
private readonly SteamApps SteamApps; private readonly SteamApps SteamApps;
private readonly SteamFriends SteamFriends; private readonly SteamFriends SteamFriends;
private readonly SteamUser SteamUser; private readonly SteamUser SteamUser;
private readonly Timer AcceptConfirmationsTimer, SendItemsTimer; private readonly Timer AcceptConfirmationsTimer, SendItemsTimer;
private readonly Trading Trading; private readonly Trading Trading;
[JsonProperty]
internal bool KeepRunning { get; private set; } internal bool KeepRunning { get; private set; }
internal bool PlayingBlocked { get; private set; } internal bool PlayingBlocked { get; private set; }
private bool FirstTradeSent, InvalidPassword, SkipFirstShutdown; private bool FirstTradeSent, InvalidPassword, SkipFirstShutdown;
@@ -252,7 +257,7 @@ namespace ArchiSteamFarm {
} }
if (acceptedType != Steam.ConfirmationDetails.EType.Unknown) { if (acceptedType != Steam.ConfirmationDetails.EType.Unknown) {
if (confirmations.RemoveWhere(confirmation => confirmation.Type != acceptedType) > 0) { if (confirmations.RemoveWhere(confirmation => (confirmation.Type != acceptedType) && (confirmation.Type != Steam.ConfirmationDetails.EType.Other)) > 0) {
confirmations.TrimExcess(); confirmations.TrimExcess();
if (confirmations.Count == 0) { if (confirmations.Count == 0) {
return; return;
@@ -261,8 +266,8 @@ namespace ArchiSteamFarm {
} }
if ((acceptedSteamID != 0) || ((acceptedTradeIDs != null) && (acceptedTradeIDs.Count > 0))) { if ((acceptedSteamID != 0) || ((acceptedTradeIDs != null) && (acceptedTradeIDs.Count > 0))) {
List<Task<Steam.ConfirmationDetails>> detailsTasks = confirmations.Select(BotDatabase.MobileAuthenticator.GetConfirmationDetails).ToList(); List<Task<Steam.ConfirmationDetails>> tasks = confirmations.Select(BotDatabase.MobileAuthenticator.GetConfirmationDetails).ToList();
Steam.ConfirmationDetails[] detailsResults = await Task.WhenAll(detailsTasks).ConfigureAwait(false); Steam.ConfirmationDetails[] detailsResults = await Task.WhenAll(tasks).ConfigureAwait(false);
HashSet<uint> ignoredConfirmationIDs = new HashSet<uint>(); HashSet<uint> ignoredConfirmationIDs = new HashSet<uint>();
foreach (Steam.ConfirmationDetails details in detailsResults.Where(details => (details != null) && ( foreach (Steam.ConfirmationDetails details in detailsResults.Where(details => (details != null) && (
@@ -282,8 +287,11 @@ namespace ArchiSteamFarm {
} }
} }
List<Task<bool>> tasks = confirmations.Select(confirmation => BotDatabase.MobileAuthenticator.HandleConfirmation(confirmation, accept)).ToList(); // This could be done in parallel, but for some reason Steam allows only one confirmation being accepted at the time
await Task.WhenAll(tasks).ConfigureAwait(false); // Therefore, even though no physical barrier stops us from doing so, we execute this synchronously
foreach (MobileAuthenticator.Confirmation confirmation in confirmations) {
await BotDatabase.MobileAuthenticator.HandleConfirmation(confirmation, accept).ConfigureAwait(false);
}
} }
internal async Task<bool> RefreshSession() { internal async Task<bool> RefreshSession() {
@@ -306,7 +314,7 @@ namespace ArchiSteamFarm {
return false; return false;
} }
if (ArchiWebHandler.Init(SteamClient, callback.Nonce, BotConfig.SteamParentalPIN)) { if (await ArchiWebHandler.Init(SteamClient, callback.Nonce, BotConfig.SteamParentalPIN).ConfigureAwait(false)) {
return true; return true;
} }
@@ -351,6 +359,8 @@ namespace ArchiSteamFarm {
return await Response2FAConfirm(steamID, false).ConfigureAwait(false); return await Response2FAConfirm(steamID, false).ConfigureAwait(false);
case "!2faok": case "!2faok":
return await Response2FAConfirm(steamID, true).ConfigureAwait(false); return await Response2FAConfirm(steamID, true).ConfigureAwait(false);
case "!api":
return ResponseAPI(steamID);
case "!exit": case "!exit":
return ResponseExit(steamID); return ResponseExit(steamID);
case "!farm": case "!farm":
@@ -360,9 +370,11 @@ namespace ArchiSteamFarm {
case "!loot": case "!loot":
return await ResponseSendTrade(steamID).ConfigureAwait(false); return await ResponseSendTrade(steamID).ConfigureAwait(false);
case "!pause": case "!pause":
return await ResponsePause(steamID).ConfigureAwait(false); return await ResponsePause(steamID, true).ConfigureAwait(false);
case "!rejoinchat": case "!rejoinchat":
return ResponseRejoinChat(steamID); return ResponseRejoinChat(steamID);
case "!resume":
return await ResponsePause(steamID, false).ConfigureAwait(false);
case "!restart": case "!restart":
return ResponseRestart(steamID); return ResponseRestart(steamID);
case "!status": case "!status":
@@ -405,7 +417,7 @@ namespace ArchiSteamFarm {
return await ResponseOwns(steamID, BotName, args[1]).ConfigureAwait(false); return await ResponseOwns(steamID, BotName, args[1]).ConfigureAwait(false);
case "!pause": case "!pause":
return await ResponsePause(steamID, args[1]).ConfigureAwait(false); return await ResponsePause(steamID, args[1], true).ConfigureAwait(false);
case "!play": case "!play":
if (args.Length > 2) { if (args.Length > 2) {
return await ResponsePlay(steamID, args[1], args[2]).ConfigureAwait(false); return await ResponsePlay(steamID, args[1], args[2]).ConfigureAwait(false);
@@ -418,6 +430,8 @@ namespace ArchiSteamFarm {
} }
return await ResponseRedeem(steamID, BotName, args[1], false).ConfigureAwait(false); return await ResponseRedeem(steamID, BotName, args[1], false).ConfigureAwait(false);
case "!resume":
return await ResponsePause(steamID, args[1], false).ConfigureAwait(false);
case "!start": case "!start":
return await ResponseStart(steamID, args[1]).ConfigureAwait(false); return await ResponseStart(steamID, args[1]).ConfigureAwait(false);
case "!status": case "!status":
@@ -500,7 +514,7 @@ namespace ArchiSteamFarm {
Logging.LogGenericInfo("Successfully finished importing mobile authenticator!", BotName); Logging.LogGenericInfo("Successfully finished importing mobile authenticator!", BotName);
} }
private async Task<string> ResponsePause(ulong steamID) { private async Task<string> ResponsePause(ulong steamID, bool pause) {
if (steamID == 0) { if (steamID == 0) {
Logging.LogNullError(nameof(steamID), BotName); Logging.LogNullError(nameof(steamID), BotName);
return null; return null;
@@ -510,16 +524,24 @@ namespace ArchiSteamFarm {
return null; return null;
} }
if (pause) {
if (CardsFarmer.ManualMode) { if (CardsFarmer.ManualMode) {
await CardsFarmer.SwitchToManualMode(false).ConfigureAwait(false); return "Automatic farming is stopped already!";
return "Automatic farming is enabled again!";
} }
await CardsFarmer.SwitchToManualMode(true).ConfigureAwait(false); await CardsFarmer.SwitchToManualMode(true).ConfigureAwait(false);
return "Automatic farming is now stopped!"; return "Automatic farming is now stopped!";
} }
private static async Task<string> ResponsePause(ulong steamID, string botName) { if (!CardsFarmer.ManualMode) {
return "Automatic farming is enabled already!";
}
await CardsFarmer.SwitchToManualMode(false).ConfigureAwait(false);
return "Automatic farming is now enabled!";
}
private static async Task<string> ResponsePause(ulong steamID, string botName, bool pause) {
if ((steamID == 0) || string.IsNullOrEmpty(botName)) { if ((steamID == 0) || string.IsNullOrEmpty(botName)) {
Logging.LogNullError(nameof(steamID) + " || " + nameof(botName)); Logging.LogNullError(nameof(steamID) + " || " + nameof(botName));
return null; return null;
@@ -527,7 +549,7 @@ namespace ArchiSteamFarm {
Bot bot; Bot bot;
if (Bots.TryGetValue(botName, out bot)) { if (Bots.TryGetValue(botName, out bot)) {
return await bot.ResponsePause(steamID).ConfigureAwait(false); return await bot.ResponsePause(steamID, pause).ConfigureAwait(false);
} }
if (IsOwner(steamID)) { if (IsOwner(steamID)) {
@@ -739,6 +761,28 @@ namespace ArchiSteamFarm {
return null; return null;
} }
private static string ResponseAPI(ulong steamID) {
if (steamID == 0) {
Logging.LogNullError(nameof(steamID));
return null;
}
if (!IsOwner(steamID)) {
return null;
}
var response = new {
Bots
};
try {
return JsonConvert.SerializeObject(response);
} catch (JsonException e) {
Logging.LogGenericException(e);
return null;
}
}
private static string ResponseExit(ulong steamID) { private static string ResponseExit(ulong steamID) {
if (steamID == 0) { if (steamID == 0) {
Logging.LogNullError(nameof(steamID)); Logging.LogNullError(nameof(steamID));
@@ -1452,6 +1496,7 @@ namespace ArchiSteamFarm {
return; return;
} }
ArchiWebHandler.OnDisconnected();
Logging.LogGenericInfo("Disconnected from Steam!", BotName); Logging.LogGenericInfo("Disconnected from Steam!", BotName);
FirstTradeSent = false; FirstTradeSent = false;
@@ -1503,6 +1548,14 @@ namespace ArchiSteamFarm {
return; return;
} }
for (byte i = 0; (i < Program.GlobalConfig.HttpTimeout) && !ArchiWebHandler.Ready; i++) {
await Task.Delay(1000).ConfigureAwait(false);
}
if (!ArchiWebHandler.Ready) {
return;
}
bool acceptedSomething = false; bool acceptedSomething = false;
foreach (ulong gid in callback.GuestPasses.Select(guestPass => guestPass["gid"].AsUnsignedLong()).Where(gid => gid != 0)) { foreach (ulong gid in callback.GuestPasses.Select(guestPass => guestPass["gid"].AsUnsignedLong()).Where(gid => gid != 0)) {
Logging.LogGenericInfo("Accepting gift: " + gid + "...", BotName); Logging.LogGenericInfo("Accepting gift: " + gid + "...", BotName);
@@ -1698,7 +1751,7 @@ namespace ArchiSteamFarm {
} }
} }
if (!ArchiWebHandler.Init(SteamClient, callback.WebAPIUserNonce, BotConfig.SteamParentalPIN)) { if (!await ArchiWebHandler.Init(SteamClient, callback.WebAPIUserNonce, BotConfig.SteamParentalPIN).ConfigureAwait(false)) {
if (!await RefreshSession().ConfigureAwait(false)) { if (!await RefreshSession().ConfigureAwait(false)) {
return; return;
} }

View File

@@ -31,10 +31,14 @@ using System.Linq;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Newtonsoft.Json;
namespace ArchiSteamFarm { namespace ArchiSteamFarm {
internal sealed class CardsFarmer { internal sealed class CardsFarmer {
[JsonProperty]
internal readonly ConcurrentDictionary<uint, float> GamesToFarm = new ConcurrentDictionary<uint, float>(); internal readonly ConcurrentDictionary<uint, float> GamesToFarm = new ConcurrentDictionary<uint, float>();
[JsonProperty]
internal readonly ConcurrentHashSet<uint> CurrentGamesFarming = new ConcurrentHashSet<uint>(); internal readonly ConcurrentHashSet<uint> CurrentGamesFarming = new ConcurrentHashSet<uint>();
private readonly ManualResetEventSlim FarmResetEvent = new ManualResetEventSlim(false); private readonly ManualResetEventSlim FarmResetEvent = new ManualResetEventSlim(false);
@@ -42,6 +46,7 @@ namespace ArchiSteamFarm {
private readonly Bot Bot; private readonly Bot Bot;
private readonly Timer Timer; private readonly Timer Timer;
[JsonProperty]
internal bool ManualMode { get; private set; } internal bool ManualMode { get; private set; }
private bool KeepFarming, NowFarming; private bool KeepFarming, NowFarming;
@@ -388,8 +393,18 @@ namespace ArchiSteamFarm {
return null; return null;
} }
Logging.LogGenericInfo("Status for " + appID + ": " + progress, Bot.BotName); byte cardsRemaining = 0;
return progress.Equals("No card drops remaining");
Match match = Regex.Match(progress, @"\d+");
if (match.Success) {
if (!byte.TryParse(match.Value, out cardsRemaining)) {
Logging.LogNullError(nameof(cardsRemaining), Bot.BotName);
return null;
}
}
Logging.LogGenericInfo("Status for " + appID + ": " + cardsRemaining + " cards remaining", Bot.BotName);
return cardsRemaining > 0;
} }
private bool FarmMultiple() { private bool FarmMultiple() {

View File

@@ -81,7 +81,7 @@ namespace ArchiSteamFarm {
internal byte FarmingDelay { get; private set; } = DefaultFarmingDelay; internal byte FarmingDelay { get; private set; } = DefaultFarmingDelay;
[JsonProperty(Required = Required.DisallowNull)] [JsonProperty(Required = Required.DisallowNull)]
internal byte LoginLimiterDelay { get; private set; } = 7; internal byte LoginLimiterDelay { get; private set; } = 10;
[JsonProperty(Required = Required.DisallowNull)] [JsonProperty(Required = Required.DisallowNull)]
internal byte InventoryLimiterDelay { get; private set; } = 3; internal byte InventoryLimiterDelay { get; private set; } = 3;

View File

@@ -442,8 +442,10 @@ namespace ArchiSteamFarm.JSON {
} }
} }
#pragma warning disable 649
[JsonProperty(PropertyName = "html")] [JsonProperty(PropertyName = "html")]
private string HTML; private string HTML;
#pragma warning restore 649
private uint _OtherSteamID3; private uint _OtherSteamID3;
private uint OtherSteamID3 { private uint OtherSteamID3 {

View File

@@ -31,5 +31,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers // You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below: // by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")] // [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("2.1.0.2")] [assembly: AssemblyVersion("2.1.0.7")]
[assembly: AssemblyFileVersion("2.1.0.2")] [assembly: AssemblyFileVersion("2.1.0.7")]

View File

@@ -201,14 +201,16 @@ namespace ArchiSteamFarm {
// Calculate our value of items to give // Calculate our value of items to give
List<uint> amountsToGive = new List<uint>(tradeOffer.ItemsToGive.Count); List<uint> amountsToGive = new List<uint>(tradeOffer.ItemsToGive.Count);
Dictionary<ulong, uint> amountMapToGive = new Dictionary<ulong, uint>(amountMap);
foreach (ulong key in tradeOffer.ItemsToGive.Select(item => item.ClassID)) { foreach (ulong key in tradeOffer.ItemsToGive.Select(item => item.ClassID)) {
uint amount; uint amount;
if (!amountMap.TryGetValue(key, out amount)) { if (!amountMapToGive.TryGetValue(key, out amount)) {
amountsToGive.Add(0); amountsToGive.Add(0);
continue; continue;
} }
amountsToGive.Add(amount); amountsToGive.Add(amount);
amountMapToGive[key] = amount - 1; // We're giving one, so we have one less
} }
// Sort it ascending // Sort it ascending
@@ -216,20 +218,23 @@ namespace ArchiSteamFarm {
// Calculate our value of items to receive // Calculate our value of items to receive
List<uint> amountsToReceive = new List<uint>(tradeOffer.ItemsToReceive.Count); List<uint> amountsToReceive = new List<uint>(tradeOffer.ItemsToReceive.Count);
Dictionary<ulong, uint> amountMapToReceive = new Dictionary<ulong, uint>(amountMap);
foreach (ulong key in tradeOffer.ItemsToReceive.Select(item => item.ClassID)) { foreach (ulong key in tradeOffer.ItemsToReceive.Select(item => item.ClassID)) {
uint amount; uint amount;
if (!amountMap.TryGetValue(key, out amount)) { if (!amountMapToReceive.TryGetValue(key, out amount)) {
amountsToReceive.Add(0); amountsToReceive.Add(0);
continue; continue;
} }
amountsToReceive.Add(amount); amountsToReceive.Add(amount);
amountMapToReceive[key] = amount + 1; // We're getting one, so we have one more
} }
// Sort it ascending // Sort it ascending
amountsToReceive.Sort(); amountsToReceive.Sort();
// Check actual difference // Check actual difference
// We sum only values at proper indexes of giving, because user might be overpaying
int difference = amountsToGive.Select((t, i) => (int) (t - amountsToReceive[i])).Sum(); int difference = amountsToGive.Select((t, i) => (int) (t - amountsToReceive[i])).Sum();
// Trade is worth for us if the difference is greater than 0 // Trade is worth for us if the difference is greater than 0

View File

@@ -80,6 +80,9 @@ namespace ArchiSteamFarm {
// Most web services expect that UserAgent is set, so we declare it globally // Most web services expect that UserAgent is set, so we declare it globally
HttpClient.DefaultRequestHeaders.UserAgent.ParseAdd(DefaultUserAgent); HttpClient.DefaultRequestHeaders.UserAgent.ParseAdd(DefaultUserAgent);
// We should always operate in English language, declare it globally
HttpClient.DefaultRequestHeaders.AcceptLanguage.ParseAdd("en-US,en;q=0.8,en-GB;q=0.6");
} }
internal async Task<bool> UrlHeadRetry(string request, string referer = null) { internal async Task<bool> UrlHeadRetry(string request, string referer = null) {

View File

@@ -9,7 +9,7 @@
"MaxFarmingTime": 10, "MaxFarmingTime": 10,
"IdleFarmingPeriod": 3, "IdleFarmingPeriod": 3,
"FarmingDelay": 15, "FarmingDelay": 15,
"LoginLimiterDelay": 7, "LoginLimiterDelay": 10,
"InventoryLimiterDelay": 3, "InventoryLimiterDelay": 3,
"ForceHttp": false, "ForceHttp": false,
"HttpTimeout": 60, "HttpTimeout": 60,

View File

@@ -81,7 +81,7 @@ namespace ConfigGenerator {
public byte FarmingDelay { get; set; } = DefaultFarmingDelay; public byte FarmingDelay { get; set; } = DefaultFarmingDelay;
[JsonProperty(Required = Required.DisallowNull)] [JsonProperty(Required = Required.DisallowNull)]
public byte LoginLimiterDelay { get; set; } = 7; public byte LoginLimiterDelay { get; set; } = 10;
[JsonProperty(Required = Required.DisallowNull)] [JsonProperty(Required = Required.DisallowNull)]
public byte InventoryLimiterDelay { get; set; } = 3; public byte InventoryLimiterDelay { get; set; } = 3;