2015-10-28 19:21:27 +01:00
/ *
_ _ _ ____ _ _____
/ \ _ __ ___ | | __ ( _ ) / ___ | | | _ ___ __ _ _ __ ___ | ___ | __ _ _ __ _ __ ___
/ _ \ | ' __ | / __ | | ' _ \ | | \ ___ \ | __ | / _ \ / _ ` | | ' _ ` _ \ | | _ / _ ` | | ' __ | | ' _ ` _ \
/ ___ \ | | | ( __ | | | | | | ___ ) | | | _ | __ / | ( _ | | | | | | | | | _ | | ( _ | | | | | | | | | |
/ _ / \ _ \ | _ | \ ___ | | _ | | _ | | _ | | ____ / \ __ | \ ___ | \ __ , _ | | _ | | _ | | _ | | _ | \ __ , _ | | _ | | _ | | _ | | _ |
Copyright 2015 Ł 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 .
* /
2015-12-11 22:53:28 +01:00
using Newtonsoft.Json ;
using SteamAuth ;
2015-10-28 19:21:27 +01:00
using SteamKit2 ;
2015-10-25 06:16:50 +01:00
using System ;
2015-11-18 14:28:46 +01:00
using System.Collections.Concurrent ;
2015-10-25 06:16:50 +01:00
using System.Collections.Generic ;
using System.IO ;
using System.Security.Cryptography ;
using System.Threading.Tasks ;
using System.Xml ;
namespace ArchiSteamFarm {
2015-11-27 16:25:03 +01:00
internal sealed class Bot {
2015-10-28 19:21:27 +01:00
private const ushort CallbackSleep = 500 ; // In miliseconds
2015-10-25 06:16:50 +01:00
2015-11-18 14:28:46 +01:00
private static readonly ConcurrentDictionary < string , Bot > Bots = new ConcurrentDictionary < string , Bot > ( ) ;
2015-10-25 06:16:50 +01:00
2015-12-11 22:53:28 +01:00
private readonly string ConfigFile , LoginKeyFile , MobileAuthenticatorFile , SentryFile ;
2015-11-20 14:47:16 +01:00
internal readonly string BotName ;
2015-10-25 06:16:50 +01:00
2015-12-09 20:55:43 +01:00
private bool LoggedInElsewhere = false ;
2015-10-31 03:27:58 +01:00
private bool IsRunning = false ;
2015-12-11 22:53:28 +01:00
private string AuthCode , LoginKey , TwoFactorAuth ;
2015-10-25 06:16:50 +01:00
internal ArchiHandler ArchiHandler { get ; private set ; }
internal ArchiWebHandler ArchiWebHandler { get ; private set ; }
internal CallbackManager CallbackManager { get ; private set ; }
2015-10-31 03:27:58 +01:00
internal CardsFarmer CardsFarmer { get ; private set ; }
2015-10-25 06:16:50 +01:00
internal SteamClient SteamClient { get ; private set ; }
internal SteamFriends SteamFriends { get ; private set ; }
2015-12-11 22:53:28 +01:00
internal SteamGuardAccount SteamGuardAccount { get ; private set ; }
2015-10-25 06:16:50 +01:00
internal SteamUser SteamUser { get ; private set ; }
internal Trading Trading { get ; private set ; }
// Config variables
2015-10-31 09:17:56 +01:00
internal bool Enabled { get ; private set ; } = false ;
2015-10-31 03:27:58 +01:00
internal string SteamLogin { get ; private set ; } = "null" ;
internal string SteamPassword { get ; private set ; } = "null" ;
internal string SteamNickname { get ; private set ; } = "null" ;
internal string SteamApiKey { get ; private set ; } = "null" ;
internal string SteamParentalPIN { get ; private set ; } = "0" ;
2015-11-04 04:46:55 +01:00
internal ulong SteamMasterID { get ; private set ; } = 0 ;
2015-10-31 03:27:58 +01:00
internal ulong SteamMasterClanID { get ; private set ; } = 0 ;
2015-11-21 18:27:30 +01:00
internal bool CardDropsRestricted { get ; private set ; } = false ;
2015-12-11 22:53:28 +01:00
internal bool UseAsfAsMobileAuthenticator { get ; private set ; } = false ;
2015-10-31 06:09:03 +01:00
internal bool ShutdownOnFarmingFinished { get ; private set ; } = false ;
2015-12-18 06:54:53 +01:00
internal HashSet < uint > Blacklist { get ; private set ; } = new HashSet < uint > { 303700 , 335590 , 368020 , 425280 } ;
2015-10-31 03:27:58 +01:00
internal bool Statistics { get ; private set ; } = true ;
2015-11-25 16:49:01 +01:00
private static bool IsValidCdKey ( string key ) {
if ( string . IsNullOrEmpty ( key ) ) {
return false ;
}
if ( key . Length ! = 17 & & key . Length ! = 29 ) {
return false ;
}
for ( byte i = 5 ; i < key . Length ; i + = 6 ) {
if ( key [ i ] ! = '-' ) {
return false ;
}
}
return true ;
}
2015-10-31 03:27:58 +01:00
internal static int GetRunningBotsCount ( ) {
2015-11-18 14:28:46 +01:00
return Bots . Count ;
2015-11-18 14:30:05 +01:00
}
2015-10-31 03:27:58 +01:00
2015-10-31 05:27:30 +01:00
internal static async Task ShutdownAllBots ( ) {
List < Task > tasks = new List < Task > ( ) ;
2015-11-18 14:28:46 +01:00
foreach ( Bot bot in Bots . Values ) {
tasks . Add ( Task . Run ( async ( ) = > await bot . Shutdown ( ) . ConfigureAwait ( false ) ) ) ;
2015-10-31 03:27:58 +01:00
}
2015-10-31 05:27:30 +01:00
await Task . WhenAll ( tasks ) . ConfigureAwait ( false ) ;
2015-10-31 03:27:58 +01:00
}
2015-10-25 06:16:50 +01:00
2015-10-28 19:21:27 +01:00
internal Bot ( string botName ) {
2015-11-01 02:04:44 +01:00
if ( Bots . ContainsKey ( botName ) ) {
return ;
}
2015-10-25 06:16:50 +01:00
BotName = botName ;
ConfigFile = Path . Combine ( Program . ConfigDirectoryPath , BotName + ".xml" ) ;
2015-12-11 22:53:28 +01:00
LoginKeyFile = Path . Combine ( Program . ConfigDirectoryPath , BotName + ".key" ) ;
MobileAuthenticatorFile = Path . Combine ( Program . ConfigDirectoryPath , BotName + ".auth" ) ;
2015-10-25 06:16:50 +01:00
SentryFile = Path . Combine ( Program . ConfigDirectoryPath , BotName + ".bin" ) ;
2015-11-01 02:04:44 +01:00
if ( ! ReadConfig ( ) ) {
return ;
}
2015-10-25 06:16:50 +01:00
if ( ! Enabled ) {
return ;
}
2015-10-28 19:21:27 +01:00
2015-11-18 14:28:46 +01:00
Bots . AddOrUpdate ( BotName , this , ( key , value ) = > this ) ;
2015-10-31 03:27:58 +01:00
// Initialize
SteamClient = new SteamClient ( ) ;
ArchiHandler = new ArchiHandler ( ) ;
SteamClient . AddHandler ( ArchiHandler ) ;
CallbackManager = new CallbackManager ( SteamClient ) ;
CallbackManager . Subscribe < SteamClient . ConnectedCallback > ( OnConnected ) ;
CallbackManager . Subscribe < SteamClient . DisconnectedCallback > ( OnDisconnected ) ;
SteamFriends = SteamClient . GetHandler < SteamFriends > ( ) ;
CallbackManager . Subscribe < SteamFriends . FriendsListCallback > ( OnFriendsList ) ;
CallbackManager . Subscribe < SteamFriends . FriendMsgCallback > ( OnFriendMsg ) ;
2015-12-11 22:53:28 +01:00
if ( UseAsfAsMobileAuthenticator & & File . Exists ( MobileAuthenticatorFile ) ) {
SteamGuardAccount = JsonConvert . DeserializeObject < SteamGuardAccount > ( File . ReadAllText ( MobileAuthenticatorFile ) ) ;
}
2015-10-31 03:27:58 +01:00
SteamUser = SteamClient . GetHandler < SteamUser > ( ) ;
CallbackManager . Subscribe < SteamUser . AccountInfoCallback > ( OnAccountInfo ) ;
CallbackManager . Subscribe < SteamUser . LoggedOffCallback > ( OnLoggedOff ) ;
CallbackManager . Subscribe < SteamUser . LoggedOnCallback > ( OnLoggedOn ) ;
2015-12-11 22:53:28 +01:00
CallbackManager . Subscribe < SteamUser . LoginKeyCallback > ( OnLoginKey ) ;
2015-10-31 03:27:58 +01:00
CallbackManager . Subscribe < SteamUser . UpdateMachineAuthCallback > ( OnMachineAuth ) ;
CallbackManager . Subscribe < ArchiHandler . NotificationCallback > ( OnNotification ) ;
CallbackManager . Subscribe < ArchiHandler . PurchaseResponseCallback > ( OnPurchaseResponse ) ;
ArchiWebHandler = new ArchiWebHandler ( this , SteamApiKey ) ;
CardsFarmer = new CardsFarmer ( this ) ;
Trading = new Trading ( this ) ;
// Start
2015-11-04 04:42:13 +01:00
var fireAndForget = Task . Run ( async ( ) = > await Start ( ) . ConfigureAwait ( false ) ) ;
2015-10-25 06:16:50 +01:00
}
2015-12-17 22:04:13 +01:00
internal async Task AcceptAllConfirmations ( ) {
2015-12-11 22:53:28 +01:00
if ( SteamGuardAccount = = null ) {
return ;
}
2015-12-17 22:04:13 +01:00
await SteamGuardAccount . RefreshSessionAsync ( ) . ConfigureAwait ( false ) ;
2015-12-13 15:25:00 +01:00
try {
2015-12-17 22:04:13 +01:00
foreach ( Confirmation confirmation in await SteamGuardAccount . FetchConfirmationsAsync ( ) . ConfigureAwait ( false ) ) {
2015-12-13 15:25:00 +01:00
if ( SteamGuardAccount . AcceptConfirmation ( confirmation ) ) {
Logging . LogGenericInfo ( BotName , "Accepting confirmation: Success!" ) ;
} else {
Logging . LogGenericWarning ( BotName , "Accepting confirmation: Failed!" ) ;
}
2015-12-11 22:53:28 +01:00
}
2015-12-13 15:25:00 +01:00
} catch ( SteamGuardAccount . WGTokenInvalidException ) {
Logging . LogGenericWarning ( BotName , "Accepting confirmation: Failed!" ) ;
Logging . LogGenericWarning ( BotName , "Confirmation could not be accepted because of invalid token exception" ) ;
Logging . LogGenericWarning ( BotName , "If issue persists, consider removing and readding ASF 2FA" ) ;
2015-12-11 22:53:28 +01:00
}
}
private bool LinkMobileAuthenticator ( ) {
if ( SteamGuardAccount ! = null ) {
return false ;
}
Logging . LogGenericNotice ( BotName , "Linking new ASF MobileAuthenticator..." ) ;
UserLogin userLogin = new UserLogin ( SteamLogin , SteamPassword ) ;
LoginResult loginResult ;
while ( ( loginResult = userLogin . DoLogin ( ) ) ! = LoginResult . LoginOkay ) {
switch ( loginResult ) {
case LoginResult . NeedEmail :
userLogin . EmailCode = Program . GetUserInput ( BotName , Program . EUserInputType . SteamGuard ) ;
break ;
default :
Logging . LogGenericError ( BotName , "Unhandled situation: " + loginResult ) ;
return false ;
}
}
AuthenticatorLinker authenticatorLinker = new AuthenticatorLinker ( userLogin . Session ) ;
2015-12-13 13:51:58 +01:00
AuthenticatorLinker . LinkResult linkResult ;
while ( ( linkResult = authenticatorLinker . AddAuthenticator ( ) ) ! = AuthenticatorLinker . LinkResult . AwaitingFinalization ) {
switch ( linkResult ) {
case AuthenticatorLinker . LinkResult . MustProvidePhoneNumber :
2015-12-11 22:53:28 +01:00
authenticatorLinker . PhoneNumber = Program . GetUserInput ( BotName , Program . EUserInputType . PhoneNumber ) ;
2015-12-13 13:51:58 +01:00
break ;
default :
Logging . LogGenericError ( BotName , "Unhandled situation: " + linkResult ) ;
return false ;
}
2015-12-11 22:53:28 +01:00
}
SteamGuardAccount = authenticatorLinker . LinkedAccount ;
try {
File . WriteAllText ( MobileAuthenticatorFile , JsonConvert . SerializeObject ( SteamGuardAccount ) ) ;
} catch ( Exception e ) {
Logging . LogGenericException ( BotName , e ) ;
return false ;
}
AuthenticatorLinker . FinalizeResult finalizeResult = authenticatorLinker . FinalizeAddAuthenticator ( Program . GetUserInput ( BotName , Program . EUserInputType . SMS ) ) ;
if ( finalizeResult ! = AuthenticatorLinker . FinalizeResult . Success ) {
Logging . LogGenericError ( BotName , "Unhandled situation: " + finalizeResult ) ;
DelinkMobileAuthenticator ( ) ;
return false ;
}
Logging . LogGenericInfo ( BotName , "Successfully linked ASF as new mobile authenticator for this account!" ) ;
Program . GetUserInput ( BotName , Program . EUserInputType . RevocationCode , SteamGuardAccount . RevocationCode ) ;
return true ;
}
private bool DelinkMobileAuthenticator ( ) {
if ( SteamGuardAccount = = null ) {
return false ;
}
bool result = SteamGuardAccount . DeactivateAuthenticator ( ) ;
SteamGuardAccount = null ;
File . Delete ( MobileAuthenticatorFile ) ;
return result ;
}
2015-11-01 02:04:44 +01:00
private bool ReadConfig ( ) {
if ( ! File . Exists ( ConfigFile ) ) {
return false ;
}
2015-11-04 04:51:52 +01:00
try {
using ( XmlReader reader = XmlReader . Create ( ConfigFile ) ) {
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" :
Enabled = bool . Parse ( value ) ;
break ;
case "SteamLogin" :
SteamLogin = value ;
break ;
case "SteamPassword" :
SteamPassword = value ;
break ;
case "SteamNickname" :
SteamNickname = value ;
break ;
case "SteamApiKey" :
SteamApiKey = value ;
break ;
case "SteamParentalPIN" :
SteamParentalPIN = value ;
break ;
case "SteamMasterID" :
SteamMasterID = ulong . Parse ( value ) ;
break ;
case "SteamMasterClanID" :
SteamMasterClanID = ulong . Parse ( value ) ;
break ;
2015-12-11 22:53:28 +01:00
case "UseAsfAsMobileAuthenticator" :
UseAsfAsMobileAuthenticator = bool . Parse ( value ) ;
break ;
2015-11-21 18:27:30 +01:00
case "CardDropsRestricted" :
CardDropsRestricted = bool . Parse ( value ) ;
break ;
2015-11-04 04:51:52 +01:00
case "ShutdownOnFarmingFinished" :
ShutdownOnFarmingFinished = bool . Parse ( value ) ;
break ;
case "Blacklist" :
2015-11-04 04:55:00 +01:00
Blacklist . Clear ( ) ;
2015-11-04 04:51:52 +01:00
foreach ( string appID in value . Split ( ',' ) ) {
Blacklist . Add ( uint . Parse ( appID ) ) ;
}
break ;
case "Statistics" :
Statistics = bool . Parse ( value ) ;
break ;
default :
Logging . LogGenericWarning ( BotName , "Unrecognized config value: " + key + "=" + value ) ;
break ;
}
2015-10-28 22:34:53 +01:00
}
2015-10-25 06:16:50 +01:00
}
2015-11-18 14:28:46 +01:00
} catch ( Exception e ) {
2015-11-04 04:51:52 +01:00
Logging . LogGenericException ( BotName , e ) ;
Logging . LogGenericError ( BotName , "Your config for this bot instance is invalid, it won't run!" ) ;
return false ;
2015-10-25 06:16:50 +01:00
}
2015-11-01 02:04:44 +01:00
return true ;
2015-10-25 06:16:50 +01:00
}
2015-11-04 04:42:13 +01:00
internal async Task Start ( ) {
2015-10-31 03:27:58 +01:00
if ( IsRunning ) {
2015-10-25 06:16:50 +01:00
return ;
}
2015-10-31 03:27:58 +01:00
IsRunning = true ;
2015-11-04 04:42:13 +01:00
Logging . LogGenericInfo ( BotName , "Starting..." ) ;
2015-12-06 19:38:09 +01:00
// 2FA tokens are expiring soon, use limiter only when we don't have any pending
if ( TwoFactorAuth = = null ) {
await Program . LimitSteamRequestsAsync ( ) . ConfigureAwait ( false ) ;
}
2015-11-04 04:31:27 +01:00
SteamClient . Connect ( ) ;
2015-11-04 04:42:13 +01:00
var fireAndForget = Task . Run ( ( ) = > HandleCallbacks ( ) ) ;
2015-10-25 06:16:50 +01:00
}
2015-10-31 05:27:30 +01:00
internal async Task Stop ( ) {
2015-10-31 03:27:58 +01:00
if ( ! IsRunning ) {
2015-10-25 06:16:50 +01:00
return ;
}
2015-10-31 05:27:30 +01:00
await CardsFarmer . StopFarming ( ) . ConfigureAwait ( false ) ;
2015-10-31 03:27:58 +01:00
IsRunning = false ;
2015-11-01 02:08:41 +01:00
SteamClient . Disconnect ( ) ;
2015-10-31 03:50:08 +01:00
}
2015-10-31 03:27:58 +01:00
2015-12-12 08:04:41 +01:00
internal async Task < bool > Shutdown ( string botName = null ) {
Bot bot ;
if ( string . IsNullOrEmpty ( botName ) ) {
bot = this ;
} else {
if ( ! Bots . TryGetValue ( botName , out bot ) ) {
return false ;
}
2015-11-01 02:04:44 +01:00
}
2015-12-12 08:04:41 +01:00
await bot . Stop ( ) . ConfigureAwait ( false ) ;
2015-12-13 21:05:01 +01:00
Bots . TryRemove ( bot . BotName , out bot ) ;
2015-11-01 02:04:44 +01:00
2015-12-12 08:04:41 +01:00
Program . OnBotShutdown ( ) ;
2015-11-01 02:04:44 +01:00
return true ;
}
2015-10-31 05:27:30 +01:00
internal async Task OnFarmingFinished ( ) {
2015-10-31 03:27:58 +01:00
if ( ShutdownOnFarmingFinished ) {
2015-10-31 05:27:30 +01:00
await Shutdown ( ) . ConfigureAwait ( false ) ;
2015-10-31 03:27:58 +01:00
}
2015-10-25 06:16:50 +01:00
}
private void HandleCallbacks ( ) {
TimeSpan timeSpan = TimeSpan . FromMilliseconds ( CallbackSleep ) ;
2015-10-31 03:27:58 +01:00
while ( IsRunning ) {
2015-10-25 06:16:50 +01:00
CallbackManager . RunWaitCallbacks ( timeSpan ) ;
}
}
2015-11-01 02:04:44 +01:00
private void SendMessageToUser ( ulong steamID , string message ) {
if ( steamID = = 0 | | string . IsNullOrEmpty ( message ) ) {
return ;
}
SteamFriends . SendChatMessage ( steamID , EChatEntryType . ChatMsg , message ) ;
}
2015-11-18 22:10:24 +01:00
private void ResponseStatus ( ulong steamID , string botName = null ) {
2015-11-01 02:04:44 +01:00
if ( steamID = = 0 ) {
return ;
}
2015-11-18 22:10:24 +01:00
Bot bot ;
if ( string . IsNullOrEmpty ( botName ) ) {
bot = this ;
} else {
if ( ! Bots . TryGetValue ( botName , out bot ) ) {
SendMessageToUser ( steamID , "Couldn't find any bot named " + botName + "!" ) ;
return ;
}
}
2015-11-21 21:28:34 +01:00
if ( bot . CardsFarmer . CurrentGamesFarming . Count > 0 ) {
2015-11-21 21:30:49 +01:00
SendMessageToUser ( steamID , "Bot " + bot . BotName + " is currently farming appIDs: " + string . Join ( ", " , bot . CardsFarmer . CurrentGamesFarming ) + " and has a total of " + bot . CardsFarmer . GamesToFarm . Count + " games left to farm" ) ;
2015-11-18 22:10:24 +01:00
}
SendMessageToUser ( steamID , "Currently " + Bots . Count + " bots are running" ) ;
2015-11-01 02:04:44 +01:00
}
2015-12-11 22:53:28 +01:00
private void Response2FA ( ulong steamID , string botName = null ) {
if ( steamID = = 0 ) {
return ;
}
Bot bot ;
if ( string . IsNullOrEmpty ( botName ) ) {
bot = this ;
} else {
if ( ! Bots . TryGetValue ( botName , out bot ) ) {
SendMessageToUser ( steamID , "Couldn't find any bot named " + botName + "!" ) ;
return ;
}
}
if ( bot . SteamGuardAccount = = null ) {
SendMessageToUser ( steamID , "That bot doesn't have ASF 2FA enabled!" ) ;
return ;
}
long timeLeft = 30 - TimeAligner . GetSteamTime ( ) % 30 ;
SendMessageToUser ( steamID , "2FA Token: " + bot . SteamGuardAccount . GenerateSteamGuardCode ( ) + " (expires in " + timeLeft + " seconds)" ) ;
}
private void Response2FAOff ( ulong steamID , string botName = null ) {
if ( steamID = = 0 ) {
return ;
}
Bot bot ;
if ( string . IsNullOrEmpty ( botName ) ) {
bot = this ;
} else {
if ( ! Bots . TryGetValue ( botName , out bot ) ) {
SendMessageToUser ( steamID , "Couldn't find any bot named " + botName + "!" ) ;
return ;
}
}
if ( bot . SteamGuardAccount = = null ) {
SendMessageToUser ( steamID , "That bot doesn't have ASF 2FA enabled!" ) ;
return ;
}
if ( bot . DelinkMobileAuthenticator ( ) ) {
SendMessageToUser ( steamID , "Done! Bot is no longer using ASF 2FA" ) ;
} else {
SendMessageToUser ( steamID , "Something went wrong!" ) ;
}
}
2015-12-12 08:04:41 +01:00
private void ResponseStart ( ulong steamID , string botName ) {
if ( steamID = = 0 | | string . IsNullOrEmpty ( botName ) ) {
2015-11-01 02:04:44 +01:00
return ;
}
2015-12-12 08:04:41 +01:00
if ( Bots . ContainsKey ( botName ) ) {
2015-11-01 02:04:44 +01:00
SendMessageToUser ( steamID , "That bot instance is already running!" ) ;
return ;
}
2015-12-12 08:04:41 +01:00
new Bot ( botName ) ;
if ( Bots . ContainsKey ( botName ) ) {
2015-11-01 02:04:44 +01:00
SendMessageToUser ( steamID , "Done!" ) ;
} else {
2015-12-12 08:04:41 +01:00
SendMessageToUser ( steamID , "That bot instance failed to start, make sure that XML config exists and bot is active!" ) ;
2015-11-01 02:04:44 +01:00
}
}
2015-12-12 08:04:41 +01:00
private async Task ResponseStop ( ulong steamID , string botName ) {
if ( steamID = = 0 | | string . IsNullOrEmpty ( botName ) ) {
2015-11-01 02:04:44 +01:00
return ;
}
2015-12-12 08:04:41 +01:00
if ( ! Bots . ContainsKey ( botName ) ) {
2015-11-01 02:04:44 +01:00
SendMessageToUser ( steamID , "That bot instance is already inactive!" ) ;
return ;
}
2015-12-12 08:04:41 +01:00
if ( await Shutdown ( botName ) . ConfigureAwait ( false ) ) {
2015-11-01 02:04:44 +01:00
SendMessageToUser ( steamID , "Done!" ) ;
} else {
SendMessageToUser ( steamID , "That bot instance failed to shutdown!" ) ;
}
}
2015-10-25 06:16:50 +01:00
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!" ) ;
2015-12-11 22:53:28 +01:00
if ( File . Exists ( LoginKeyFile ) ) {
LoginKey = File . ReadAllText ( LoginKeyFile ) ;
}
2015-10-25 06:16:50 +01:00
byte [ ] sentryHash = null ;
if ( File . Exists ( SentryFile ) ) {
byte [ ] sentryFileContent = File . ReadAllBytes ( SentryFile ) ;
sentryHash = CryptoHelper . SHAHash ( sentryFileContent ) ;
}
2015-10-31 03:27:58 +01:00
if ( SteamLogin . Equals ( "null" ) ) {
SteamLogin = Program . GetUserInput ( BotName , Program . EUserInputType . Login ) ;
2015-10-28 23:29:50 +01:00
}
2015-12-11 22:53:28 +01:00
if ( SteamPassword . Equals ( "null" ) & & string . IsNullOrEmpty ( LoginKey ) ) {
2015-10-31 03:27:58 +01:00
SteamPassword = Program . GetUserInput ( BotName , Program . EUserInputType . Password ) ;
2015-10-28 23:29:50 +01:00
}
2015-12-16 20:01:40 +01:00
// TODO: We should use SteamUser.LogOn with proper LoginID once https://github.com/SteamRE/SteamKit/pull/217 gets merged
ArchiHandler . HackedLogOn ( Program . UniqueID , new SteamUser . LogOnDetails {
2015-12-16 01:20:54 +01:00
Username = SteamLogin ,
Password = SteamPassword ,
AuthCode = AuthCode ,
LoginKey = LoginKey ,
TwoFactorCode = TwoFactorAuth ,
SentryFileHash = sentryHash ,
ShouldRememberPassword = true
2015-12-16 20:01:40 +01:00
} ) ;
2015-10-25 06:16:50 +01:00
}
2015-11-01 02:04:44 +01:00
private async void OnDisconnected ( SteamClient . DisconnectedCallback callback ) {
2015-10-25 06:16:50 +01:00
if ( callback = = null ) {
return ;
}
2015-10-28 22:34:53 +01:00
2015-11-01 02:08:41 +01:00
if ( ! IsRunning ) {
2015-11-01 02:04:44 +01:00
return ;
}
2015-12-09 20:55:43 +01:00
await CardsFarmer . StopFarming ( ) . ConfigureAwait ( false ) ;
2015-10-25 06:16:50 +01:00
Logging . LogGenericWarning ( BotName , "Disconnected from Steam, reconnecting..." ) ;
2015-12-06 19:38:09 +01:00
// 2FA tokens are expiring soon, use limiter only when we don't have any pending
if ( TwoFactorAuth = = null ) {
await Program . LimitSteamRequestsAsync ( ) . ConfigureAwait ( false ) ;
}
2015-12-09 20:55:43 +01:00
if ( LoggedInElsewhere ) {
LoggedInElsewhere = false ;
2015-12-12 08:04:41 +01:00
Logging . LogGenericWarning ( BotName , "Account is being used elsewhere, will try reconnecting in 5 minutes..." ) ;
await Utilities . SleepAsync ( 5 * 60 * 1000 ) . ConfigureAwait ( false ) ;
2015-12-09 20:55:43 +01:00
}
2015-10-25 06:16:50 +01:00
SteamClient . Connect ( ) ;
}
private void OnFriendsList ( SteamFriends . FriendsListCallback callback ) {
if ( callback = = null ) {
return ;
}
foreach ( var friend in callback . FriendList ) {
if ( friend . Relationship ! = EFriendRelationship . RequestRecipient ) {
continue ;
}
SteamID steamID = friend . SteamID ;
switch ( steamID . AccountType ) {
case EAccountType . Clan :
2015-12-16 20:09:31 +01:00
// TODO: Accept clan invites from master?
2015-10-25 06:16:50 +01:00
break ;
default :
if ( steamID = = SteamMasterID ) {
SteamFriends . AddFriend ( steamID ) ;
}
break ;
}
}
}
2015-10-31 05:27:30 +01:00
private async void OnFriendMsg ( SteamFriends . FriendMsgCallback callback ) {
2015-10-25 06:16:50 +01:00
if ( callback = = null ) {
return ;
}
if ( callback . EntryType ! = EChatEntryType . ChatMsg ) {
return ;
}
ulong steamID = callback . Sender ;
if ( steamID ! = SteamMasterID ) {
return ;
}
string message = callback . Message ;
if ( string . IsNullOrEmpty ( message ) ) {
return ;
}
2015-11-25 16:49:01 +01:00
if ( IsValidCdKey ( message ) ) {
2015-10-25 06:16:50 +01:00
ArchiHandler . RedeemKey ( message ) ;
2015-11-01 02:04:44 +01:00
return ;
2015-10-25 06:16:50 +01:00
}
2015-10-31 05:27:30 +01:00
2015-11-01 02:04:44 +01:00
if ( ! message . StartsWith ( "!" ) ) {
return ;
}
if ( ! message . Contains ( " " ) ) {
switch ( message ) {
2015-12-11 22:53:28 +01:00
case "!2fa" :
Response2FA ( steamID ) ;
break ;
case "!2faoff" :
Response2FAOff ( steamID ) ;
break ;
2015-11-01 02:04:44 +01:00
case "!exit" :
await ShutdownAllBots ( ) . ConfigureAwait ( false ) ;
break ;
case "!farm" :
2015-11-20 14:47:16 +01:00
SendMessageToUser ( steamID , "Please wait..." ) ;
2015-11-01 02:04:44 +01:00
await CardsFarmer . StartFarming ( ) . ConfigureAwait ( false ) ;
SendMessageToUser ( steamID , "Done!" ) ;
break ;
case "!restart" :
await Program . Restart ( ) . ConfigureAwait ( false ) ;
break ;
case "!status" :
ResponseStatus ( steamID ) ;
break ;
case "!stop" :
await Shutdown ( ) . ConfigureAwait ( false ) ;
break ;
}
} else {
string [ ] args = message . Split ( ' ' ) ;
switch ( args [ 0 ] ) {
2015-12-11 22:53:28 +01:00
case "!2fa" :
Response2FA ( steamID , args [ 1 ] ) ;
break ;
case "!2faoff" :
Response2FAOff ( steamID , args [ 1 ] ) ;
break ;
2015-11-20 14:47:16 +01:00
case "!redeem" :
ArchiHandler . RedeemKey ( args [ 1 ] ) ;
break ;
2015-11-01 02:04:44 +01:00
case "!start" :
ResponseStart ( steamID , args [ 1 ] ) ;
break ;
case "!stop" :
await ResponseStop ( steamID , args [ 1 ] ) . ConfigureAwait ( false ) ;
break ;
2015-11-18 22:10:24 +01:00
case "!status" :
ResponseStatus ( steamID , args [ 1 ] ) ;
break ;
}
2015-10-31 05:27:30 +01:00
}
2015-10-25 06:16:50 +01:00
}
private void OnAccountInfo ( SteamUser . AccountInfoCallback callback ) {
if ( callback = = null ) {
return ;
}
SteamFriends . SetPersonaState ( EPersonaState . Online ) ;
}
private void OnLoggedOff ( SteamUser . LoggedOffCallback callback ) {
if ( callback = = null ) {
return ;
}
Logging . LogGenericInfo ( BotName , "Logged off of Steam: " + callback . Result ) ;
2015-12-09 20:55:43 +01:00
switch ( callback . Result ) {
case EResult . AlreadyLoggedInElsewhere :
case EResult . LoggedInElsewhere :
2015-12-16 20:01:40 +01:00
case EResult . LogonSessionReplaced :
2015-12-09 20:55:43 +01:00
LoggedInElsewhere = true ;
break ;
}
2015-10-25 06:16:50 +01:00
}
private async void OnLoggedOn ( SteamUser . LoggedOnCallback callback ) {
if ( callback = = null ) {
return ;
}
EResult result = callback . Result ;
switch ( result ) {
case EResult . AccountLogonDenied :
2015-10-28 23:29:50 +01:00
AuthCode = Program . GetUserInput ( SteamLogin , Program . EUserInputType . SteamGuard ) ;
2015-10-25 06:16:50 +01:00
break ;
case EResult . AccountLoginDeniedNeedTwoFactor :
2015-12-11 22:53:28 +01:00
if ( SteamGuardAccount = = null ) {
TwoFactorAuth = Program . GetUserInput ( SteamLogin , Program . EUserInputType . TwoFactorAuthentication ) ;
} else {
TwoFactorAuth = SteamGuardAccount . GenerateSteamGuardCode ( ) ;
}
2015-10-25 06:16:50 +01:00
break ;
2015-11-26 01:22:56 +01:00
case EResult . InvalidPassword :
2015-12-16 09:39:03 +01:00
Logging . LogGenericWarning ( BotName , "Unable to login to Steam: " + result ) ;
2015-11-26 01:22:56 +01:00
await Stop ( ) . ConfigureAwait ( false ) ;
2015-12-13 13:00:23 +01:00
2015-12-16 09:39:03 +01:00
// InvalidPassword means usually that login key has expired, if we used it
if ( ! string . IsNullOrEmpty ( LoginKey ) ) {
LoginKey = null ;
File . Delete ( LoginKeyFile ) ;
Logging . LogGenericInfo ( BotName , "Removed expired login key, reconnecting..." ) ;
} else { // If we didn't use login key, InvalidPassword usually means we got captcha or other network-based throttling
Logging . LogGenericInfo ( BotName , "Will retry after 25 minutes..." ) ;
await Utilities . SleepAsync ( 25 * 60 * 1000 ) . ConfigureAwait ( false ) ; // Captcha disappears after around 20 minutes, so we make it 25
}
2015-12-13 13:00:23 +01:00
// After all of that, try again
2015-11-26 01:22:56 +01:00
await Start ( ) . ConfigureAwait ( false ) ;
break ;
2015-10-25 06:16:50 +01:00
case EResult . OK :
Logging . LogGenericInfo ( BotName , "Successfully logged on!" ) ;
2015-10-28 20:01:43 +01:00
2015-12-11 22:53:28 +01:00
if ( UseAsfAsMobileAuthenticator & & TwoFactorAuth = = null & & SteamGuardAccount = = null ) {
LinkMobileAuthenticator ( ) ;
}
2015-12-06 19:38:09 +01:00
// Reset one-time-only access tokens
AuthCode = null ;
TwoFactorAuth = null ;
2015-10-31 03:27:58 +01:00
if ( ! SteamNickname . Equals ( "null" ) ) {
SteamFriends . SetPersonaName ( SteamNickname ) ;
2015-10-28 20:01:43 +01:00
}
2015-10-31 03:27:58 +01:00
if ( SteamParentalPIN . Equals ( "null" ) ) {
SteamParentalPIN = Program . GetUserInput ( BotName , Program . EUserInputType . SteamParentalPIN ) ;
2015-10-29 01:01:31 +01:00
}
2015-10-31 03:27:58 +01:00
await ArchiWebHandler . Init ( SteamClient , callback . WebAPIUserNonce , callback . VanityURL , SteamParentalPIN ) . ConfigureAwait ( false ) ;
2015-10-25 06:16:50 +01:00
2015-12-06 20:12:44 +01:00
if ( SteamMasterClanID ! = 0 ) {
await ArchiWebHandler . JoinClan ( SteamMasterClanID ) . ConfigureAwait ( false ) ;
SteamFriends . JoinChat ( SteamMasterClanID ) ;
2015-10-28 19:21:27 +01:00
}
2015-10-25 06:16:50 +01:00
2015-10-29 16:38:16 +01:00
if ( Statistics ) {
await ArchiWebHandler . JoinClan ( Program . ArchiSCFarmGroup ) . ConfigureAwait ( false ) ;
2015-12-06 20:12:44 +01:00
SteamFriends . JoinChat ( Program . ArchiSCFarmGroup ) ;
2015-10-29 16:38:16 +01:00
}
2015-12-13 15:25:00 +01:00
Trading . CheckTrades ( ) ;
2015-10-25 06:16:50 +01:00
await CardsFarmer . StartFarming ( ) . ConfigureAwait ( false ) ;
break ;
2015-12-02 17:17:47 +01:00
case EResult . ServiceUnavailable :
2015-10-28 20:01:43 +01:00
case EResult . Timeout :
case EResult . TryAnotherCM :
2015-11-26 01:22:56 +01:00
Logging . LogGenericWarning ( BotName , "Unable to login to Steam: " + result + ", retrying..." ) ;
2015-10-31 05:27:30 +01:00
await Stop ( ) . ConfigureAwait ( false ) ;
2015-11-04 04:42:13 +01:00
await Start ( ) . ConfigureAwait ( false ) ;
2015-10-25 06:16:50 +01:00
break ;
2015-10-28 20:01:43 +01:00
default :
2015-11-26 01:22:56 +01:00
Logging . LogGenericWarning ( BotName , "Unable to login to Steam: " + result ) ;
2015-10-31 05:27:30 +01:00
await Shutdown ( ) . ConfigureAwait ( false ) ;
2015-10-28 20:01:43 +01:00
break ;
2015-10-25 06:16:50 +01:00
}
}
2015-12-11 22:53:28 +01:00
private void OnLoginKey ( SteamUser . LoginKeyCallback callback ) {
if ( callback = = null ) {
return ;
}
File . WriteAllText ( LoginKeyFile , callback . LoginKey ) ;
SteamUser . AcceptNewLoginKey ( callback ) ;
}
2015-10-25 06:16:50 +01:00
private void OnMachineAuth ( SteamUser . UpdateMachineAuthCallback callback ) {
if ( callback = = null ) {
return ;
}
Logging . LogGenericInfo ( BotName , "Updating sentryfile..." ) ;
int fileSize ;
byte [ ] sentryHash ;
using ( FileStream fileStream = File . Open ( SentryFile , FileMode . OpenOrCreate , FileAccess . ReadWrite ) ) {
fileStream . Seek ( callback . Offset , SeekOrigin . Begin ) ;
fileStream . Write ( callback . Data , 0 , callback . BytesToWrite ) ;
fileSize = ( int ) fileStream . Length ;
fileStream . Seek ( 0 , SeekOrigin . Begin ) ;
using ( SHA1CryptoServiceProvider sha = new SHA1CryptoServiceProvider ( ) ) {
sentryHash = sha . ComputeHash ( fileStream ) ;
}
}
// Inform the steam servers that we're accepting this sentry file
SteamUser . SendMachineAuthResponse ( new SteamUser . MachineAuthDetails {
JobID = callback . JobID ,
FileName = callback . FileName ,
BytesWritten = callback . BytesToWrite ,
FileSize = fileSize ,
Offset = callback . Offset ,
Result = EResult . OK ,
LastError = 0 ,
OneTimePassword = callback . OneTimePassword ,
SentryFileHash = sentryHash ,
} ) ;
2015-10-28 20:09:16 +01:00
Logging . LogGenericInfo ( BotName , "Sentryfile updated successfully!" ) ;
2015-10-25 06:16:50 +01:00
}
private void OnNotification ( ArchiHandler . NotificationCallback callback ) {
if ( callback = = null ) {
return ;
}
switch ( callback . NotificationType ) {
case ArchiHandler . NotificationCallback . ENotificationType . Trading :
Trading . CheckTrades ( ) ;
break ;
}
}
private async void OnPurchaseResponse ( ArchiHandler . PurchaseResponseCallback callback ) {
if ( callback = = null ) {
return ;
}
var purchaseResult = callback . PurchaseResult ;
2015-11-01 16:46:02 +01:00
var items = callback . Items ;
2015-11-01 16:53:08 +01:00
SendMessageToUser ( SteamMasterID , "Status: " + purchaseResult + " | Items: " + string . Join ( "" , items ) ) ;
2015-10-25 06:16:50 +01:00
if ( purchaseResult = = ArchiHandler . PurchaseResponseCallback . EPurchaseResult . OK ) {
await CardsFarmer . StartFarming ( ) . ConfigureAwait ( false ) ;
}
}
}
}