Files
ArchiSteamFarm/ArchiSteamFarm/Program.cs

303 lines
9.7 KiB
C#
Raw Normal View History

2015-10-28 19:21:27 +01:00
/*
_ _ _ ____ _ _____
/ \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
/ _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
2016-01-16 04:21:36 +01:00
Copyright 2015-2016 Łukasz "JustArchi" Domeradzki
2015-10-28 19:21:27 +01:00
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.
*/
2015-10-29 17:36:16 +01:00
using Newtonsoft.Json.Linq;
2015-10-28 19:21:27 +01:00
using System;
2015-10-25 06:16:50 +01:00
using System.IO;
2015-10-29 17:36:16 +01:00
using System.Reflection;
2015-10-25 06:16:50 +01:00
using System.Threading;
2015-10-29 17:36:16 +01:00
using System.Threading.Tasks;
2015-10-25 06:16:50 +01:00
namespace ArchiSteamFarm {
internal static class Program {
2016-01-03 20:36:13 +01:00
internal enum EUserInputType : byte {
Login,
Password,
PhoneNumber,
SMS,
SteamGuard,
SteamParentalPIN,
RevocationCode,
TwoFactorAuthentication,
}
2016-01-03 20:36:13 +01:00
internal enum EMode : byte {
Normal, // Standard most common usage
Client, // WCF client only
Server // Normal + WCF server
}
2015-11-18 14:28:46 +01:00
private const string LatestGithubReleaseURL = "https://api.github.com/repos/JustArchi/ArchiSteamFarm/releases/latest";
internal const string ConfigDirectory = "config";
internal const string LogFile = "log.txt";
2015-10-29 17:36:16 +01:00
private static readonly object ConsoleLock = new object();
2015-11-04 04:31:27 +01:00
private static readonly SemaphoreSlim SteamSemaphore = new SemaphoreSlim(1);
2015-10-31 06:09:03 +01:00
private static readonly ManualResetEvent ShutdownResetEvent = new ManualResetEvent(false);
2015-11-01 02:04:44 +01:00
private static readonly Assembly Assembly = Assembly.GetExecutingAssembly();
private static readonly string ExecutableFile = Assembly.Location;
private static readonly string ExecutableDirectory = Path.GetDirectoryName(ExecutableFile);
2016-01-03 20:36:13 +01:00
private static readonly WCF WCF = new WCF();
2015-10-29 17:36:16 +01:00
internal static readonly string Version = Assembly.GetName().Version.ToString();
2015-12-16 01:17:54 +01:00
2016-01-03 20:36:13 +01:00
private static EMode Mode;
2015-12-19 07:07:08 +01:00
internal static bool ConsoleIsBusy { get; private set; } = false;
2015-11-18 14:28:46 +01:00
2015-10-29 17:36:16 +01:00
private static async Task CheckForUpdate() {
2015-11-25 16:31:39 +01:00
JObject response = await WebBrowser.UrlGetToJObject(LatestGithubReleaseURL).ConfigureAwait(false);
2015-10-29 17:36:16 +01:00
if (response == null) {
return;
}
string remoteVersion = response["tag_name"].ToString();
if (string.IsNullOrEmpty(remoteVersion)) {
return;
}
string localVersion = Version;
2016-01-24 18:08:27 +01:00
Logging.LogGenericInfo("Local version: " + localVersion);
Logging.LogGenericInfo("Remote version: " + remoteVersion);
2015-10-31 06:09:03 +01:00
int comparisonResult = localVersion.CompareTo(remoteVersion);
2015-11-18 14:28:46 +01:00
if (comparisonResult < 0) {
2016-01-24 18:08:27 +01:00
Logging.LogGenericInfo("New version is available!");
Logging.LogGenericInfo("Consider updating yourself!");
2015-11-01 02:04:44 +01:00
await Utilities.SleepAsync(5000).ConfigureAwait(false);
2015-11-18 14:28:46 +01:00
} else if (comparisonResult > 0) {
2016-01-24 18:08:27 +01:00
Logging.LogGenericInfo("You're currently using pre-release version!");
Logging.LogGenericInfo("Be careful!");
2015-10-29 17:36:16 +01:00
}
}
2015-10-25 06:16:50 +01:00
internal static void Exit(int exitCode = 0) {
2015-10-25 06:16:50 +01:00
Environment.Exit(exitCode);
}
internal static void Restart() {
2016-01-03 22:23:30 +01:00
System.Diagnostics.Process.Start(ExecutableFile, string.Join(" ", Environment.GetCommandLineArgs()));
Exit();
2015-11-01 02:04:44 +01:00
}
2015-11-04 04:31:27 +01:00
internal static async Task LimitSteamRequestsAsync() {
await SteamSemaphore.WaitAsync().ConfigureAwait(false);
2016-03-06 22:14:02 +01:00
Task.Run(async () => {
await Utilities.SleepAsync(7000).ConfigureAwait(false); // We must add some delay to not get caught by Steam rate limiter
SteamSemaphore.Release();
2016-03-06 22:14:02 +01:00
}).Forget();
2015-11-04 04:31:27 +01:00
}
internal static string GetUserInput(string botLogin, EUserInputType userInputType, string extraInformation = null) {
2015-10-28 20:01:43 +01:00
string result;
2015-10-25 06:16:50 +01:00
lock (ConsoleLock) {
2015-12-16 01:17:54 +01:00
ConsoleIsBusy = true;
switch (userInputType) {
case EUserInputType.Login:
Console.Write("<" + botLogin + "> Please enter your login: ");
break;
case EUserInputType.Password:
Console.Write("<" + botLogin + "> Please enter your password: ");
break;
case EUserInputType.PhoneNumber:
Console.Write("<" + botLogin + "> Please enter your full phone number (e.g. +1234567890): ");
break;
case EUserInputType.SMS:
Console.Write("<" + botLogin + "> Please enter SMS code sent on your mobile: ");
break;
case EUserInputType.SteamGuard:
Console.Write("<" + botLogin + "> Please enter the auth code sent to your email: ");
break;
case EUserInputType.SteamParentalPIN:
Console.Write("<" + botLogin + "> Please enter steam parental PIN: ");
break;
case EUserInputType.RevocationCode:
Console.WriteLine("<" + botLogin + "> PLEASE WRITE DOWN YOUR REVOCATION CODE: " + extraInformation);
Console.WriteLine("<" + botLogin + "> THIS IS THE ONLY WAY TO NOT GET LOCKED OUT OF YOUR ACCOUNT!");
Console.Write("<" + botLogin + "> Hit enter once ready...");
break;
case EUserInputType.TwoFactorAuthentication:
Console.Write("<" + botLogin + "> Please enter your 2 factor auth code from your authenticator app: ");
break;
2015-10-25 06:16:50 +01:00
}
2015-10-28 20:01:43 +01:00
result = Console.ReadLine();
Console.Clear(); // For security purposes
2015-12-16 01:17:54 +01:00
ConsoleIsBusy = false;
2015-10-25 06:16:50 +01:00
}
2015-12-16 01:17:54 +01:00
return result.Trim(); // Get rid of all whitespace characters
2015-10-25 06:16:50 +01:00
}
2016-01-03 20:36:13 +01:00
internal static void OnBotShutdown() {
foreach (Bot bot in Bot.Bots.Values) {
if (bot.KeepRunning) {
return;
}
2015-10-25 06:16:50 +01:00
}
2016-02-03 05:46:44 +01:00
if (WCF.IsServerRunning()) {
return;
}
Logging.LogGenericInfo("No bots are running, exiting");
ShutdownResetEvent.Set();
2015-10-25 06:16:50 +01:00
}
2015-11-25 16:31:39 +01:00
private static void InitServices() {
2015-12-21 10:19:47 +01:00
Logging.Init();
2015-11-25 16:31:39 +01:00
WebBrowser.Init();
}
2016-01-03 20:36:13 +01:00
private static void ParseArgs(string[] args) {
foreach (string arg in args) {
switch (arg) {
case "--client":
Mode = EMode.Client;
Logging.LogToFile = false;
break;
case "--log":
Logging.LogToFile = true;
break;
case "--no-log":
Logging.LogToFile = false;
break;
case "--server":
Mode = EMode.Server;
WCF.StartServer();
break;
default:
if (arg.StartsWith("--")) {
2016-01-24 18:08:27 +01:00
Logging.LogGenericWarning("Unrecognized parameter: " + arg);
2016-01-03 20:36:13 +01:00
continue;
}
if (Mode != EMode.Client) {
2016-01-24 18:08:27 +01:00
Logging.LogGenericWarning("Ignoring command because --client wasn't specified: " + arg);
2016-01-03 20:36:13 +01:00
continue;
}
2016-01-24 18:08:27 +01:00
Logging.LogGenericInfo("Command sent: \"" + arg + "\"");
2016-01-04 00:02:18 +01:00
// We intentionally execute this async block synchronously
2016-01-24 18:08:27 +01:00
Logging.LogGenericInfo("Response received: \"" + WCF.SendCommand(arg) + "\"");
2016-01-04 00:02:18 +01:00
/*
Task.Run(async () => {
Logging.LogGenericNotice("WCF", "Response received: " + await WCF.SendCommand(arg).ConfigureAwait(false));
}).Wait();
*/
2016-01-03 20:36:13 +01:00
break;
}
}
}
2016-01-09 19:54:08 +01:00
private static void UnhandledExceptionHandler(object sender, UnhandledExceptionEventArgs args) {
if (sender == null || args == null) {
return;
}
2016-01-24 18:08:27 +01:00
Logging.LogGenericException((Exception) args.ExceptionObject);
2016-01-09 19:54:08 +01:00
}
2015-10-25 06:16:50 +01:00
private static void Main(string[] args) {
2016-02-24 06:20:35 +01:00
AppDomain.CurrentDomain.UnhandledException += UnhandledExceptionHandler;
2016-01-09 19:54:08 +01:00
2016-01-24 18:08:27 +01:00
Logging.LogGenericInfo("Archi's Steam Farm, version " + Version);
Directory.SetCurrentDirectory(ExecutableDirectory);
2015-11-25 16:31:39 +01:00
InitServices();
2015-11-01 02:04:44 +01:00
// 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
2015-11-01 02:04:44 +01:00
for (var i = 0; i < 4; i++) {
Directory.SetCurrentDirectory("..");
if (Directory.Exists(ConfigDirectory)) {
2015-11-01 02:04:44 +01:00
break;
}
}
// If config directory doesn't exist after our adjustment, abort all of that
if (!Directory.Exists(ConfigDirectory)) {
Directory.SetCurrentDirectory(ExecutableDirectory);
}
2015-11-01 02:04:44 +01:00
}
2016-01-03 20:36:13 +01:00
// 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)) {
2016-01-24 18:08:27 +01:00
Logging.LogGenericError("Config directory doesn't exist!");
2016-01-01 12:49:41 +01:00
Thread.Sleep(5000);
Exit(1);
2015-10-31 06:09:03 +01:00
}
2015-10-25 06:16:50 +01:00
2016-01-24 17:38:45 +01:00
// Before attempting to connect, initialize our list of CMs
2016-02-20 17:07:05 +01:00
Bot.RefreshCMs().Wait();
2016-01-24 17:38:45 +01:00
2016-03-06 02:20:41 +01:00
foreach (var configFile in Directory.EnumerateFiles(ConfigDirectory, "*.json")) {
string botName = Path.GetFileNameWithoutExtension(configFile);
Bot bot = new Bot(botName);
if (!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);
2016-03-06 02:20:41 +01:00
Logging.LogGenericWarning("Found legacy " + botName + ".xml config file, it will now be converted to new ASF V2.0 format!");
2016-02-12 16:06:57 +01:00
Bot bot = new Bot(botName);
2016-03-06 02:20:41 +01:00
if (!bot.BotConfig.Enabled) {
2016-01-24 18:08:27 +01:00
Logging.LogGenericInfo("Not starting this instance because it's disabled in config file", botName);
2015-10-25 06:16:50 +01:00
}
}
2016-03-06 02:20:41 +01:00
// CONVERSION END
2015-10-25 06:16:50 +01:00
// Check if we got any bots running
2015-12-12 08:04:41 +01:00
OnBotShutdown();
// Wait for signal to shutdown
ShutdownResetEvent.WaitOne();
2016-01-03 20:36:13 +01:00
// We got a signal to shutdown, consider giving user some time to read the message
Thread.Sleep(5000);
2016-01-03 20:36:13 +01:00
// This is over, cleanup only now
WCF.StopServer();
2015-10-25 06:16:50 +01:00
}
}
}