private static void Main(string[] args) { Logging.LogGenericInfo("Main", "Archi's Steam Farm, version " + Version); Task.Run(async() => await CheckForUpdate().ConfigureAwait(false)).Wait(); // Config directory may not be in the same directory as the .exe, check maximum of 3 levels lower for (var i = 0; i < 4 && !Directory.Exists(ConfigDirectoryPath); i++) { Directory.SetCurrentDirectory(".."); } if (!Directory.Exists(ConfigDirectoryPath)) { Logging.LogGenericError("Main", "Config directory doesn't exist!"); Console.ReadLine(); Exit(1); } lock (Bots) { foreach (var configFile in Directory.EnumerateFiles(ConfigDirectoryPath, "*.xml")) { string botName = Path.GetFileNameWithoutExtension(configFile); Bot bot = new Bot(botName); Bots.Add(bot); if (!bot.Enabled) { Logging.LogGenericInfo(botName, "Not starting this instance because it's disabled in config file"); } } } Thread.Sleep(Timeout.Infinite); }
private static void InitServices() { string globalConfigFile = Path.Combine(SharedInfo.ConfigDirectory, SharedInfo.GlobalConfigFileName); GlobalConfig = GlobalConfig.Load(globalConfigFile); if (GlobalConfig == null) { Logging.LogGenericError("Global config could not be loaded, please make sure that " + globalConfigFile + " exists and is valid!"); Thread.Sleep(5000); Exit(1); } string globalDatabaseFile = Path.Combine(SharedInfo.ConfigDirectory, SharedInfo.GlobalDatabaseFileName); GlobalDatabase = GlobalDatabase.Load(globalDatabaseFile); if (GlobalDatabase == null) { Logging.LogGenericError("Global database could not be loaded, if issue persists, please remove " + globalDatabaseFile + " in order to recreate database!"); Thread.Sleep(5000); Exit(1); } ArchiWebHandler.Init(); WebBrowser.Init(); WCF.Init(); WebBrowser = new WebBrowser(SharedInfo.ASF); }
private static void OnDeleted(object sender, FileSystemEventArgs e) { if ((sender == null) || (e == null)) { Logging.LogNullError(nameof(sender) + " || " + nameof(e)); return; } string botName = Path.GetFileNameWithoutExtension(e.Name); if (string.IsNullOrEmpty(botName)) { return; } if (botName.Equals(SharedInfo.ASF)) { Logging.LogGenericError("Global config file has been removed, exiting..."); Program.Exit(1); return; } Bot bot; if (Bot.Bots.TryGetValue(botName, out bot)) { bot.OnNewConfigLoaded(new BotConfigEventArgs()).Forget(); } }
private async Task <HttpResponseMessage> UrlRequest(string request, HttpMethod httpMethod, Dictionary <string, string> data = null, string referer = null) { if (string.IsNullOrEmpty(request) || (httpMethod == null)) { Logging.LogNullError(nameof(request) + " || " + nameof(httpMethod)); return(null); } if (request.StartsWith("https://", StringComparison.Ordinal) && Program.GlobalConfig.ForceHttp) { return(null); } HttpResponseMessage responseMessage; using (HttpRequestMessage requestMessage = new HttpRequestMessage(httpMethod, request)) { if ((data != null) && (data.Count > 0)) { try { requestMessage.Content = new FormUrlEncodedContent(data); } catch (UriFormatException e) { Logging.LogGenericException(e, Identifier); return(null); } } if (!string.IsNullOrEmpty(referer)) { requestMessage.Headers.Referrer = new Uri(referer); } try { responseMessage = await HttpClient.SendAsync(requestMessage).ConfigureAwait(false); } catch { // Request failed, we don't need to know the exact reason, swallow exception return(null); } } if (responseMessage == null) { return(null); } if (responseMessage.IsSuccessStatusCode) { return(responseMessage); } if (Debugging.IsDebugBuild || Program.GlobalConfig.Debug) { Logging.LogGenericError("Request: " + request + " failed!", Identifier); Logging.LogGenericError("Status code: " + responseMessage.StatusCode, Identifier); Logging.LogGenericError("Content: " + Environment.NewLine + await responseMessage.Content.ReadAsStringAsync().ConfigureAwait(false), Identifier); } responseMessage.Dispose(); return(null); }
private bool FarmHours(ConcurrentHashSet <Game> games) { if ((games == null) || (games.Count == 0)) { Logging.LogNullError(nameof(games), Bot.BotName); return(false); } float maxHour = games.Max(game => game.HoursPlayed); if (maxHour < 0) { Logging.LogNullError(nameof(maxHour), Bot.BotName); return(false); } if (maxHour >= 2) { Logging.LogGenericError("Received request for past-2h games!", Bot.BotName); return(true); } Bot.ArchiHandler.PlayGames(games.Select(game => game.AppID), Bot.BotConfig.CustomGamePlayedWhileFarming); bool success = true; while (maxHour < 2) { Logging.LogGenericInfo("Still farming: " + string.Join(", ", games.Select(game => game.AppID)), Bot.BotName); DateTime startFarmingPeriod = DateTime.Now; if (FarmResetEvent.Wait(60 * 1000 * Program.GlobalConfig.FarmingDelay)) { FarmResetEvent.Reset(); success = KeepFarming; } // Don't forget to update our GamesToFarm hours float timePlayed = (float)DateTime.Now.Subtract(startFarmingPeriod).TotalHours; foreach (Game game in games) { game.HoursPlayed += timePlayed; } if (!success) { break; } maxHour += timePlayed; } Logging.LogGenericInfo("Stopped farming: " + string.Join(", ", games.Select(game => game.AppID)), Bot.BotName); return(success); }
private async Task <ParseTradeResult> ParseTrade(Steam.TradeOffer tradeOffer) { if (tradeOffer == null) { Logging.LogNullError(nameof(tradeOffer), Bot.BotName); return(null); } if (tradeOffer.State != Steam.TradeOffer.ETradeOfferState.Active) { Logging.LogGenericError("Ignoring trade in non-active state!", Bot.BotName); return(null); } ParseTradeResult result = await ShouldAcceptTrade(tradeOffer).ConfigureAwait(false); if (result == null) { Logging.LogNullError(nameof(result), Bot.BotName); return(null); } switch (result.Result) { case ParseTradeResult.EResult.AcceptedWithItemLose: case ParseTradeResult.EResult.AcceptedWithoutItemLose: Logging.LogGenericInfo("Accepting trade: " + tradeOffer.TradeOfferID, Bot.BotName); await Bot.ArchiWebHandler.AcceptTradeOffer(tradeOffer.TradeOfferID).ConfigureAwait(false); break; case ParseTradeResult.EResult.RejectedPermanently: case ParseTradeResult.EResult.RejectedTemporarily: if (result.Result == ParseTradeResult.EResult.RejectedPermanently) { if (Bot.BotConfig.IsBotAccount) { Logging.LogGenericInfo("Rejecting trade: " + tradeOffer.TradeOfferID, Bot.BotName); Bot.ArchiWebHandler.DeclineTradeOffer(tradeOffer.TradeOfferID); break; } IgnoredTrades.Add(tradeOffer.TradeOfferID); } Logging.LogGenericInfo("Ignoring trade: " + tradeOffer.TradeOfferID, Bot.BotName); break; } return(result); }
private void SortGamesToFarm() { IOrderedEnumerable <Game> gamesToFarm; switch (Bot.BotConfig.FarmingOrder) { case BotConfig.EFarmingOrder.Unordered: return; case BotConfig.EFarmingOrder.AppIDsAscending: gamesToFarm = GamesToFarm.OrderBy(game => game.AppID); break; case BotConfig.EFarmingOrder.AppIDsDescending: gamesToFarm = GamesToFarm.OrderByDescending(game => game.AppID); break; case BotConfig.EFarmingOrder.CardDropsAscending: gamesToFarm = GamesToFarm.OrderBy(game => game.CardsRemaining); break; case BotConfig.EFarmingOrder.CardDropsDescending: gamesToFarm = GamesToFarm.OrderByDescending(game => game.CardsRemaining); break; case BotConfig.EFarmingOrder.HoursAscending: gamesToFarm = GamesToFarm.OrderBy(game => game.HoursPlayed); break; case BotConfig.EFarmingOrder.HoursDescending: gamesToFarm = GamesToFarm.OrderByDescending(game => game.HoursPlayed); break; case BotConfig.EFarmingOrder.NamesAscending: gamesToFarm = GamesToFarm.OrderBy(game => game.GameName); break; case BotConfig.EFarmingOrder.NamesDescending: gamesToFarm = GamesToFarm.OrderByDescending(game => game.GameName); break; default: Logging.LogGenericError("Unhandled case: " + Bot.BotConfig.FarmingOrder, Bot.BotName); return; } GamesToFarm.ReplaceWith(gamesToFarm.ToList()); // We must call ToList() here as we can't enumerate during replacing }
private void OnConnected(SteamClient.ConnectedCallback callback) { if (callback == null) { return; } if (callback.Result != EResult.OK) { Logging.LogGenericError(BotName, "Unable to connect to Steam: " + callback.Result); return; } Logging.LogGenericInfo(BotName, "Connected to Steam!"); byte[] sentryHash = null; if (File.Exists(SentryFile)) { byte[] sentryFileContent = File.ReadAllBytes(SentryFile); sentryHash = CryptoHelper.SHAHash(sentryFileContent); } string steamLogin = SteamLogin; if (string.IsNullOrEmpty(steamLogin) || steamLogin.Equals("null")) { steamLogin = Program.GetUserInput(BotName, Program.EUserInputType.Login); Config["SteamLogin"] = steamLogin; } string steamPassword = SteamPassword; if (string.IsNullOrEmpty(steamPassword) || steamPassword.Equals("null")) { steamPassword = Program.GetUserInput(BotName, Program.EUserInputType.Password); Config["SteamPassword"] = steamPassword; } SteamUser.LogOn(new SteamUser.LogOnDetails { Username = steamLogin, Password = steamPassword, AuthCode = AuthCode, TwoFactorCode = TwoFactorAuth, SentryFileHash = sentryHash }); }
private static void Main(string[] args) { Logging.LogGenericInfo("Main", "Archi's Steam Farm, version " + Version); Task.Run(async() => await CheckForUpdate().ConfigureAwait(false)).Wait(); // Allow loading configs from source tree if it's a debug build if (Debugging.IsDebugBuild) { for (var i = 0; i < 4; i++) { Directory.SetCurrentDirectory(".."); if (Directory.Exists(ConfigDirectoryPath)) { break; } } } if (!Directory.Exists(ConfigDirectoryPath)) { Logging.LogGenericError("Main", "Config directory doesn't exist!"); Console.ReadLine(); Task.Run(async() => await Exit(1).ConfigureAwait(false)).Wait(); } foreach (var configFile in Directory.EnumerateFiles(ConfigDirectoryPath, "*.xml")) { string botName = Path.GetFileNameWithoutExtension(configFile); Bot bot = new Bot(botName); if (!bot.Enabled) { Logging.LogGenericInfo(botName, "Not starting this instance because it's disabled in config file"); } Thread.Sleep(1000); // Try to avoid spamming steam } // Check if we got any bots running OnBotShutdown(null); ShutdownResetEvent.WaitOne(); }
private static void InitServices() { GlobalConfig = GlobalConfig.Load(); if (GlobalConfig == null) { Logging.LogGenericError("Global config could not be loaded, please make sure that ASF.json exists and is valid!"); Thread.Sleep(5000); Exit(1); } GlobalDatabase = GlobalDatabase.Load(); if (GlobalDatabase == null) { Logging.LogGenericError("Global database could not be loaded!"); Thread.Sleep(5000); Exit(1); } ArchiWebHandler.Init(); WebBrowser.Init(); WCF.Init(); }
private async Task <HttpResponseMessage> UrlRequest(string request, HttpMethod httpMethod, IEnumerable <KeyValuePair <string, string> > data = null, string referer = null) { if (string.IsNullOrEmpty(request) || (httpMethod == null)) { Logging.LogNullError(nameof(request) + " || " + nameof(httpMethod), Identifier); return(null); } if (request.StartsWith("https://", StringComparison.Ordinal) && Program.GlobalConfig.ForceHttp) { return(null); } HttpResponseMessage responseMessage; using (HttpRequestMessage requestMessage = new HttpRequestMessage(httpMethod, request)) { if (data != null) { try { requestMessage.Content = new FormUrlEncodedContent(data); } catch (UriFormatException e) { Logging.LogGenericException(e, Identifier); return(null); } } if (!string.IsNullOrEmpty(referer)) { requestMessage.Headers.Referrer = new Uri(referer); } try { responseMessage = await HttpClient.SendAsync(requestMessage).ConfigureAwait(false); } catch (Exception e) { // This exception is really common, don't bother with it unless debug mode is enabled if (Debugging.IsDebugBuild || Program.GlobalConfig.Debug) { Logging.LogGenericException(e, Identifier); } return(null); } } if (responseMessage == null) { return(null); } if (responseMessage.IsSuccessStatusCode) { return(responseMessage); } if (Debugging.IsDebugBuild || Program.GlobalConfig.Debug) { Logging.LogGenericError("Request: " + request + " failed!", Identifier); Logging.LogGenericError("Status code: " + responseMessage.StatusCode, Identifier); Logging.LogGenericError("Content: " + Environment.NewLine + await responseMessage.Content.ReadAsStringAsync().ConfigureAwait(false), Identifier); } responseMessage.Dispose(); return(null); }
private static void Init(string[] args) { AppDomain.CurrentDomain.UnhandledException += UnhandledExceptionHandler; TaskScheduler.UnobservedTaskException += UnobservedTaskExceptionHandler; Logging.LogGenericInfo("Archi's Steam Farm, version " + Version); Directory.SetCurrentDirectory(ExecutableDirectory); InitServices(); // Allow loading configs from source tree if it's a debug build if (Debugging.IsDebugBuild) { // Common structure is bin/(x64/)Debug/ArchiSteamFarm.exe, so we allow up to 4 directories up for (var i = 0; i < 4; i++) { Directory.SetCurrentDirectory(".."); if (Directory.Exists(ConfigDirectory)) { break; } } // If config directory doesn't exist after our adjustment, abort all of that if (!Directory.Exists(ConfigDirectory)) { Directory.SetCurrentDirectory(ExecutableDirectory); } } // If debugging is on, we prepare debug directory prior to running if (GlobalConfig.Debug) { if (Directory.Exists(DebugDirectory)) { Directory.Delete(DebugDirectory, true); Thread.Sleep(1000); // Dirty workaround giving Windows some time to sync } Directory.CreateDirectory(DebugDirectory); SteamKit2.DebugLog.AddListener(new Debugging.DebugListener(Path.Combine(Program.DebugDirectory, "debug.txt"))); SteamKit2.DebugLog.Enabled = true; } // Parse args ParseArgs(args); // If we ran ASF as a client, we're done by now if (Mode == EMode.Client) { return; } // From now on it's server mode Logging.Init(); if (!Directory.Exists(ConfigDirectory)) { Logging.LogGenericError("Config directory doesn't exist!"); Thread.Sleep(5000); Environment.Exit(1); } CheckForUpdate().Wait(); // Before attempting to connect, initialize our list of CMs Bot.RefreshCMs(GlobalDatabase.CellID).Wait(); bool isRunning = false; foreach (var configFile in Directory.EnumerateFiles(ConfigDirectory, "*.json")) { string botName = Path.GetFileNameWithoutExtension(configFile); if (botName.Equals(ASF)) { continue; } Bot bot = new Bot(botName); if (bot.BotConfig != null && bot.BotConfig.Enabled) { isRunning = true; } else { Logging.LogGenericInfo("Not starting this instance because it's disabled in config file", botName); } } // CONVERSION START foreach (var configFile in Directory.EnumerateFiles(ConfigDirectory, "*.xml")) { string botName = Path.GetFileNameWithoutExtension(configFile); Logging.LogGenericWarning("Found legacy " + botName + ".xml config file, it will now be converted to new ASF V2.0 format!"); Bot bot = new Bot(botName); if (bot.BotConfig != null && bot.BotConfig.Enabled) { isRunning = true; } else { Logging.LogGenericInfo("Not starting this instance because it's disabled in config file", botName); } } // CONVERSION END // Check if we got any bots running if (!isRunning) { OnBotShutdown(); } }
private static async Task <HttpResponseMessage> UrlRequest(string request, HttpMethod httpMethod, Dictionary <string, string> data = null, Dictionary <string, string> cookies = null, string referer = null) { if (string.IsNullOrEmpty(request) || httpMethod == null) { return(null); } if (request.StartsWith("https://") && Program.GlobalConfig.ForceHttp) { return(null); } HttpResponseMessage responseMessage; using (HttpRequestMessage requestMessage = new HttpRequestMessage(httpMethod, request)) { if (data != null && data.Count > 0) { try { requestMessage.Content = new FormUrlEncodedContent(data); } catch (UriFormatException e) { Logging.LogGenericException(e); return(null); } } if (cookies != null && cookies.Count > 0) { StringBuilder cookieHeader = new StringBuilder(); foreach (KeyValuePair <string, string> cookie in cookies) { cookieHeader.Append(cookie.Key + "=" + cookie.Value + ";"); } requestMessage.Headers.Add("Cookie", cookieHeader.ToString()); } if (!string.IsNullOrEmpty(referer)) { requestMessage.Headers.Referrer = new Uri(referer); } try { responseMessage = await HttpClient.SendAsync(requestMessage).ConfigureAwait(false); } catch { // Request failed, we don't need to know the exact reason, swallow exception return(null); } } if (responseMessage == null) { return(null); } if (!responseMessage.IsSuccessStatusCode) { if (Debugging.IsDebugBuild || Program.GlobalConfig.Debug) { Logging.LogGenericError("Request: " + request + " failed!"); Logging.LogGenericError("Status code: " + responseMessage.StatusCode); Logging.LogGenericError("Content: " + Environment.NewLine + await responseMessage.Content.ReadAsStringAsync().ConfigureAwait(false)); } return(null); } return(responseMessage); }
private static void Init(IEnumerable <string> args) { AppDomain.CurrentDomain.UnhandledException += UnhandledExceptionHandler; TaskScheduler.UnobservedTaskException += UnobservedTaskExceptionHandler; Logging.LogGenericInfo("ASF V" + Version); Directory.SetCurrentDirectory(ExecutableDirectory); InitServices(); // Allow loading configs from source tree if it's a debug build if (Debugging.IsDebugBuild) { // Common structure is bin/(x64/)Debug/ArchiSteamFarm.exe, so we allow up to 4 directories up for (byte i = 0; i < 4; i++) { Directory.SetCurrentDirectory(".."); if (Directory.Exists(ConfigDirectory)) { break; } } // If config directory doesn't exist after our adjustment, abort all of that if (!Directory.Exists(ConfigDirectory)) { Directory.SetCurrentDirectory(ExecutableDirectory); } } // If debugging is on, we prepare debug directory prior to running if (GlobalConfig.Debug) { if (Directory.Exists(DebugDirectory)) { Directory.Delete(DebugDirectory, true); Thread.Sleep(1000); // Dirty workaround giving Windows some time to sync } Directory.CreateDirectory(DebugDirectory); SteamKit2.DebugLog.AddListener(new Debugging.DebugListener(Path.Combine(DebugDirectory, "debug.txt"))); SteamKit2.DebugLog.Enabled = true; } // Parse args if (args != null) { ParseArgs(args); } // If we ran ASF as a client, we're done by now if (Mode == EMode.Client) { Exit(); } // From now on it's server mode Logging.Init(); if (!Directory.Exists(ConfigDirectory)) { Logging.LogGenericError("Config directory doesn't exist!"); Thread.Sleep(5000); Exit(1); } CheckForUpdate().Wait(); // Before attempting to connect, initialize our list of CMs Bot.RefreshCMs(GlobalDatabase.CellID).Wait(); bool isRunning = false; foreach (string botName in Directory.EnumerateFiles(ConfigDirectory, "*.json").Select(Path.GetFileNameWithoutExtension)) { switch (botName) { case ASF: case "example": case "minimal": continue; } Bot bot = new Bot(botName); if ((bot.BotConfig == null) || !bot.BotConfig.Enabled) { continue; } if (bot.BotConfig.StartOnLaunch) { isRunning = true; } } // Check if we got any bots running if (!isRunning) { OnBotShutdown(); } }
internal async Task StartFarming() { await StopFarming().ConfigureAwait(false); await Semaphore.WaitAsync().ConfigureAwait(false); if (NowFarming) { Semaphore.Release(); return; } Logging.LogGenericInfo(Bot.BotName, "Checking badges..."); // Find the number of badge pages HtmlDocument badgesDocument = await Bot.ArchiWebHandler.GetBadgePage(1).ConfigureAwait(false); if (badgesDocument == null) { Logging.LogGenericWarning(Bot.BotName, "Could not get badges information, farming is stopped!"); Semaphore.Release(); return; } var maxPages = 1; HtmlNodeCollection badgesPagesNodeCollection = badgesDocument.DocumentNode.SelectNodes("//a[@class='pagelink']"); if (badgesPagesNodeCollection != null) { maxPages = (badgesPagesNodeCollection.Count / 2) + 1; // Don't do this at home } // Find APPIDs we need to farm List <uint> appIDs = new List <uint>(); for (var page = 1; page <= maxPages; page++) { Logging.LogGenericInfo(Bot.BotName, "Checking page: " + page + "/" + maxPages); if (page > 1) // Because we fetched page number 1 already { badgesDocument = await Bot.ArchiWebHandler.GetBadgePage(page).ConfigureAwait(false); if (badgesDocument == null) { break; } } HtmlNodeCollection badgesPageNodes = badgesDocument.DocumentNode.SelectNodes("//a[@class='btn_green_white_innerfade btn_small_thin']"); if (badgesPageNodes == null) { continue; } foreach (HtmlNode badgesPageNode in badgesPageNodes) { string steamLink = badgesPageNode.GetAttributeValue("href", null); if (steamLink == null) { Logging.LogGenericError(Bot.BotName, "Couldn't get steamLink for one of the games: " + badgesPageNode.OuterHtml); continue; } uint appID = (uint)Utilities.OnlyNumbers(steamLink); if (appID == 0) { Logging.LogGenericError(Bot.BotName, "Couldn't get appID for one of the games: " + badgesPageNode.OuterHtml); continue; } if (Bot.Blacklist.Contains(appID)) { continue; } appIDs.Add(appID); } } // Start farming while (appIDs.Count > 0) { Logging.LogGenericInfo(Bot.BotName, "Farming in progress..."); uint appID = appIDs[0]; if (await Farm(appID).ConfigureAwait(false)) { appIDs.Remove(appID); } else { return; } } Logging.LogGenericInfo(Bot.BotName, "Farming finished!"); await Bot.OnFarmingFinished().ConfigureAwait(false); }
// TODO: This should be removed soon internal static BotConfig LoadOldFormat(string path) { if (!File.Exists(path)) { return(null); } BotConfig botConfig = new BotConfig(); try { using (XmlReader reader = XmlReader.Create(path)) { while (reader.Read()) { if (reader.NodeType != XmlNodeType.Element) { continue; } string key = reader.Name; if (string.IsNullOrEmpty(key)) { continue; } string value = reader.GetAttribute("value"); if (string.IsNullOrEmpty(value)) { continue; } switch (key) { case "Enabled": botConfig.Enabled = bool.Parse(value); break; case "SteamLogin": botConfig.SteamLogin = value; break; case "SteamPassword": botConfig.SteamPassword = value; break; case "SteamApiKey": botConfig.SteamApiKey = value; break; case "SteamTradeToken": botConfig.SteamTradeToken = value; break; case "SteamParentalPIN": botConfig.SteamParentalPIN = value; break; case "SteamMasterID": botConfig.SteamMasterID = ulong.Parse(value); break; case "SteamMasterClanID": botConfig.SteamMasterClanID = ulong.Parse(value); break; case "StartOnLaunch": botConfig.StartOnLaunch = bool.Parse(value); break; case "UseAsfAsMobileAuthenticator": botConfig.UseAsfAsMobileAuthenticator = bool.Parse(value); break; case "CardDropsRestricted": botConfig.CardDropsRestricted = bool.Parse(value); break; case "FarmOffline": botConfig.FarmOffline = bool.Parse(value); break; case "HandleOfflineMessages": botConfig.HandleOfflineMessages = bool.Parse(value); break; case "ForwardKeysToOtherBots": botConfig.ForwardKeysToOtherBots = bool.Parse(value); break; case "DistributeKeys": botConfig.DistributeKeys = bool.Parse(value); break; case "ShutdownOnFarmingFinished": botConfig.ShutdownOnFarmingFinished = bool.Parse(value); break; case "SendOnFarmingFinished": botConfig.SendOnFarmingFinished = bool.Parse(value); break; case "SendTradePeriod": botConfig.SendTradePeriod = byte.Parse(value); break; case "GamesPlayedWhileIdle": botConfig.GamesPlayedWhileIdle.Clear(); foreach (string appID in value.Split(',')) { botConfig.GamesPlayedWhileIdle.Add(uint.Parse(appID)); } break; case "Statistics": case "Blacklist": case "SteamNickname": break; default: Logging.LogGenericWarning("Unrecognized config value: " + key + "=" + value); break; } } } } catch (Exception e) { Logging.LogGenericException(e); Logging.LogGenericError("Your config for this bot instance is invalid, it won't run!"); return(null); } // Fixups for new format if (botConfig.SteamLogin != null && botConfig.SteamLogin.Equals("null")) { botConfig.SteamLogin = null; } if (botConfig.SteamPassword != null && botConfig.SteamPassword.Equals("null")) { botConfig.SteamPassword = null; } if (botConfig.SteamApiKey != null && botConfig.SteamApiKey.Equals("null")) { botConfig.SteamApiKey = null; } if (botConfig.SteamParentalPIN != null && botConfig.SteamParentalPIN.Equals("null")) { botConfig.SteamParentalPIN = null; } if (botConfig.SteamTradeToken != null && botConfig.SteamTradeToken.Equals("null")) { botConfig.SteamTradeToken = null; } return(botConfig); }
private static void Init() { AppDomain.CurrentDomain.UnhandledException += UnhandledExceptionHandler; TaskScheduler.UnobservedTaskException += UnobservedTaskExceptionHandler; Logging.InitCoreLoggers(); if (!Runtime.IsRuntimeSupported) { Logging.LogGenericError("ASF detected unsupported runtime version, program might NOT run correctly in current environment. You're running it at your own risk!"); } string homeDirectory = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location); if (!string.IsNullOrEmpty(homeDirectory)) { Directory.SetCurrentDirectory(homeDirectory); // Allow loading configs from source tree if it's a debug build if (Debugging.IsDebugBuild) { // Common structure is bin/(x64/)Debug/ArchiSteamFarm.exe, so we allow up to 4 directories up for (byte i = 0; i < 4; i++) { Directory.SetCurrentDirectory(".."); if (!Directory.Exists(SharedInfo.ASFDirectory)) { continue; } Directory.SetCurrentDirectory(SharedInfo.ASFDirectory); break; } // If config directory doesn't exist after our adjustment, abort all of that if (!Directory.Exists(SharedInfo.ConfigDirectory)) { Directory.SetCurrentDirectory(homeDirectory); } } } InitServices(); // If debugging is on, we prepare debug directory prior to running if (GlobalConfig.Debug) { if (Directory.Exists(SharedInfo.DebugDirectory)) { Directory.Delete(SharedInfo.DebugDirectory, true); Thread.Sleep(1000); // Dirty workaround giving Windows some time to sync } Directory.CreateDirectory(SharedInfo.DebugDirectory); SteamKit2.DebugLog.AddListener(new Debugging.DebugListener()); SteamKit2.DebugLog.Enabled = true; } Logging.InitEnhancedLoggers(); }
private static void Main(string[] args) { AppDomain.CurrentDomain.UnhandledException += UnhandledExceptionHandler; Logging.LogGenericInfo("Archi's Steam Farm, version " + Version); Directory.SetCurrentDirectory(ExecutableDirectory); InitServices(); // Allow loading configs from source tree if it's a debug build if (Debugging.IsDebugBuild) { // Common structure is bin/(x64/)Debug/ArchiSteamFarm.exe, so we allow up to 4 directories up for (var i = 0; i < 4; i++) { Directory.SetCurrentDirectory(".."); if (Directory.Exists(ConfigDirectory)) { break; } } // If config directory doesn't exist after our adjustment, abort all of that if (!Directory.Exists(ConfigDirectory)) { Directory.SetCurrentDirectory(ExecutableDirectory); } } // By default we're operating on normal mode Mode = EMode.Normal; Logging.LogToFile = true; // But that can be overriden by arguments ParseArgs(args); // If we ran ASF as a client, we're done by now if (Mode == EMode.Client) { return; } Task.Run(async() => await CheckForUpdate().ConfigureAwait(false)).Wait(); if (!Directory.Exists(ConfigDirectory)) { Logging.LogGenericError("Config directory doesn't exist!"); Thread.Sleep(5000); Exit(1); } // Before attempting to connect, initialize our list of CMs Bot.RefreshCMs().Wait(); foreach (var configFile in Directory.EnumerateFiles(ConfigDirectory, "*.xml")) { string botName = Path.GetFileNameWithoutExtension(configFile); Bot bot = new Bot(botName); if (!bot.Enabled) { Logging.LogGenericInfo("Not starting this instance because it's disabled in config file", botName); } } // Check if we got any bots running OnBotShutdown(); // Wait for signal to shutdown ShutdownResetEvent.WaitOne(); // We got a signal to shutdown, consider giving user some time to read the message Thread.Sleep(5000); // This is over, cleanup only now WCF.StopServer(); }
private static void Main(string[] args) { AppDomain.CurrentDomain.UnhandledException += UnhandledExceptionHandler; Logging.LogGenericInfo("Archi's Steam Farm, version " + Version); Directory.SetCurrentDirectory(ExecutableDirectory); InitServices(); // Allow loading configs from source tree if it's a debug build if (Debugging.IsDebugBuild) { // Common structure is bin/(x64/)Debug/ArchiSteamFarm.exe, so we allow up to 4 directories up for (var i = 0; i < 4; i++) { Directory.SetCurrentDirectory(".."); if (Directory.Exists(ConfigDirectory)) { break; } } // If config directory doesn't exist after our adjustment, abort all of that if (!Directory.Exists(ConfigDirectory)) { Directory.SetCurrentDirectory(ExecutableDirectory); } } // Parse args ParseArgs(args); // If we ran ASF as a client, we're done by now if (Mode == EMode.Client) { return; } // From now on it's server mode Logging.Init(); if (!Directory.Exists(ConfigDirectory)) { Logging.LogGenericError("Config directory doesn't exist!"); Thread.Sleep(5000); Exit(1); } CheckForUpdate().Wait(); // Before attempting to connect, initialize our list of CMs Bot.RefreshCMs(GlobalDatabase.CellID).Wait(); foreach (var configFile in Directory.EnumerateFiles(ConfigDirectory, "*.json")) { string botName = Path.GetFileNameWithoutExtension(configFile); if (botName.Equals(ASF)) { continue; } Bot bot = new Bot(botName); if (bot.BotConfig == null || !bot.BotConfig.Enabled) { Logging.LogGenericInfo("Not starting this instance because it's disabled in config file", botName); } } // CONVERSION START foreach (var configFile in Directory.EnumerateFiles(ConfigDirectory, "*.xml")) { string botName = Path.GetFileNameWithoutExtension(configFile); Logging.LogGenericWarning("Found legacy " + botName + ".xml config file, it will now be converted to new ASF V2.0 format!"); Bot bot = new Bot(botName); if (bot.BotConfig == null || !bot.BotConfig.Enabled) { Logging.LogGenericInfo("Not starting this instance because it's disabled in config file", botName); } } // CONVERSION END // Check if we got any bots running OnBotShutdown(); // Wait for signal to shutdown ShutdownResetEvent.WaitOne(); // We got a signal to shutdown, consider giving user some time to read the message Thread.Sleep(5000); // This is over, cleanup only now WCF.StopServer(); }
internal static async Task CheckForUpdate(bool updateOverride = false) { string oldExeFile = ExecutableFile + ".old"; // We booted successfully so we can now remove old exe file if (File.Exists(oldExeFile)) { // It's entirely possible that old process is still running, allow at least a second before trying to remove the file await Utilities.SleepAsync(1000).ConfigureAwait(false); try { File.Delete(oldExeFile); } catch (Exception e) { Logging.LogGenericException(e); Logging.LogGenericError("Could not remove old ASF binary, please remove " + oldExeFile + " manually in order for update function to work!"); } } if (GlobalConfig.UpdateChannel == GlobalConfig.EUpdateChannel.Unknown) { return; } string releaseURL = GithubReleaseURL; if (GlobalConfig.UpdateChannel == GlobalConfig.EUpdateChannel.Stable) { releaseURL += "/latest"; } Logging.LogGenericInfo("Checking new version..."); string response = await WebBrowser.UrlGetToContentRetry(releaseURL).ConfigureAwait(false); if (string.IsNullOrEmpty(response)) { Logging.LogGenericWarning("Could not check latest version!"); return; } GitHub.ReleaseResponse releaseResponse; if (GlobalConfig.UpdateChannel == GlobalConfig.EUpdateChannel.Stable) { try { releaseResponse = JsonConvert.DeserializeObject <GitHub.ReleaseResponse>(response); } catch (JsonException e) { Logging.LogGenericException(e); return; } } else { List <GitHub.ReleaseResponse> releases; try { releases = JsonConvert.DeserializeObject <List <GitHub.ReleaseResponse> >(response); } catch (JsonException e) { Logging.LogGenericException(e); return; } if ((releases == null) || (releases.Count == 0)) { Logging.LogGenericWarning("Could not check latest version!"); return; } releaseResponse = releases[0]; } if (string.IsNullOrEmpty(releaseResponse.Tag)) { Logging.LogGenericWarning("Could not check latest version!"); return; } Version newVersion = new Version(releaseResponse.Tag); Logging.LogGenericInfo("Local version: " + Version + " | Remote version: " + newVersion); if (Version.CompareTo(newVersion) >= 0) // If local version is the same or newer than remote version { if ((AutoUpdatesTimer != null) || !GlobalConfig.AutoUpdates) { return; } Logging.LogGenericInfo("ASF will automatically check for new versions every 24 hours"); AutoUpdatesTimer = new Timer( async e => await CheckForUpdate().ConfigureAwait(false), null, TimeSpan.FromDays(1), // Delay TimeSpan.FromDays(1) // Period ); return; } if (!updateOverride && !GlobalConfig.AutoUpdates) { Logging.LogGenericInfo("New version is available!"); Logging.LogGenericInfo("Consider updating yourself!"); await Utilities.SleepAsync(5000).ConfigureAwait(false); return; } if (File.Exists(oldExeFile)) { Logging.LogGenericWarning("Refusing to proceed with auto update as old " + oldExeFile + " binary could not be removed, please remove it manually"); return; } // Auto update logic starts here if (releaseResponse.Assets == null) { Logging.LogGenericWarning("Could not proceed with update because that version doesn't include assets!"); return; } GitHub.ReleaseResponse.Asset binaryAsset = releaseResponse.Assets.FirstOrDefault(asset => !string.IsNullOrEmpty(asset.Name) && asset.Name.Equals(ExecutableName, StringComparison.OrdinalIgnoreCase)); if (binaryAsset == null) { Logging.LogGenericWarning("Could not proceed with update because there is no asset that relates to currently running binary!"); return; } if (string.IsNullOrEmpty(binaryAsset.DownloadURL)) { Logging.LogGenericWarning("Could not proceed with update because download URL is empty!"); return; } byte[] result = await WebBrowser.UrlGetToBytesRetry(binaryAsset.DownloadURL).ConfigureAwait(false); if (result == null) { return; } string newExeFile = ExecutableFile + ".new"; // Firstly we create new exec try { File.WriteAllBytes(newExeFile, result); } catch (Exception e) { Logging.LogGenericException(e); return; } // Now we move current -> old try { File.Move(ExecutableFile, oldExeFile); } catch (Exception e) { Logging.LogGenericException(e); try { // Cleanup File.Delete(newExeFile); } catch { // Ignored } return; } // Now we move new -> current try { File.Move(newExeFile, ExecutableFile); } catch (Exception e) { Logging.LogGenericException(e); try { // Cleanup File.Move(oldExeFile, ExecutableFile); File.Delete(newExeFile); } catch { // Ignored } return; } Logging.LogGenericInfo("Update process finished!"); if (GlobalConfig.AutoRestart) { Logging.LogGenericInfo("Restarting..."); await Utilities.SleepAsync(5000).ConfigureAwait(false); Restart(); } else { Logging.LogGenericInfo("Exiting..."); await Utilities.SleepAsync(5000).ConfigureAwait(false); Exit(); } }
private static void Init(string[] args) { AppDomain.CurrentDomain.UnhandledException += UnhandledExceptionHandler; TaskScheduler.UnobservedTaskException += UnobservedTaskExceptionHandler; string homeDirectory = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location); if (!string.IsNullOrEmpty(homeDirectory)) { Directory.SetCurrentDirectory(homeDirectory); // Allow loading configs from source tree if it's a debug build if (Debugging.IsDebugBuild) { // Common structure is bin/(x64/)Debug/ArchiSteamFarm.exe, so we allow up to 4 directories up for (byte i = 0; i < 4; i++) { Directory.SetCurrentDirectory(".."); if (Directory.Exists(SharedInfo.ConfigDirectory)) { break; } } // If config directory doesn't exist after our adjustment, abort all of that if (!Directory.Exists(SharedInfo.ConfigDirectory)) { Directory.SetCurrentDirectory(homeDirectory); } } } // Parse pre-init args if (args != null) { ParsePreInitArgs(args); } Logging.InitLoggers(); Logging.LogGenericInfo("ASF V" + SharedInfo.Version); if (!Runtime.IsRuntimeSupported) { Logging.LogGenericError("ASF detected unsupported runtime version, program might NOT run correctly in current environment. You're running it at your own risk!"); Thread.Sleep(10000); } InitServices(); // If debugging is on, we prepare debug directory prior to running if (GlobalConfig.Debug) { if (Directory.Exists(SharedInfo.DebugDirectory)) { Directory.Delete(SharedInfo.DebugDirectory, true); Thread.Sleep(1000); // Dirty workaround giving Windows some time to sync } Directory.CreateDirectory(SharedInfo.DebugDirectory); SteamKit2.DebugLog.AddListener(new Debugging.DebugListener()); SteamKit2.DebugLog.Enabled = true; } // Parse post-init args if (args != null) { ParsePostInitArgs(args); } // If we ran ASF as a client, we're done by now if (Mode == EMode.Client) { Exit(); } // From now on it's server mode if (!Directory.Exists(SharedInfo.ConfigDirectory)) { Logging.LogGenericError("Config directory doesn't exist!"); Thread.Sleep(5000); Exit(1); } ASF.CheckForUpdate().Wait(); // Before attempting to connect, initialize our list of CMs Bot.InitializeCMs(GlobalDatabase.CellID, GlobalDatabase.ServerListProvider); foreach (string botName in Directory.EnumerateFiles(SharedInfo.ConfigDirectory, "*.json").Select(Path.GetFileNameWithoutExtension)) { switch (botName) { case SharedInfo.ASF: case "example": case "minimal": continue; } new Bot(botName).Forget(); } if (Bot.Bots.Count == 0) { Logging.LogGenericWarning("No bots are defined, did you forget to configure your ASF?"); } ASF.InitFileWatcher(); }
internal HashSet <Steam.TradeOffer> GetActiveTradeOffers() { if (string.IsNullOrEmpty(Bot.BotConfig.SteamApiKey)) { Logging.LogNullError(nameof(Bot.BotConfig.SteamApiKey), Bot.BotName); return(null); } KeyValue response = null; for (byte i = 0; (i < WebBrowser.MaxRetries) && (response == null); i++) { using (dynamic iEconService = WebAPI.GetInterface("IEconService", Bot.BotConfig.SteamApiKey)) { iEconService.Timeout = Timeout; try { response = iEconService.GetTradeOffers( get_received_offers: 1, active_only: 1, get_descriptions: 1, secure: !Program.GlobalConfig.ForceHttp ); } catch (Exception e) { Logging.LogGenericException(e, Bot.BotName); } } } if (response == null) { Logging.LogGenericWarning("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName); return(null); } Dictionary <ulong, Tuple <uint, Steam.Item.EType> > descriptions = new Dictionary <ulong, Tuple <uint, Steam.Item.EType> >(); foreach (KeyValue description in response["descriptions"].Children) { ulong classID = description["classid"].AsUnsignedLong(); if (classID == 0) { Logging.LogNullError(nameof(classID), Bot.BotName); return(null); } if (descriptions.ContainsKey(classID)) { continue; } uint appID = 0; string hashName = description["market_hash_name"].Value; if (!string.IsNullOrEmpty(hashName)) { appID = GetAppIDFromMarketHashName(hashName); } if (appID == 0) { appID = description["appid"].AsUnsignedInteger(); } Steam.Item.EType type = Steam.Item.EType.Unknown; string descriptionType = description["type"].Value; if (!string.IsNullOrEmpty(descriptionType)) { type = GetItemType(descriptionType); } descriptions[classID] = new Tuple <uint, Steam.Item.EType>(appID, type); } HashSet <Steam.TradeOffer> result = new HashSet <Steam.TradeOffer>(); foreach (KeyValue trade in response["trade_offers_received"].Children) { Steam.TradeOffer.ETradeOfferState state = trade["trade_offer_state"].AsEnum <Steam.TradeOffer.ETradeOfferState>(); if (state == Steam.TradeOffer.ETradeOfferState.Unknown) { Logging.LogNullError(nameof(state)); return(null); } if (state != Steam.TradeOffer.ETradeOfferState.Active) { continue; } ulong tradeOfferID = trade["tradeofferid"].AsUnsignedLong(); if (tradeOfferID == 0) { Logging.LogNullError(nameof(tradeOfferID)); return(null); } uint otherSteamID3 = trade["accountid_other"].AsUnsignedInteger(); if (otherSteamID3 == 0) { Logging.LogNullError(nameof(otherSteamID3)); return(null); } Steam.TradeOffer tradeOffer = new Steam.TradeOffer(tradeOfferID, otherSteamID3, state); List <KeyValue> itemsToGive = trade["items_to_give"].Children; if (itemsToGive.Count > 0) { if (!ParseItems(descriptions, itemsToGive, tradeOffer.ItemsToGive)) { Logging.LogGenericError("Parsing " + nameof(itemsToGive) + " failed!", Bot.BotName); return(null); } } List <KeyValue> itemsToReceive = trade["items_to_receive"].Children; if (itemsToReceive.Count > 0) { if (!ParseItems(descriptions, itemsToReceive, tradeOffer.ItemsToReceive)) { Logging.LogGenericError("Parsing " + nameof(itemsToReceive) + " failed!", Bot.BotName); return(null); } } result.Add(tradeOffer); } return(result); }
private static async void OnChanged(object sender, FileSystemEventArgs e) { if ((sender == null) || (e == null)) { Logging.LogNullError(nameof(sender) + " || " + nameof(e)); return; } string botName = Path.GetFileNameWithoutExtension(e.Name); if (string.IsNullOrEmpty(botName)) { return; } if (botName.Equals(SharedInfo.ASF)) { Logging.LogGenericError("Global config file has been changed, restarting..."); Program.Restart(); return; } Bot bot; if (!Bot.Bots.TryGetValue(botName, out bot)) { return; } DateTime lastWriteTime = File.GetLastWriteTime(e.FullPath); DateTime savedLastWriteTime; if (LastWriteTimes.TryGetValue(bot, out savedLastWriteTime)) { if (savedLastWriteTime >= lastWriteTime) { return; } } LastWriteTimes[bot] = lastWriteTime; // It's entirely possible that some process is still accessing our file, allow at least a second before trying to read it await Task.Delay(1000).ConfigureAwait(false); // It's also possible that we got some other event in the meantime if (LastWriteTimes.TryGetValue(bot, out savedLastWriteTime)) { if (lastWriteTime != savedLastWriteTime) { return; } if (LastWriteTimes.TryRemove(bot, out savedLastWriteTime)) { if (lastWriteTime != savedLastWriteTime) { return; } } } bot.OnNewConfigLoaded(new BotConfigEventArgs(BotConfig.Load(e.FullPath))).Forget(); }