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 .
* /
using System ;
2016-03-09 03:10:33 +01:00
using System.Collections.Generic ;
using System.Diagnostics ;
2015-10-25 06:16:50 +01:00
using System.IO ;
2016-03-09 03:52:04 +01:00
using System.Linq ;
2015-10-29 17:36:16 +01:00
using System.Reflection ;
2016-06-27 23:39:47 -04:00
using System.ServiceProcess ;
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-08-06 22:16:46 +02:00
internal enum EMode : byte {
2016-01-03 20:36:13 +01:00
Normal , // Standard most common usage
Client , // WCF client only
Server // Normal + WCF server
}
2016-01-01 12:46:49 +01:00
private static readonly object ConsoleLock = new object ( ) ;
2016-04-18 18:43:58 +02:00
private static readonly ManualResetEventSlim ShutdownResetEvent = new ManualResetEventSlim ( false ) ;
2016-11-06 17:08:44 +01:00
private static readonly WCF WCF = new WCF ( ) ;
2015-10-29 17:36:16 +01:00
2016-07-04 19:22:46 +02:00
internal static bool IsRunningAsService { get ; private set ; }
2016-08-06 22:16:46 +02:00
internal static EMode Mode { get ; private set ; } = EMode . Normal ;
2016-03-06 23:28:56 +01:00
internal static GlobalConfig GlobalConfig { get ; private set ; }
2016-03-07 02:39:55 +01:00
internal static GlobalDatabase GlobalDatabase { get ; private set ; }
2016-08-02 06:04:44 +02:00
internal static WebBrowser WebBrowser { get ; private set ; }
2015-11-18 14:28:46 +01:00
2016-10-07 00:04:08 +02:00
private static bool ShutdownSequenceInitialized ;
2016-10-27 22:51:44 +02:00
internal static bool IsWCFRunning = > WCF . IsServerRunning ;
2016-07-18 11:03:13 +02:00
internal static void Exit ( byte exitCode = 0 ) {
2016-06-28 07:14:51 +02:00
Shutdown ( ) ;
2016-04-02 13:08:43 +02:00
Environment . Exit ( exitCode ) ;
}
2016-03-19 15:31:54 +01:00
internal static void Restart ( ) {
2016-10-31 06:29:48 +01:00
if ( ! InitShutdownSequence ( ) ) {
return ;
}
2016-07-18 11:03:13 +02:00
2016-03-09 03:10:33 +01:00
try {
2016-07-25 06:08:45 +02:00
Process . Start ( Assembly . GetEntryAssembly ( ) . Location , string . Join ( " " , Environment . GetCommandLineArgs ( ) . Skip ( 1 ) ) ) ;
2016-03-09 03:10:33 +01:00
} catch ( Exception e ) {
2016-11-06 12:06:02 +01:00
ASF . ArchiLogger . LogGenericException ( e ) ;
2016-03-09 03:10:33 +01:00
}
2016-04-07 01:39:02 +02:00
2016-10-31 06:29:48 +01:00
ShutdownResetEvent . Set ( ) ;
2016-07-18 11:03:13 +02:00
Environment . Exit ( 0 ) ;
2015-11-01 02:04:44 +01:00
}
2016-08-02 06:04:44 +02:00
internal static string GetUserInput ( SharedInfo . EUserInputType userInputType , string botName = SharedInfo . ASF , string extraInformation = null ) {
if ( userInputType = = SharedInfo . EUserInputType . Unknown ) {
2016-03-12 06:01:55 +01:00
return null ;
}
2016-06-28 10:36:28 +02:00
if ( GlobalConfig . Headless | | ! Runtime . IsUserInteractive ) {
2016-11-06 12:06:02 +01:00
ASF . ArchiLogger . LogGenericWarning ( "Received a request for user input, but process is running in headless mode!" ) ;
2016-04-06 16:37:45 +02:00
return null ;
}
2015-10-28 20:01:43 +01:00
string result ;
2015-10-25 06:16:50 +01:00
lock ( ConsoleLock ) {
2016-07-04 19:22:46 +02:00
Logging . OnUserInputStart ( ) ;
2015-10-28 23:29:50 +01:00
switch ( userInputType ) {
2016-08-02 06:04:44 +02:00
case SharedInfo . EUserInputType . DeviceID :
2016-05-30 01:57:06 +02:00
Console . Write ( "<" + botName + "> Please enter your Device ID (including \"android:\"): " ) ;
2016-03-12 05:58:51 +01:00
break ;
2016-08-02 06:04:44 +02:00
case SharedInfo . EUserInputType . Login :
2016-05-30 01:57:06 +02:00
Console . Write ( "<" + botName + "> Please enter your login: " ) ;
2015-10-28 23:29:50 +01:00
break ;
2016-08-02 06:04:44 +02:00
case SharedInfo . EUserInputType . Password :
2016-05-30 01:57:06 +02:00
Console . Write ( "<" + botName + "> Please enter your password: " ) ;
2015-10-28 23:29:50 +01:00
break ;
2016-08-02 06:04:44 +02:00
case SharedInfo . EUserInputType . PhoneNumber :
2016-05-30 01:57:06 +02:00
Console . Write ( "<" + botName + "> Please enter your full phone number (e.g. +1234567890): " ) ;
2015-12-11 22:53:28 +01:00
break ;
2016-08-02 06:04:44 +02:00
case SharedInfo . EUserInputType . SMS :
2016-05-30 01:57:06 +02:00
Console . Write ( "<" + botName + "> Please enter SMS code sent on your mobile: " ) ;
2015-12-11 22:53:28 +01:00
break ;
2016-08-02 06:04:44 +02:00
case SharedInfo . EUserInputType . SteamGuard :
2016-05-30 01:57:06 +02:00
Console . Write ( "<" + botName + "> Please enter the auth code sent to your email: " ) ;
2015-10-29 01:01:31 +01:00
break ;
2016-08-02 06:04:44 +02:00
case SharedInfo . EUserInputType . SteamParentalPIN :
2016-05-30 01:57:06 +02:00
Console . Write ( "<" + botName + "> Please enter steam parental PIN: " ) ;
2015-10-28 23:29:50 +01:00
break ;
2016-08-02 06:04:44 +02:00
case SharedInfo . EUserInputType . RevocationCode :
2016-05-30 01:57:06 +02:00
Console . WriteLine ( "<" + botName + "> PLEASE WRITE DOWN YOUR REVOCATION CODE: " + extraInformation ) ;
Console . Write ( "<" + botName + "> Hit enter once ready..." ) ;
2015-12-11 22:53:28 +01:00
break ;
2016-08-02 06:04:44 +02:00
case SharedInfo . EUserInputType . TwoFactorAuthentication :
2016-05-30 01:57:06 +02:00
Console . Write ( "<" + botName + "> Please enter your 2 factor auth code from your authenticator app: " ) ;
2016-03-22 17:26:39 +01:00
break ;
2016-08-02 06:04:44 +02:00
case SharedInfo . EUserInputType . WCFHostname :
2016-05-30 01:57:06 +02:00
Console . Write ( "<" + botName + "> Please enter your WCF hostname: " ) ;
2015-10-28 23:29:50 +01:00
break ;
2016-03-12 06:01:55 +01:00
default :
2016-05-30 01:57:06 +02:00
Console . Write ( "<" + botName + "> Please enter not documented yet value of \"" + userInputType + "\": " ) ;
2016-03-12 06:01:55 +01:00
break ;
2015-10-25 06:16:50 +01:00
}
2016-05-13 06:32:42 +02:00
2015-10-28 20:01:43 +01:00
result = Console . ReadLine ( ) ;
2016-05-30 01:57:06 +02:00
2016-04-13 16:48:11 +02:00
if ( ! Console . IsOutputRedirected ) {
Console . Clear ( ) ; // For security purposes
}
2016-05-30 01:57:06 +02:00
2016-07-04 19:22:46 +02:00
Logging . OnUserInputEnd ( ) ;
2015-10-25 06:16:50 +01:00
}
2015-12-16 01:17:54 +01:00
2016-05-30 01:57:06 +02:00
return ! string . IsNullOrEmpty ( result ) ? result . Trim ( ) : null ;
2015-10-25 06:16:50 +01:00
}
2016-10-31 06:29:48 +01:00
internal static void Shutdown ( ) {
2016-07-19 22:18:00 +02:00
if ( ! InitShutdownSequence ( ) ) {
2016-06-28 07:50:44 +02:00
return ;
}
2016-06-28 07:14:51 +02:00
ShutdownResetEvent . Set ( ) ;
2016-07-19 22:18:00 +02:00
}
private static bool InitShutdownSequence ( ) {
if ( ShutdownSequenceInitialized ) {
return false ;
}
ShutdownSequenceInitialized = true ;
2016-06-28 07:14:51 +02:00
2016-07-19 22:18:00 +02:00
WCF . StopServer ( ) ;
2016-06-28 07:14:51 +02:00
foreach ( Bot bot in Bot . Bots . Values ) {
bot . Stop ( ) ;
}
2016-07-19 22:18:00 +02:00
return true ;
2016-06-28 07:14:51 +02:00
}
2015-11-25 16:31:39 +01:00
private static void InitServices ( ) {
2016-08-02 06:04:44 +02:00
string globalConfigFile = Path . Combine ( SharedInfo . ConfigDirectory , SharedInfo . GlobalConfigFileName ) ;
2016-07-31 17:38:14 +02:00
GlobalConfig = GlobalConfig . Load ( globalConfigFile ) ;
2016-03-06 23:28:56 +01:00
if ( GlobalConfig = = null ) {
2016-11-06 12:06:02 +01:00
ASF . ArchiLogger . LogGenericError ( "Global config could not be loaded, please make sure that " + globalConfigFile + " exists and is valid! Did you forget to read wiki?" ) ;
2016-03-06 23:28:56 +01:00
Thread . Sleep ( 5000 ) ;
2016-04-02 13:08:43 +02:00
Exit ( 1 ) ;
2016-03-06 23:28:56 +01:00
}
2016-08-02 06:04:44 +02:00
string globalDatabaseFile = Path . Combine ( SharedInfo . ConfigDirectory , SharedInfo . GlobalDatabaseFileName ) ;
2016-07-31 17:38:14 +02:00
GlobalDatabase = GlobalDatabase . Load ( globalDatabaseFile ) ;
2016-03-07 02:39:55 +01:00
if ( GlobalDatabase = = null ) {
2016-11-06 12:06:02 +01:00
ASF . ArchiLogger . LogGenericError ( "Global database could not be loaded, if issue persists, please remove " + globalDatabaseFile + " in order to recreate database!" ) ;
2016-03-07 02:39:55 +01:00
Thread . Sleep ( 5000 ) ;
2016-04-02 13:08:43 +02:00
Exit ( 1 ) ;
2016-03-07 02:39:55 +01:00
}
2016-03-06 23:28:56 +01:00
ArchiWebHandler . Init ( ) ;
2015-11-25 16:31:39 +01:00
WebBrowser . Init ( ) ;
2016-03-06 23:28:56 +01:00
WCF . Init ( ) ;
2016-04-12 16:58:45 +02:00
2016-11-06 12:06:02 +01:00
WebBrowser = new WebBrowser ( ASF . ArchiLogger ) ;
2015-11-25 16:31:39 +01:00
}
2016-07-24 00:36:15 +02:00
private static void ParsePreInitArgs ( IEnumerable < string > args ) {
if ( args = = null ) {
2016-11-06 12:06:02 +01:00
ASF . ArchiLogger . LogNullError ( nameof ( args ) ) ;
2016-07-24 00:36:15 +02:00
return ;
}
foreach ( string arg in args ) {
switch ( arg ) {
case "" :
break ;
2016-08-06 22:16:46 +02:00
case "--client" :
Mode = EMode . Client ;
break ;
case "--server" :
Mode = EMode . Server ;
break ;
2016-07-24 00:36:15 +02:00
default :
if ( arg . StartsWith ( "--" , StringComparison . Ordinal ) ) {
if ( arg . StartsWith ( "--path=" , StringComparison . Ordinal ) & & ( arg . Length > 7 ) ) {
Directory . SetCurrentDirectory ( arg . Substring ( 7 ) ) ;
}
}
break ;
}
}
}
private static void ParsePostInitArgs ( IEnumerable < string > args ) {
2016-05-13 06:32:42 +02:00
if ( args = = null ) {
2016-11-06 12:06:02 +01:00
ASF . ArchiLogger . LogNullError ( nameof ( args ) ) ;
2016-05-13 06:32:42 +02:00
return ;
}
2016-01-03 20:36:13 +01:00
foreach ( string arg in args ) {
switch ( arg ) {
2016-05-30 01:57:06 +02:00
case "" :
break ;
2016-01-03 20:36:13 +01:00
case "--client" :
Mode = EMode . Client ;
break ;
case "--server" :
Mode = EMode . Server ;
WCF . StartServer ( ) ;
break ;
default :
2016-05-13 06:32:42 +02:00
if ( arg . StartsWith ( "--" , StringComparison . Ordinal ) ) {
2016-06-28 09:26:11 +02:00
if ( arg . StartsWith ( "--cryptkey=" , StringComparison . Ordinal ) & & ( arg . Length > 11 ) ) {
CryptoHelper . SetEncryptionKey ( arg . Substring ( 11 ) ) ;
2016-06-28 09:24:32 +02:00
}
break ;
2016-01-03 20:36:13 +01:00
}
if ( Mode ! = EMode . Client ) {
2016-11-06 12:06:02 +01:00
ASF . ArchiLogger . LogGenericWarning ( "Ignoring command because --client wasn't specified: " + arg ) ;
2016-06-28 09:24:32 +02:00
break ;
2016-01-03 20:36:13 +01:00
}
2016-11-06 12:06:02 +01:00
ASF . ArchiLogger . LogGenericInfo ( "Command sent: " + arg ) ;
ASF . ArchiLogger . LogGenericInfo ( "Response received: " + WCF . SendCommand ( arg ) ) ;
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 ) {
2016-07-10 20:17:35 +02:00
if ( args ? . ExceptionObject = = null ) {
2016-11-06 12:06:02 +01:00
ASF . ArchiLogger . LogNullError ( nameof ( args ) + " || " + nameof ( args . ExceptionObject ) ) ;
2016-01-09 19:54:08 +01:00
return ;
}
2016-11-06 12:06:02 +01:00
ASF . ArchiLogger . LogFatalException ( ( Exception ) args . ExceptionObject ) ;
2016-01-09 19:54:08 +01:00
}
2016-03-14 20:51:29 +01:00
private static void UnobservedTaskExceptionHandler ( object sender , UnobservedTaskExceptionEventArgs args ) {
2016-07-10 20:17:35 +02:00
if ( args ? . Exception = = null ) {
2016-11-06 12:06:02 +01:00
ASF . ArchiLogger . LogNullError ( nameof ( args ) + " || " + nameof ( args . Exception ) ) ;
2016-03-14 20:51:29 +01:00
return ;
}
2016-11-06 12:06:02 +01:00
ASF . ArchiLogger . LogFatalException ( args . Exception ) ;
2016-03-14 20:51:29 +01:00
}
2016-07-24 00:36:15 +02:00
private static void Init ( string [ ] args ) {
2016-02-24 06:20:35 +01:00
AppDomain . CurrentDomain . UnhandledException + = UnhandledExceptionHandler ;
2016-03-14 20:51:29 +01:00
TaskScheduler . UnobservedTaskException + = UnobservedTaskExceptionHandler ;
2016-01-09 19:54:08 +01:00
2016-07-25 06:08:45 +02:00
string homeDirectory = Path . GetDirectoryName ( Assembly . GetEntryAssembly ( ) . Location ) ;
if ( ! string . IsNullOrEmpty ( homeDirectory ) ) {
Directory . SetCurrentDirectory ( homeDirectory ) ;
2015-11-25 16:31:39 +01:00
2016-07-25 06:08:45 +02:00
// Allow loading configs from source tree if it's a debug build
if ( Debugging . IsDebugBuild ) {
2016-01-01 12:46:49 +01:00
2016-07-25 06:08:45 +02:00
// 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 ( ".." ) ;
2016-08-02 06:04:44 +02:00
if ( Directory . Exists ( SharedInfo . ConfigDirectory ) ) {
2016-07-25 06:08:45 +02:00
break ;
}
2015-11-01 02:04:44 +01:00
}
2016-01-01 12:46:49 +01:00
2016-07-25 06:08:45 +02:00
// If config directory doesn't exist after our adjustment, abort all of that
2016-08-02 06:04:44 +02:00
if ( ! Directory . Exists ( SharedInfo . ConfigDirectory ) ) {
2016-07-25 06:08:45 +02:00
Directory . SetCurrentDirectory ( homeDirectory ) ;
}
2016-01-01 12:46:49 +01:00
}
2015-11-01 02:04:44 +01:00
}
2016-08-06 22:16:46 +02:00
// Parse pre-init args
if ( args ! = null ) {
ParsePreInitArgs ( args ) ;
}
Logging . InitLoggers ( ) ;
2016-11-06 12:06:02 +01:00
ASF . ArchiLogger . LogGenericInfo ( "ASF V" + SharedInfo . Version ) ;
2016-08-04 22:44:17 +02:00
if ( ! Runtime . IsRuntimeSupported ) {
2016-11-06 12:06:02 +01:00
ASF . ArchiLogger . LogGenericError ( "ASF detected unsupported runtime version, program might NOT run correctly in current environment. You're running it at your own risk!" ) ;
2016-08-04 22:44:17 +02:00
Thread . Sleep ( 10000 ) ;
}
2016-07-08 09:02:53 +02:00
InitServices ( ) ;
2016-03-13 21:22:52 +01:00
// If debugging is on, we prepare debug directory prior to running
if ( GlobalConfig . Debug ) {
2016-08-02 06:04:44 +02:00
if ( Directory . Exists ( SharedInfo . DebugDirectory ) ) {
Directory . Delete ( SharedInfo . DebugDirectory , true ) ;
2016-03-13 21:22:52 +01:00
Thread . Sleep ( 1000 ) ; // Dirty workaround giving Windows some time to sync
}
2016-08-31 13:56:09 +02:00
2016-08-02 06:04:44 +02:00
Directory . CreateDirectory ( SharedInfo . DebugDirectory ) ;
2016-03-13 21:22:52 +01:00
2016-08-31 13:56:09 +02:00
SteamKit2 . DebugLog . AddListener ( new Debugging . DebugListener ( ) ) ;
2016-03-13 21:22:52 +01:00
SteamKit2 . DebugLog . Enabled = true ;
}
2016-07-24 00:36:15 +02:00
// Parse post-init args
2016-05-30 01:57:06 +02:00
if ( args ! = null ) {
2016-07-24 00:36:15 +02:00
ParsePostInitArgs ( args ) ;
2016-05-30 01:57:06 +02:00
}
2016-01-03 20:36:13 +01:00
// If we ran ASF as a client, we're done by now
if ( Mode = = EMode . Client ) {
2016-04-02 13:08:43 +02:00
Exit ( ) ;
2016-01-03 20:36:13 +01:00
}
2016-03-06 23:28:56 +01:00
// From now on it's server mode
2016-08-02 06:04:44 +02:00
if ( ! Directory . Exists ( SharedInfo . ConfigDirectory ) ) {
2016-11-06 12:06:02 +01:00
ASF . ArchiLogger . LogGenericError ( "Config directory doesn't exist!" ) ;
2016-01-01 12:49:41 +01:00
Thread . Sleep ( 5000 ) ;
2016-04-02 13:08:43 +02:00
Exit ( 1 ) ;
2015-10-31 06:09:03 +01:00
}
2015-10-25 06:16:50 +01:00
2016-08-02 06:04:44 +02:00
ASF . CheckForUpdate ( ) . Wait ( ) ;
2016-03-06 23:28:56 +01:00
2016-01-24 17:38:45 +01:00
// Before attempting to connect, initialize our list of CMs
2016-07-08 09:02:53 +02:00
Bot . InitializeCMs ( GlobalDatabase . CellID , GlobalDatabase . ServerListProvider ) ;
2016-03-06 23:28:56 +01:00
2016-08-02 06:04:44 +02:00
foreach ( string botName in Directory . EnumerateFiles ( SharedInfo . ConfigDirectory , "*.json" ) . Select ( Path . GetFileNameWithoutExtension ) ) {
2016-03-22 11:28:59 +01:00
switch ( botName ) {
2016-08-02 06:04:44 +02:00
case SharedInfo . ASF :
2016-03-22 11:28:59 +01:00
case "example" :
case "minimal" :
continue ;
2016-03-06 23:28:56 +01:00
}
2016-10-07 00:04:08 +02:00
new Bot ( botName ) . Forget ( ) ;
2016-03-18 20:56:23 +01:00
}
2016-10-07 01:36:02 +02:00
2016-10-14 22:57:57 +02:00
if ( Bot . Bots . Count = = 0 ) {
2016-11-06 12:06:02 +01:00
ASF . ArchiLogger . LogGenericWarning ( "No bots are defined, did you forget to configure your ASF?" ) ;
2016-10-14 22:57:57 +02:00
}
2016-10-07 01:36:02 +02:00
ASF . InitFileWatcher ( ) ;
2016-03-18 20:56:23 +01:00
}
private static void Main ( string [ ] args ) {
2016-06-28 10:36:28 +02:00
if ( Runtime . IsUserInteractive ) {
2016-06-28 06:58:59 +02:00
// App
Init ( args ) ;
// Wait for signal to shutdown
ShutdownResetEvent . Wait ( ) ;
// We got a signal to shutdown
Exit ( ) ;
2016-06-28 07:14:51 +02:00
} else {
// Service
2016-07-04 19:22:46 +02:00
IsRunningAsService = true ;
2016-06-28 07:14:51 +02:00
using ( Service service = new Service ( ) ) {
ServiceBase . Run ( service ) ;
}
2016-06-28 06:58:59 +02:00
}
2016-06-27 23:39:47 -04:00
}
2015-10-31 03:27:58 +01:00
2016-06-28 06:58:59 +02:00
private sealed class Service : ServiceBase {
internal Service ( ) {
ServiceName = SharedInfo . ServiceName ;
}
2016-06-28 07:14:51 +02:00
protected override void OnStart ( string [ ] args ) = > Task . Run ( ( ) = > {
2016-06-28 06:58:59 +02:00
Init ( args ) ;
ShutdownResetEvent . Wait ( ) ;
Stop ( ) ;
2016-06-28 07:14:51 +02:00
} ) ;
2016-06-28 06:58:59 +02:00
2016-06-28 07:14:51 +02:00
protected override void OnStop ( ) = > Shutdown ( ) ;
2016-06-28 06:58:59 +02:00
}
}
2016-06-27 23:39:47 -04:00
2015-10-25 06:16:50 +01:00
}