diff --git a/ArchiSteamFarm/ArchiWebHandler.cs b/ArchiSteamFarm/ArchiWebHandler.cs index 10ba5ab87..beeb43e47 100644 --- a/ArchiSteamFarm/ArchiWebHandler.cs +++ b/ArchiSteamFarm/ArchiWebHandler.cs @@ -35,11 +35,10 @@ using System.Threading; namespace ArchiSteamFarm { internal sealed class ArchiWebHandler { - private const string SteamCommunity = "steamcommunity.com"; + private const string SteamCommunityHost = "steamcommunity.com"; private const byte MinSessionTTL = 15; // Assume session is valid for at least that amount of seconds - private static string SteamCommunityURL = "https://" + SteamCommunity; - + private static string SteamCommunityURL = "https://" + SteamCommunityHost; private static int Timeout = GlobalConfig.DefaultHttpTimeout * 1000; private readonly Bot Bot; @@ -50,7 +49,7 @@ namespace ArchiSteamFarm { internal static void Init() { Timeout = Program.GlobalConfig.HttpTimeout * 1000; - SteamCommunityURL = (Program.GlobalConfig.ForceHttp ? "http://" : "https://") + SteamCommunity; + SteamCommunityURL = (Program.GlobalConfig.ForceHttp ? "http://" : "https://") + SteamCommunityHost; } private static uint GetAppIDFromMarketHashName(string hashName) { @@ -110,6 +109,39 @@ namespace ArchiSteamFarm { WebBrowser = new WebBrowser(bot.BotName); } + internal async Task AcceptGift(ulong gid) { + if (gid == 0) { + return false; + } + + if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) { + return false; + } + + string sessionID = WebBrowser.CookieContainer.GetCookieValue(SteamCommunityURL, "sessionid"); + if (string.IsNullOrEmpty(sessionID)) { + Logging.LogNullError("sessionID"); + return false; + } + + string request = SteamCommunityURL + "/gifts/" + gid + "/acceptunpack"; + Dictionary data = new Dictionary(1) { + { "sessionid", sessionID } + }; + + bool result = false; + for (byte i = 0; i < WebBrowser.MaxRetries && !result; i++) { + result = await WebBrowser.UrlPost(request, data).ConfigureAwait(false); + } + + if (!result) { + Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName); + return false; + } + + return true; + } + internal bool Init(SteamClient steamClient, string webAPIUserNonce, string parentalPin) { if (steamClient == null || steamClient.SteamID == null || string.IsNullOrEmpty(webAPIUserNonce)) { return false; @@ -162,13 +194,13 @@ namespace ArchiSteamFarm { Logging.LogGenericInfo("Success!", Bot.BotName); - WebBrowser.CookieContainer.Add(new Cookie("sessionid", sessionID, "/", "." + SteamCommunity)); + WebBrowser.CookieContainer.Add(new Cookie("sessionid", sessionID, "/", "." + SteamCommunityHost)); string steamLogin = authResult["token"].Value; - WebBrowser.CookieContainer.Add(new Cookie("steamLogin", steamLogin, "/", "." + SteamCommunity)); + WebBrowser.CookieContainer.Add(new Cookie("steamLogin", steamLogin, "/", "." + SteamCommunityHost)); string steamLoginSecure = authResult["tokensecure"].Value; - WebBrowser.CookieContainer.Add(new Cookie("steamLoginSecure", steamLoginSecure, "/", "." + SteamCommunity)); + WebBrowser.CookieContainer.Add(new Cookie("steamLoginSecure", steamLoginSecure, "/", "." + SteamCommunityHost)); if (!UnlockParentalAccount(parentalPin).Result) { return false; @@ -178,48 +210,38 @@ namespace ArchiSteamFarm { return true; } - internal async Task IsLoggedIn() { - string request = SteamCommunityURL + "/my/profile"; - - HtmlDocument htmlDocument = null; - for (byte i = 0; i < WebBrowser.MaxRetries && htmlDocument == null; i++) { - htmlDocument = await WebBrowser.UrlGetToHtmlDocument(request).ConfigureAwait(false); + internal async Task JoinGroup(ulong groupID) { + if (groupID == 0) { + return false; } - if (htmlDocument == null) { + if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) { + return false; + } + + string sessionID = WebBrowser.CookieContainer.GetCookieValue(SteamCommunityURL, "sessionid"); + if (string.IsNullOrEmpty(sessionID)) { + Logging.LogNullError("sessionID"); + return false; + } + + string request = SteamCommunityURL + "/gid/" + groupID; + Dictionary data = new Dictionary(2) { + { "sessionID", sessionID }, + { "action", "join" } + }; + + bool result = false; + for (byte i = 0; i < WebBrowser.MaxRetries && !result; i++) { + result = await WebBrowser.UrlPost(request, data).ConfigureAwait(false); + } + + if (!result) { Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName); - return null; + return false; } - HtmlNode htmlNode = htmlDocument.DocumentNode.SelectSingleNode("//span[@id='account_pulldown']"); - return htmlNode != null; - } - - internal async Task RefreshSessionIfNeeded() { - if (DateTime.Now.Subtract(LastSessionRefreshCheck).TotalSeconds < MinSessionTTL) { - return true; - } - - await SessionSemaphore.WaitAsync().ConfigureAwait(false); - - if (DateTime.Now.Subtract(LastSessionRefreshCheck).TotalSeconds < MinSessionTTL) { - SessionSemaphore.Release(); - return true; - } - - bool result; - - bool? isLoggedIn = await IsLoggedIn().ConfigureAwait(false); - if (isLoggedIn.GetValueOrDefault(true)) { - result = true; - LastSessionRefreshCheck = DateTime.Now; - } else { - Logging.LogGenericInfo("Refreshing our session!", Bot.BotName); - result = await Bot.RefreshSession().ConfigureAwait(false); - } - - SessionSemaphore.Release(); - return result; + return true; } internal async Task> GetOwnedGames() { @@ -295,7 +317,7 @@ namespace ArchiSteamFarm { return null; } - Dictionary, Tuple> descriptionMap = new Dictionary, Tuple>(); + Dictionary, Tuple> descriptions = new Dictionary, Tuple>(); foreach (KeyValue description in response["descriptions"].Children) { ulong classID = description["classid"].AsUnsignedLong(); if (classID == 0) { @@ -305,7 +327,7 @@ namespace ArchiSteamFarm { ulong instanceID = description["instanceid"].AsUnsignedLong(); Tuple key = new Tuple(classID, instanceID); - if (descriptionMap.ContainsKey(key)) { + if (descriptions.ContainsKey(key)) { continue; } @@ -322,7 +344,7 @@ namespace ArchiSteamFarm { type = GetItemType(descriptionType); } - descriptionMap[key] = new Tuple(appID, type); + descriptions[key] = new Tuple(appID, type); } HashSet result = new HashSet(); @@ -347,7 +369,7 @@ namespace ArchiSteamFarm { Tuple key = new Tuple(steamItem.ClassID, steamItem.InstanceID); Tuple description; - if (descriptionMap.TryGetValue(key, out description)) { + if (descriptions.TryGetValue(key, out description)) { steamItem.RealAppID = description.Item1; steamItem.Type = description.Item2; } @@ -368,7 +390,7 @@ namespace ArchiSteamFarm { Tuple key = new Tuple(steamItem.ClassID, steamItem.InstanceID); Tuple description; - if (descriptionMap.TryGetValue(key, out description)) { + if (descriptions.TryGetValue(key, out description)) { steamItem.RealAppID = description.Item1; steamItem.Type = description.Item2; } @@ -382,41 +404,6 @@ namespace ArchiSteamFarm { return result; } - internal async Task JoinClan(ulong clanID) { - if (clanID == 0) { - return false; - } - - if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) { - return false; - } - - string sessionID = WebBrowser.CookieContainer.GetCookieValue(SteamCommunityURL, "sessionid"); - if (string.IsNullOrEmpty(sessionID)) { - Logging.LogNullError("sessionID"); - return false; - } - - string request = SteamCommunityURL + "/gid/" + clanID; - - Dictionary data = new Dictionary(2) { - { "sessionID", sessionID }, - { "action", "join" } - }; - - bool result = false; - for (byte i = 0; i < WebBrowser.MaxRetries && !result; i++) { - result = await WebBrowser.UrlPost(request, data).ConfigureAwait(false); - } - - if (!result) { - Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName); - return false; - } - - return true; - } - internal async Task AcceptTradeOffer(ulong tradeID) { if (tradeID == 0) { return false; @@ -432,9 +419,7 @@ namespace ArchiSteamFarm { return false; } - string referer = SteamCommunityURL + "/tradeoffer/" + tradeID; - string request = referer + "/accept"; - + string request = SteamCommunityURL + "/tradeoffer/" + tradeID + "/accept"; Dictionary data = new Dictionary(3) { { "sessionid", sessionID }, { "serverid", "1" }, @@ -443,7 +428,7 @@ namespace ArchiSteamFarm { bool result = false; for (byte i = 0; i < WebBrowser.MaxRetries && !result; i++) { - result = await WebBrowser.UrlPost(request, data, referer).ConfigureAwait(false); + result = await WebBrowser.UrlPost(request, data, SteamCommunityURL).ConfigureAwait(false); } if (!result) { @@ -608,9 +593,7 @@ namespace ArchiSteamFarm { itemID++; } - string referer = SteamCommunityURL + "/tradeoffer/new"; - string request = referer + "/send"; - + string request = SteamCommunityURL + "/tradeoffer/new/send"; foreach (Steam.TradeOfferRequest trade in trades) { Dictionary data = new Dictionary(6) { { "sessionid", sessionID }, @@ -623,7 +606,7 @@ namespace ArchiSteamFarm { bool result = false; for (byte i = 0; i < WebBrowser.MaxRetries && !result; i++) { - result = await WebBrowser.UrlPost(request, data, referer).ConfigureAwait(false); + result = await WebBrowser.UrlPost(request, data, SteamCommunityURL).ConfigureAwait(false); } if (!result) { @@ -703,37 +686,48 @@ namespace ArchiSteamFarm { return true; } - internal async Task AcceptGift(ulong gid) { - if (gid == 0) { - return false; + private async Task IsLoggedIn() { + string request = SteamCommunityURL + "/my/profile"; + + HtmlDocument htmlDocument = null; + for (byte i = 0; i < WebBrowser.MaxRetries && htmlDocument == null; i++) { + htmlDocument = await WebBrowser.UrlGetToHtmlDocument(request).ConfigureAwait(false); } - if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) { - return false; - } - - string sessionID = WebBrowser.CookieContainer.GetCookieValue(SteamCommunityURL, "sessionid"); - if (string.IsNullOrEmpty(sessionID)) { - Logging.LogNullError("sessionID"); - return false; - } - - string request = SteamCommunityURL + "/gifts/" + gid + "/acceptunpack"; - Dictionary data = new Dictionary(1) { - { "sessionid", sessionID } - }; - - bool result = false; - for (byte i = 0; i < WebBrowser.MaxRetries && !result; i++) { - result = await WebBrowser.UrlPost(request, data).ConfigureAwait(false); - } - - if (!result) { + if (htmlDocument == null) { Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName); - return false; + return null; } - return true; + HtmlNode htmlNode = htmlDocument.DocumentNode.SelectSingleNode("//span[@id='account_pulldown']"); + return htmlNode != null; + } + + private async Task RefreshSessionIfNeeded() { + if (DateTime.Now.Subtract(LastSessionRefreshCheck).TotalSeconds < MinSessionTTL) { + return true; + } + + await SessionSemaphore.WaitAsync().ConfigureAwait(false); + + if (DateTime.Now.Subtract(LastSessionRefreshCheck).TotalSeconds < MinSessionTTL) { + SessionSemaphore.Release(); + return true; + } + + bool result; + + bool? isLoggedIn = await IsLoggedIn().ConfigureAwait(false); + if (isLoggedIn.GetValueOrDefault(true)) { + result = true; + LastSessionRefreshCheck = DateTime.Now; + } else { + Logging.LogGenericInfo("Refreshing our session!", Bot.BotName); + result = await Bot.RefreshSession().ConfigureAwait(false); + } + + SessionSemaphore.Release(); + return result; } private async Task UnlockParentalAccount(string parentalPin) { @@ -742,16 +736,15 @@ namespace ArchiSteamFarm { } Logging.LogGenericInfo("Unlocking parental account...", Bot.BotName); + + string request = SteamCommunityURL + "/parental/ajaxunlock"; Dictionary data = new Dictionary(1) { { "pin", parentalPin } }; - string referer = SteamCommunityURL; - string request = referer + "/parental/ajaxunlock"; - bool result = false; for (byte i = 0; i < WebBrowser.MaxRetries && !result; i++) { - result = await WebBrowser.UrlPost(request, data, referer).ConfigureAwait(false); + result = await WebBrowser.UrlPost(request, data, SteamCommunityURL).ConfigureAwait(false); } if (!result) { diff --git a/ArchiSteamFarm/Bot.cs b/ArchiSteamFarm/Bot.cs index af8f9a22f..b8586f58b 100755 --- a/ArchiSteamFarm/Bot.cs +++ b/ArchiSteamFarm/Bot.cs @@ -44,7 +44,7 @@ namespace ArchiSteamFarm { internal static readonly Dictionary Bots = new Dictionary(); private static readonly uint LoginID = MsgClientLogon.ObfuscationMask; // This must be the same for all ASF bots and all ASF processes - private static readonly SemaphoreSlim SteamSemaphore = new SemaphoreSlim(1); + private static readonly SemaphoreSlim LoginSemaphore = new SemaphoreSlim(1); internal readonly string BotName; internal readonly ArchiHandler ArchiHandler; @@ -70,7 +70,7 @@ namespace ArchiSteamFarm { internal static async Task RefreshCMs(uint cellID) { bool initialized = false; - for (byte i = 0; i < 3 && !initialized; i++) { + for (byte i = 0; i < WebBrowser.MaxRetries && !initialized; i++) { try { Logging.LogGenericInfo("Refreshing list of CMs..."); await SteamDirectory.Initialize(cellID).ConfigureAwait(false); @@ -84,7 +84,7 @@ namespace ArchiSteamFarm { if (initialized) { Logging.LogGenericInfo("Success!"); } else { - Logging.LogGenericWarning("Failed to initialize list of CMs after 3 tries, ASF will use built-in SK2 list, it may take a while to connect"); + Logging.LogGenericWarning("Failed to initialize list of CMs after " + WebBrowser.MaxRetries + " tries, ASF will use built-in SK2 list, it may take a while to connect"); } } @@ -106,11 +106,11 @@ namespace ArchiSteamFarm { return Regex.IsMatch(key, @"[0-9A-Z]{4,5}-[0-9A-Z]{4,5}-[0-9A-Z]{4,5}-?(?:(?:[0-9A-Z]{4,5}-?)?(?:[0-9A-Z]{4,5}))?"); } - private static async Task LimitSteamRequestsAsync() { - await SteamSemaphore.WaitAsync().ConfigureAwait(false); + private static async Task LimitLoginRequestsAsync() { + await LoginSemaphore.WaitAsync().ConfigureAwait(false); Task.Run(async () => { await Utilities.SleepAsync(Program.GlobalConfig.LoginLimiterDelay * 1000).ConfigureAwait(false); - SteamSemaphore.Release(); + LoginSemaphore.Release(); }).Forget(); } @@ -133,12 +133,6 @@ namespace ArchiSteamFarm { return; } - BotDatabase = BotDatabase.Load(botPath + ".db"); - if (BotDatabase == null) { - Logging.LogGenericError("Bot database could not be loaded, refusing to start this bot instance!", botName); - return; - } - bool alreadyExists; lock (Bots) { alreadyExists = Bots.ContainsKey(botName); @@ -153,6 +147,12 @@ namespace ArchiSteamFarm { SentryFile = botPath + ".bin"; + BotDatabase = BotDatabase.Load(botPath + ".db"); + if (BotDatabase == null) { + Logging.LogGenericError("Bot database could not be loaded, refusing to start this bot instance!", botName); + return; + } + if (BotDatabase.SteamGuardAccount == null) { // Support and convert SDA files string maFilePath = botPath + ".maFile"; @@ -166,8 +166,8 @@ namespace ArchiSteamFarm { if (Program.GlobalConfig.Debug && !Debugging.NetHookAlreadyInitialized && Directory.Exists(Program.DebugDirectory)) { try { - SteamClient.DebugNetworkListener = new NetHookNetworkListener(Program.DebugDirectory); Debugging.NetHookAlreadyInitialized = true; + SteamClient.DebugNetworkListener = new NetHookNetworkListener(Program.DebugDirectory); } catch (Exception e) { Logging.LogGenericException(e, botName); } @@ -421,7 +421,7 @@ namespace ArchiSteamFarm { // 2FA tokens are expiring soon, don't use limiter when user is providing one if (TwoFactorCode == null || BotDatabase.SteamGuardAccount != null) { - await LimitSteamRequestsAsync().ConfigureAwait(false); + await LimitLoginRequestsAsync().ConfigureAwait(false); } Logging.LogGenericInfo("Starting...", BotName); @@ -1435,7 +1435,7 @@ namespace ArchiSteamFarm { // 2FA tokens are expiring soon, don't use limiter when user is providing one if (TwoFactorCode == null || BotDatabase.SteamGuardAccount != null) { - await LimitSteamRequestsAsync().ConfigureAwait(false); + await LimitLoginRequestsAsync().ConfigureAwait(false); } SteamClient.Connect(); @@ -1651,14 +1651,14 @@ namespace ArchiSteamFarm { if (BotConfig.SteamMasterClanID != 0) { Task.Run(async () => { - await ArchiWebHandler.JoinClan(BotConfig.SteamMasterClanID).ConfigureAwait(false); + await ArchiWebHandler.JoinGroup(BotConfig.SteamMasterClanID).ConfigureAwait(false); JoinMasterChat(); }).Forget(); } if (Program.GlobalConfig.Statistics) { Task.Run(async () => { - await ArchiWebHandler.JoinClan(ArchiSCFarmGroup).ConfigureAwait(false); + await ArchiWebHandler.JoinGroup(ArchiSCFarmGroup).ConfigureAwait(false); SteamFriends.JoinChat(ArchiSCFarmGroup); }).Forget(); }