diff --git a/ArchiSteamFarm/ArchiSteamFarm.csproj b/ArchiSteamFarm/ArchiSteamFarm.csproj
index 8ee1f151f..1aad44939 100644
--- a/ArchiSteamFarm/ArchiSteamFarm.csproj
+++ b/ArchiSteamFarm/ArchiSteamFarm.csproj
@@ -106,6 +106,7 @@
+
@@ -120,6 +121,9 @@
+
+ PreserveNewest
+
PreserveNewest
diff --git a/ArchiSteamFarm/ArchiWebHandler.cs b/ArchiSteamFarm/ArchiWebHandler.cs
index 57b8f7646..526bf2972 100644
--- a/ArchiSteamFarm/ArchiWebHandler.cs
+++ b/ArchiSteamFarm/ArchiWebHandler.cs
@@ -34,13 +34,17 @@ using System.Threading.Tasks;
namespace ArchiSteamFarm {
internal sealed class ArchiWebHandler {
- private const int Timeout = 1000 * WebBrowser.HttpTimeout; // In miliseconds
+ private static int Timeout;
private readonly Bot Bot;
private readonly Dictionary Cookie = new Dictionary(4);
private ulong SteamID;
+ internal static void Init() {
+ Timeout = Program.GlobalConfig.HttpTimeout * 1000;
+ }
+
internal ArchiWebHandler(Bot bot) {
Bot = bot;
}
diff --git a/ArchiSteamFarm/BotConfig.cs b/ArchiSteamFarm/BotConfig.cs
index a0eaa9c84..e2f6d7af5 100644
--- a/ArchiSteamFarm/BotConfig.cs
+++ b/ArchiSteamFarm/BotConfig.cs
@@ -30,8 +30,6 @@ using System.Xml;
namespace ArchiSteamFarm {
internal sealed class BotConfig {
- internal static readonly HashSet GlobalBlacklist = new HashSet { 303700, 335590, 368020, 425280 };
-
[JsonProperty(Required = Required.DisallowNull)]
internal bool Enabled { get; private set; } = false;
diff --git a/ArchiSteamFarm/CardsFarmer.cs b/ArchiSteamFarm/CardsFarmer.cs
index b0d25ecb4..1c07b9dab 100755
--- a/ArchiSteamFarm/CardsFarmer.cs
+++ b/ArchiSteamFarm/CardsFarmer.cs
@@ -329,7 +329,7 @@ namespace ArchiSteamFarm {
continue;
}
- if (BotConfig.GlobalBlacklist.Contains(appID)) {
+ if (GlobalConfig.GlobalBlacklist.Contains(appID) || Program.GlobalConfig.Blacklist.Contains(appID)) {
continue;
}
diff --git a/ArchiSteamFarm/GlobalConfig.cs b/ArchiSteamFarm/GlobalConfig.cs
new file mode 100644
index 000000000..fd1148bc4
--- /dev/null
+++ b/ArchiSteamFarm/GlobalConfig.cs
@@ -0,0 +1,83 @@
+/*
+ _ _ _ ____ _ _____
+ / \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
+ / _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
+ / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
+/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
+
+ Copyright 2015-2016 Łukasz "JustArchi" Domeradzki
+ Contact: JustArchi@JustArchi.net
+
+ 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
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ 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.
+
+*/
+
+using Newtonsoft.Json;
+using SteamAuth;
+using System;
+using System.Collections.Generic;
+using System.IO;
+
+namespace ArchiSteamFarm {
+ internal sealed class GlobalConfig {
+ internal enum EUpdateChannel : byte {
+ Unknown,
+ Stable,
+ Experimental
+ }
+
+ // This is hardcoded blacklist which should not be possible to change
+ internal static readonly HashSet GlobalBlacklist = new HashSet { 303700, 335590, 368020, 425280 };
+
+ [JsonProperty(Required = Required.DisallowNull)]
+ internal bool AutoUpdates { get; private set; } = true;
+
+ [JsonProperty(Required = Required.DisallowNull)]
+ internal EUpdateChannel UpdateChannel { get; private set; } = GlobalConfig.EUpdateChannel.Stable;
+
+ [JsonProperty(Required = Required.DisallowNull)]
+ internal byte HttpTimeout { get; private set; } = 30;
+
+ [JsonProperty(Required = Required.DisallowNull)]
+ internal byte RequestLimiterDelay { get; private set; } = 7;
+
+ [JsonProperty(Required = Required.DisallowNull)]
+ internal string WCFHostname { get; private set; } = "localhost";
+
+ [JsonProperty(Required = Required.DisallowNull)]
+ internal ushort WCFPort { get; private set; } = 1242;
+
+ [JsonProperty(Required = Required.DisallowNull)]
+ internal HashSet Blacklist { get; private set; } = new HashSet(GlobalBlacklist);
+
+ internal static GlobalConfig Load() {
+ string filePath = Path.Combine(Program.ConfigDirectory, Program.GlobalConfigFile);
+ if (!File.Exists(filePath)) {
+ return null;
+ }
+
+ GlobalConfig globalConfig;
+ try {
+ globalConfig = JsonConvert.DeserializeObject(File.ReadAllText(filePath));
+ } catch (Exception e) {
+ Logging.LogGenericException(e);
+ return null;
+ }
+
+ return globalConfig;
+ }
+
+ // This constructor is used only by deserializer
+ private GlobalConfig() { }
+ }
+}
diff --git a/ArchiSteamFarm/Logging.cs b/ArchiSteamFarm/Logging.cs
index d69eeece7..6ab73c278 100644
--- a/ArchiSteamFarm/Logging.cs
+++ b/ArchiSteamFarm/Logging.cs
@@ -31,17 +31,18 @@ namespace ArchiSteamFarm {
internal static class Logging {
private static readonly object FileLock = new object();
- internal static bool LogToFile { get; set; } = false;
+ internal static bool? LogToFile { get; set; } = null;
internal static void Init() {
+ if (!LogToFile.HasValue) {
+ LogToFile = true;
+ }
+
lock (FileLock) {
try {
File.Delete(Program.LogFile);
} catch (Exception e) {
- bool logToFile = LogToFile;
- LogToFile = false;
LogGenericException(e);
- LogToFile = logToFile;
}
}
}
@@ -121,15 +122,14 @@ namespace ArchiSteamFarm {
Console.Write(loggedMessage);
}
- if (LogToFile) {
+ if (LogToFile.GetValueOrDefault()) {
lock (FileLock) {
try {
File.AppendAllText(Program.LogFile, loggedMessage);
} catch (Exception e) {
- bool logToFile = LogToFile;
LogToFile = false;
LogGenericException(e);
- LogToFile = logToFile;
+ LogToFile = true;
}
}
}
diff --git a/ArchiSteamFarm/Program.cs b/ArchiSteamFarm/Program.cs
index 70f36cc48..f07ce9357 100644
--- a/ArchiSteamFarm/Program.cs
+++ b/ArchiSteamFarm/Program.cs
@@ -51,6 +51,7 @@ namespace ArchiSteamFarm {
private const string LatestGithubReleaseURL = "https://api.github.com/repos/JustArchi/ArchiSteamFarm/releases/latest";
internal const string ConfigDirectory = "config";
internal const string LogFile = "log.txt";
+ internal const string GlobalConfigFile = "ASF.json";
private static readonly object ConsoleLock = new object();
private static readonly SemaphoreSlim SteamSemaphore = new SemaphoreSlim(1);
@@ -62,10 +63,11 @@ namespace ArchiSteamFarm {
internal static readonly string Version = Assembly.GetName().Version.ToString();
- private static EMode Mode;
-
+ internal static GlobalConfig GlobalConfig { get; private set; }
internal static bool ConsoleIsBusy { get; private set; } = false;
+ private static EMode Mode = EMode.Normal;
+
private static async Task CheckForUpdate() {
JObject response = await WebBrowser.UrlGetToJObject(LatestGithubReleaseURL).ConfigureAwait(false);
if (response == null) {
@@ -105,7 +107,7 @@ namespace ArchiSteamFarm {
internal static async Task LimitSteamRequestsAsync() {
await SteamSemaphore.WaitAsync().ConfigureAwait(false);
Task.Run(async () => {
- await Utilities.SleepAsync(7000).ConfigureAwait(false); // We must add some delay to not get caught by Steam rate limiter
+ await Utilities.SleepAsync(GlobalConfig.RequestLimiterDelay * 1000).ConfigureAwait(false);
SteamSemaphore.Release();
}).Forget();
}
@@ -166,8 +168,16 @@ namespace ArchiSteamFarm {
}
private static void InitServices() {
- Logging.Init();
+ GlobalConfig = GlobalConfig.Load();
+ if (GlobalConfig == null) {
+ Logging.LogGenericError("Global config could not be loaded, please make sure that ASF.db exists and is valid!");
+ Thread.Sleep(5000);
+ Exit(1);
+ }
+
+ ArchiWebHandler.Init();
WebBrowser.Init();
+ WCF.Init();
}
private static void ParseArgs(string[] args) {
@@ -244,11 +254,7 @@ namespace ArchiSteamFarm {
}
}
- // By default we're operating on normal mode
- Mode = EMode.Normal;
- Logging.LogToFile = true;
-
- // But that can be overriden by arguments
+ // Parse args
ParseArgs(args);
// If we ran ASF as a client, we're done by now
@@ -256,7 +262,8 @@ namespace ArchiSteamFarm {
return;
}
- Task.Run(async () => await CheckForUpdate().ConfigureAwait(false)).Wait();
+ // From now on it's server mode
+ Logging.Init();
if (!Directory.Exists(ConfigDirectory)) {
Logging.LogGenericError("Config directory doesn't exist!");
@@ -264,11 +271,19 @@ namespace ArchiSteamFarm {
Exit(1);
}
+ Task.Run(async () => await CheckForUpdate().ConfigureAwait(false)).Wait();
+
// Before attempting to connect, initialize our list of CMs
Bot.RefreshCMs().Wait();
+ string globalConfigName = GlobalConfigFile.Substring(0, GlobalConfigFile.LastIndexOf('.'));
+
foreach (var configFile in Directory.EnumerateFiles(ConfigDirectory, "*.json")) {
string botName = Path.GetFileNameWithoutExtension(configFile);
+ if (botName.Equals(globalConfigName)) {
+ continue;
+ }
+
Bot bot = new Bot(botName);
if (!bot.BotConfig.Enabled) {
Logging.LogGenericInfo("Not starting this instance because it's disabled in config file", botName);
diff --git a/ArchiSteamFarm/Trading.cs b/ArchiSteamFarm/Trading.cs
index fcd9bb2ec..f5b74b4a5 100644
--- a/ArchiSteamFarm/Trading.cs
+++ b/ArchiSteamFarm/Trading.cs
@@ -40,7 +40,7 @@ namespace ArchiSteamFarm {
internal static async Task LimitInventoryRequestsAsync() {
await InventorySemaphore.WaitAsync().ConfigureAwait(false);
Task.Run(async () => {
- await Utilities.SleepAsync(3000).ConfigureAwait(false); // We must add some delay to not get caught by Steam rate limiter
+ await Utilities.SleepAsync(Program.GlobalConfig.RequestLimiterDelay * 1000).ConfigureAwait(false);
InventorySemaphore.Release();
}).Forget();
}
diff --git a/ArchiSteamFarm/WCF.cs b/ArchiSteamFarm/WCF.cs
index 9ef7073dd..dd2c360de 100644
--- a/ArchiSteamFarm/WCF.cs
+++ b/ArchiSteamFarm/WCF.cs
@@ -35,11 +35,15 @@ namespace ArchiSteamFarm {
internal sealed class WCF : IWCF {
- private const string URL = "http://localhost:1242/ASF"; // 1242 = 1024 + A(65) + S(83) + F(70)
+ private static string URL = "http://localhost:1242/ASF"; // 1242 = 1024 + A(65) + S(83) + F(70)
private ServiceHost ServiceHost;
private Client Client;
+ internal static void Init() {
+ URL = "http://" + Program.GlobalConfig.WCFHostname + ":" + Program.GlobalConfig.WCFPort + "/ASF";
+ }
+
internal bool IsServerRunning() {
return ServiceHost != null;
}
diff --git a/ArchiSteamFarm/WebBrowser.cs b/ArchiSteamFarm/WebBrowser.cs
index 7e4892952..993966955 100644
--- a/ArchiSteamFarm/WebBrowser.cs
+++ b/ArchiSteamFarm/WebBrowser.cs
@@ -33,19 +33,20 @@ using System.Threading.Tasks;
namespace ArchiSteamFarm {
internal static class WebBrowser {
- internal const byte HttpTimeout = 180; // In seconds, how long we can wait for server's response
internal const byte MaxConnections = 10; // Defines maximum number of connections per ServicePoint. Be careful, as it also defines maximum number of sockets in CLOSE_WAIT state
internal const byte MaxIdleTime = 15; // In seconds, how long socket is allowed to stay in CLOSE_WAIT state after there are no connections to it
internal const byte MaxRetries = 5; // Defines maximum number of retries, UrlRequest() does not handle retry by itself (it's app responsibility)
private static readonly string DefaultUserAgent = "ArchiSteamFarm/" + Program.Version;
- private static readonly HttpClient HttpClient = new HttpClient(new HttpClientHandler {
- UseCookies = false
- }) {
- Timeout = TimeSpan.FromSeconds(HttpTimeout)
- };
+ private static HttpClient HttpClient;
internal static void Init() {
+ HttpClient = new HttpClient(new HttpClientHandler {
+ UseCookies = false
+ }) {
+ Timeout = TimeSpan.FromSeconds(Program.GlobalConfig.HttpTimeout)
+ };
+
// Most web services expect that UserAgent is set, so we declare it globally
// Any request can override that on as-needed basis (see: RequestOptions.FakeUserAgent)
HttpClient.DefaultRequestHeaders.UserAgent.ParseAdd(DefaultUserAgent);