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-12-11 22:53:28 +01:00
using Newtonsoft.Json ;
using SteamAuth ;
2015-10-28 19:21:27 +01:00
using SteamKit2 ;
2015-12-21 10:24:29 +01:00
using SteamKit2.Internal ;
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 ;
2016-01-13 11:17:58 +02:00
using System.Threading ;
2015-10-25 06:16:50 +01:00
using System.Threading.Tasks ;
using System.Xml ;
2016-01-10 20:22:46 +02:00
using System.Text ;
2015-10-25 06:16:50 +01:00
namespace ArchiSteamFarm {
2015-11-27 16:25:03 +01:00
internal sealed class Bot {
2015-12-21 10:06:37 +01:00
private const ulong ArchiSCFarmGroup = 103582791440160998 ;
2015-10-28 19:21:27 +01:00
private const ushort CallbackSleep = 500 ; // In miliseconds
2015-10-25 06:16:50 +01:00
2016-01-03 22:23:30 +01:00
internal static readonly ConcurrentDictionary < string , Bot > Bots = new ConcurrentDictionary < string , Bot > ( ) ;
2015-12-23 09:40:42 +01:00
internal static readonly HashSet < uint > GlobalBlacklist = new HashSet < uint > { 303700 , 335590 , 368020 , 425280 } ;
2016-02-22 18:34:45 +01:00
private static readonly uint LoginID = MsgClientLogon . ObfuscationMask ; // This must be the same for all ASF bots and all ASF processes
2015-12-11 22:53:28 +01:00
private readonly string ConfigFile , LoginKeyFile , MobileAuthenticatorFile , SentryFile ;
2016-01-14 02:24:55 +01:00
private readonly Timer SendItemsTimer ;
2015-11-20 14:47:16 +01:00
2016-02-22 18:34:45 +01:00
internal readonly string BotName ;
internal readonly ArchiHandler ArchiHandler ;
internal readonly ArchiWebHandler ArchiWebHandler ;
internal readonly SteamClient SteamClient ;
2016-01-22 11:01:33 +01:00
private readonly CallbackManager CallbackManager ;
private readonly CardsFarmer CardsFarmer ;
private readonly SteamApps SteamApps ;
private readonly SteamFriends SteamFriends ;
private readonly SteamUser SteamUser ;
private readonly Trading Trading ;
2015-10-25 06:16:50 +01:00
2016-02-05 20:10:15 +01:00
internal bool KeepRunning { get ; private set ; } = false ;
2015-12-11 22:53:28 +01:00
internal SteamGuardAccount SteamGuardAccount { get ; private set ; }
2015-10-25 06:16:50 +01:00
// 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 ;
2016-02-03 05:43:50 +01:00
internal bool StartOnLaunch { get ; private set ; } = true ;
2015-11-21 18:27:30 +01:00
internal bool CardDropsRestricted { get ; private set ; } = false ;
2015-12-20 19:48:58 +01:00
internal bool FarmOffline { get ; private set ; } = false ;
internal bool HandleOfflineMessages { get ; private set ; } = false ;
2016-01-10 20:36:56 +01:00
internal bool ForwardKeysToOtherBots { get ; private set ; } = false ;
2016-01-25 09:42:59 +02:00
internal bool DistributeKeys { 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 ;
2016-01-13 11:17:58 +02:00
internal bool SendOnFarmingFinished { get ; private set ; } = false ;
2016-01-14 02:24:55 +01:00
internal string SteamTradeToken { get ; private set ; } = "null" ;
internal byte SendTradePeriod { get ; private set ; } = 0 ;
2016-02-22 18:34:45 +01:00
internal HashSet < uint > Blacklist { get ; } = new HashSet < uint > ( ) ;
2015-10-31 03:27:58 +01:00
internal bool Statistics { get ; private set ; } = true ;
2016-02-22 18:34:45 +01:00
private bool InvalidPassword = false ;
private bool LoggedInElsewhere = false ;
private string AuthCode , LoginKey , TwoFactorAuth ;
2016-01-07 22:29:55 +01:00
internal static string GetAnyBotName ( ) {
foreach ( string botName in Bots . Keys ) {
return botName ;
}
return null ;
}
2015-11-25 16:49:01 +01:00
2016-02-20 17:07:05 +01:00
internal static async Task RefreshCMs ( ) {
2016-01-24 17:38:45 +01:00
bool initialized = false ;
2016-02-21 22:21:17 +01:00
for ( byte i = 0 ; i < 3 & & ! initialized ; i + + ) {
2016-01-24 17:38:45 +01:00
try {
2016-01-24 18:08:27 +01:00
Logging . LogGenericInfo ( "Refreshing list of CMs..." ) ;
2016-02-20 17:07:05 +01:00
await SteamDirectory . Initialize ( ) . ConfigureAwait ( false ) ;
2016-01-24 17:38:45 +01:00
initialized = true ;
2016-01-25 20:08:30 +01:00
} catch ( Exception e ) {
Logging . LogGenericException ( e ) ;
2016-02-21 22:21:17 +01:00
await Utilities . SleepAsync ( 1000 ) . ConfigureAwait ( false ) ;
2016-01-24 17:38:45 +01:00
}
}
2016-02-21 22:19:55 +01:00
if ( initialized ) {
Logging . LogGenericInfo ( "Success!" ) ;
} else {
2016-02-21 22:21:17 +01:00
Logging . LogGenericWarning ( "Failed to initialize list of CMs after 3 tries, ASF will use built-in SK2 list, it may take a while to connect" ) ;
2016-02-21 22:19:55 +01:00
}
2016-01-24 17:38:45 +01:00
}
2016-02-22 18:34:45 +01:00
private static bool IsValidCdKey ( string key ) {
if ( string . IsNullOrEmpty ( key ) ) {
return false ;
}
// Steam keys are offered in many formats: https://support.steampowered.com/kb_article.php?ref=7480-WUSF-3601
// It's pointless to implement them all, so we'll just do a simple check if key is supposed to be valid
// Every valid key, apart from Prey one has at least two dashes
return Utilities . GetCharCountInString ( key , '-' ) > = 2 ;
}
2016-02-12 16:06:57 +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 ;
2016-02-22 18:34:45 +01:00
string botPath = Path . Combine ( Program . ConfigDirectory , botName ) ;
ConfigFile = botPath + ".xml" ;
LoginKeyFile = botPath + ".key" ;
MobileAuthenticatorFile = botPath + ".auth" ;
SentryFile = botPath + ".bin" ;
2015-10-25 06:16:50 +01:00
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
2016-02-12 16:06:57 +01:00
Bots [ botName ] = this ;
2016-02-05 20:10:15 +01:00
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 ) ;
2016-01-22 10:56:06 +01:00
SteamApps = SteamClient . GetHandler < SteamApps > ( ) ;
CallbackManager . Subscribe < SteamApps . FreeLicenseCallback > ( OnFreeLicense ) ;
2015-10-31 03:27:58 +01:00
SteamFriends = SteamClient . GetHandler < SteamFriends > ( ) ;
2015-12-21 19:28:59 +01:00
CallbackManager . Subscribe < SteamFriends . ChatInviteCallback > ( OnChatInvite ) ;
CallbackManager . Subscribe < SteamFriends . ChatMsgCallback > ( OnChatMsg ) ;
2015-10-31 03:27:58 +01:00
CallbackManager . Subscribe < SteamFriends . FriendsListCallback > ( OnFriendsList ) ;
CallbackManager . Subscribe < SteamFriends . FriendMsgCallback > ( OnFriendMsg ) ;
2015-12-21 10:55:55 +01:00
CallbackManager . Subscribe < SteamFriends . FriendMsgHistoryCallback > ( OnFriendMsgHistory ) ;
2015-10-31 03:27:58 +01:00
2015-12-11 22:53:28 +01:00
if ( UseAsfAsMobileAuthenticator & & File . Exists ( MobileAuthenticatorFile ) ) {
2016-02-12 16:06:57 +01:00
try {
SteamGuardAccount = JsonConvert . DeserializeObject < SteamGuardAccount > ( File . ReadAllText ( MobileAuthenticatorFile ) ) ;
} catch ( Exception e ) {
Logging . LogGenericException ( e , botName ) ;
}
2015-12-11 22:53:28 +01:00
}
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 ) ;
2016-01-02 17:22:35 +01:00
CallbackManager . Subscribe < ArchiHandler . NotificationsCallback > ( OnNotifications ) ;
2015-12-20 19:48:58 +01:00
CallbackManager . Subscribe < ArchiHandler . OfflineMessageCallback > ( OnOfflineMessage ) ;
2015-10-31 03:27:58 +01:00
CallbackManager . Subscribe < ArchiHandler . PurchaseResponseCallback > ( OnPurchaseResponse ) ;
ArchiWebHandler = new ArchiWebHandler ( this , SteamApiKey ) ;
CardsFarmer = new CardsFarmer ( this ) ;
Trading = new Trading ( this ) ;
2016-01-14 02:24:55 +01:00
if ( SendTradePeriod > 0 & & SendItemsTimer = = null ) {
SendItemsTimer = new Timer (
2016-02-12 16:06:57 +01:00
async e = > await ResponseSendTrade ( ) . ConfigureAwait ( false ) ,
2016-01-14 02:24:55 +01:00
null ,
TimeSpan . FromHours ( SendTradePeriod ) , // Delay
TimeSpan . FromHours ( SendTradePeriod ) // Period
2016-01-14 02:48:56 +01:00
) ;
2016-01-14 02:24:55 +01:00
}
2016-02-05 20:11:04 +01:00
2016-02-12 16:06:57 +01:00
if ( ! StartOnLaunch ) {
2016-02-05 20:11:04 +01:00
return ;
}
2016-01-14 02:24:55 +01:00
2015-10-31 03:27:58 +01:00
// Start
2016-02-20 02:41:43 +01:00
Start ( ) . Wait ( ) ;
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 ) ) {
2016-01-24 18:08:27 +01:00
Logging . LogGenericInfo ( "Accepting confirmation: Success!" , BotName ) ;
2015-12-13 15:25:00 +01:00
} else {
2016-01-24 18:08:27 +01:00
Logging . LogGenericWarning ( "Accepting confirmation: Failed!" , BotName ) ;
2015-12-13 15:25:00 +01:00
}
2015-12-11 22:53:28 +01:00
}
2015-12-13 15:25:00 +01:00
} catch ( SteamGuardAccount . WGTokenInvalidException ) {
2016-01-24 18:08:27 +01:00
Logging . LogGenericWarning ( "Accepting confirmation: Failed!" , BotName ) ;
Logging . LogGenericWarning ( "Confirmation could not be accepted because of invalid token exception" , BotName ) ;
Logging . LogGenericWarning ( "If issue persists, consider removing and readding ASF 2FA" , BotName ) ;
2015-12-11 22:53:28 +01:00
}
}
2015-12-22 16:51:37 +01:00
internal async Task Restart ( ) {
2016-02-05 20:10:15 +01:00
Stop ( ) ;
2016-02-20 02:44:01 +01:00
await Utilities . SleepAsync ( 500 ) . ConfigureAwait ( false ) ;
2015-12-22 16:51:37 +01:00
await Start ( ) . ConfigureAwait ( false ) ;
}
2015-11-04 04:42:13 +01:00
internal async Task Start ( ) {
2015-12-22 16:51:37 +01:00
if ( SteamClient . IsConnected ) {
2015-10-25 06:16:50 +01:00
return ;
}
2016-02-05 20:10:15 +01:00
if ( ! KeepRunning ) {
KeepRunning = true ;
var handleCallbacks = Task . Run ( ( ) = > HandleCallbacks ( ) ) ;
}
2016-01-24 18:08:27 +01:00
Logging . LogGenericInfo ( "Starting..." , BotName ) ;
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-10-25 06:16:50 +01:00
}
2016-02-05 20:10:15 +01:00
internal void Stop ( ) {
2015-12-22 16:51:37 +01:00
if ( ! SteamClient . IsConnected ) {
2015-10-25 06:16:50 +01:00
return ;
}
2016-01-24 18:08:27 +01:00
Logging . LogGenericInfo ( "Stopping..." , BotName ) ;
2015-12-22 16:51:37 +01:00
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
2016-02-05 20:10:15 +01:00
internal void Shutdown ( ) {
2016-01-04 00:02:18 +01:00
KeepRunning = false ;
2016-02-05 20:10:15 +01:00
Stop ( ) ;
2016-01-04 00:02:18 +01:00
Program . OnBotShutdown ( ) ;
}
2015-12-12 08:04:41 +01:00
2016-02-22 20:18:31 +01:00
internal async Task OnFarmingFinished ( bool farmedSomething ) {
if ( farmedSomething & & SendOnFarmingFinished ) {
await ResponseSendTrade ( ) . ConfigureAwait ( false ) ;
2016-01-13 11:17:58 +02:00
}
2015-10-31 03:27:58 +01:00
if ( ShutdownOnFarmingFinished ) {
2016-02-05 20:10:15 +01:00
Shutdown ( ) ;
2015-10-31 03:27:58 +01:00
}
2015-10-25 06:16:50 +01:00
}
2016-02-05 20:10:15 +01:00
internal string ResponseStatus ( ) {
if ( CardsFarmer . CurrentGamesFarming . Count > 0 ) {
return "Bot " + BotName + " is currently farming appIDs: " + string . Join ( ", " , CardsFarmer . CurrentGamesFarming ) + " and has a total of " + CardsFarmer . GamesToFarm . Count + " games left to farm." ;
} else {
return "Bot " + BotName + " is currently not farming anything." ;
}
}
2016-01-03 22:23:30 +01:00
internal static string ResponseStatus ( string botName ) {
if ( string . IsNullOrEmpty ( botName ) ) {
return null ;
2016-01-10 18:50:17 +02:00
}
2015-11-18 22:10:24 +01:00
2016-01-10 19:31:21 +01:00
Bot bot ;
2016-01-03 22:23:30 +01:00
if ( ! Bots . TryGetValue ( botName , out bot ) ) {
2016-01-10 19:31:21 +01:00
return "Couldn't find any bot named " + botName + "!" ;
2015-11-18 22:10:24 +01:00
}
2016-01-10 19:31:21 +01:00
2016-01-10 19:35:03 +01:00
return bot . ResponseStatus ( ) ;
}
2016-01-10 20:22:46 +02:00
2016-01-10 19:39:12 +02:00
internal static string ResponseStatusAll ( ) {
2016-01-10 19:31:21 +01:00
StringBuilder result = new StringBuilder ( Environment . NewLine ) ;
2016-02-05 20:10:15 +01:00
int totalBotsCount = Bots . Count ;
int runningBotsCount = 0 ;
2016-01-10 19:35:03 +01:00
foreach ( Bot bot in Bots . Values ) {
result . Append ( bot . ResponseStatus ( ) + Environment . NewLine ) ;
2016-02-05 20:10:15 +01:00
if ( bot . KeepRunning ) {
runningBotsCount + + ;
}
2016-01-10 19:39:12 +02:00
}
2016-01-10 19:35:03 +01:00
2016-02-05 20:10:15 +01:00
result . Append ( "There are " + totalBotsCount + " bots initialized and " + runningBotsCount + " of them are currently running." ) ;
2016-01-10 20:22:46 +02:00
return result . ToString ( ) ;
2016-01-10 19:39:12 +02:00
}
2016-02-05 20:10:15 +01:00
internal async Task < string > ResponseSendTrade ( ) {
if ( SteamMasterID = = 0 ) {
2016-01-14 01:30:12 +01:00
return "Trade couldn't be send because SteamMasterID is not defined!" ;
2016-01-13 11:17:58 +02:00
}
2016-01-14 01:30:12 +01:00
string token = null ;
2016-02-05 20:10:15 +01:00
if ( ! string . IsNullOrEmpty ( SteamTradeToken ) & & ! SteamTradeToken . Equals ( "null" ) ) {
token = SteamTradeToken ;
2016-01-13 11:17:58 +02:00
}
2016-01-14 01:30:12 +01:00
2016-01-26 23:05:53 +01:00
await Trading . LimitInventoryRequestsAsync ( ) . ConfigureAwait ( false ) ;
2016-02-16 08:25:27 +01:00
List < SteamItem > inventory = await ArchiWebHandler . GetMyTradableInventory ( ) . ConfigureAwait ( false ) ;
2016-01-26 23:05:53 +01:00
2016-01-14 02:48:56 +01:00
if ( inventory = = null | | inventory . Count = = 0 ) {
2016-01-14 01:30:12 +01:00
return "Nothing to send, inventory seems empty!" ;
2016-01-13 11:17:58 +02:00
}
2016-01-14 01:30:12 +01:00
2016-02-05 20:10:15 +01:00
if ( await ArchiWebHandler . SendTradeOffer ( inventory , SteamMasterID , token ) . ConfigureAwait ( false ) ) {
await AcceptAllConfirmations ( ) . ConfigureAwait ( false ) ;
2016-01-14 01:30:12 +01:00
return "Trade offer sent successfully!" ;
} else {
return "Trade offer failed due to error!" ;
2016-01-13 11:17:58 +02:00
}
}
2016-02-05 20:10:15 +01:00
internal static async Task < string > ResponseSendTrade ( string botName ) {
2016-01-04 00:02:18 +01:00
if ( string . IsNullOrEmpty ( botName ) ) {
return null ;
2015-12-11 22:53:28 +01:00
}
Bot bot ;
2016-01-04 00:02:18 +01:00
if ( ! Bots . TryGetValue ( botName , out bot ) ) {
return "Couldn't find any bot named " + botName + "!" ;
2015-12-11 22:53:28 +01:00
}
2016-02-05 20:10:15 +01:00
return await bot . ResponseSendTrade ( ) . ConfigureAwait ( false ) ;
}
internal string Response2FA ( ) {
if ( SteamGuardAccount = = null ) {
2016-01-04 00:02:18 +01:00
return "That bot doesn't have ASF 2FA enabled!" ;
2015-12-11 22:53:28 +01:00
}
long timeLeft = 30 - TimeAligner . GetSteamTime ( ) % 30 ;
2016-02-05 20:10:15 +01:00
return "2FA Token: " + SteamGuardAccount . GenerateSteamGuardCode ( ) + " (expires in " + timeLeft + " seconds)" ;
2015-12-11 22:53:28 +01:00
}
2016-02-05 20:10:15 +01:00
internal static string Response2FA ( string botName ) {
2016-01-04 00:02:18 +01:00
if ( string . IsNullOrEmpty ( botName ) ) {
return null ;
2015-12-11 22:53:28 +01:00
}
Bot bot ;
2016-01-04 00:02:18 +01:00
if ( ! Bots . TryGetValue ( botName , out bot ) ) {
return "Couldn't find any bot named " + botName + "!" ;
2015-12-11 22:53:28 +01:00
}
2016-02-05 20:10:15 +01:00
return bot . Response2FA ( ) ;
}
internal string Response2FAOff ( ) {
if ( SteamGuardAccount = = null ) {
2016-01-04 00:02:18 +01:00
return "That bot doesn't have ASF 2FA enabled!" ;
2015-12-11 22:53:28 +01:00
}
2016-02-05 20:10:15 +01:00
if ( DelinkMobileAuthenticator ( ) ) {
2016-01-04 00:02:18 +01:00
return "Done! Bot is no longer using ASF 2FA" ;
2015-12-11 22:53:28 +01:00
} else {
2016-01-04 00:02:18 +01:00
return "Something went wrong during delinking mobile authenticator!" ;
2015-12-11 22:53:28 +01:00
}
}
2016-02-05 20:10:15 +01:00
internal static string Response2FAOff ( string botName ) {
if ( string . IsNullOrEmpty ( botName ) ) {
return null ;
}
Bot bot ;
if ( ! Bots . TryGetValue ( botName , out bot ) ) {
return "Couldn't find any bot named " + botName + "!" ;
}
return bot . Response2FAOff ( ) ;
}
2016-01-11 04:49:12 +01:00
internal async Task < string > ResponseRedeem ( string message , bool validate ) {
2016-02-03 21:53:30 +01:00
if ( string . IsNullOrEmpty ( message ) ) {
return null ;
}
2016-01-10 20:36:56 +01:00
StringBuilder response = new StringBuilder ( ) ;
using ( StringReader reader = new StringReader ( message ) ) {
2016-01-25 19:15:22 +01:00
string key = reader . ReadLine ( ) ;
2016-01-25 09:42:59 +02:00
IEnumerator < Bot > iterator = Bots . Values . GetEnumerator ( ) ;
Bot currentBot = this ;
while ( key ! = null ) {
if ( currentBot = = null ) {
break ;
}
2016-02-03 21:53:30 +01:00
2016-01-11 04:49:12 +01:00
if ( validate & & ! IsValidCdKey ( key ) ) {
2016-02-09 10:26:51 +01:00
key = reader . ReadLine ( ) ;
2016-01-10 20:36:56 +01:00
continue ;
}
2016-01-04 00:02:18 +01:00
2016-01-10 20:36:56 +01:00
ArchiHandler . PurchaseResponseCallback result ;
try {
2016-01-25 09:42:59 +02:00
result = await currentBot . ArchiHandler . RedeemKey ( key ) ;
2016-01-10 20:36:56 +01:00
} catch ( Exception e ) {
2016-01-25 09:42:59 +02:00
Logging . LogGenericException ( e , currentBot . BotName ) ;
2016-01-10 20:36:56 +01:00
break ;
}
if ( result = = null ) {
break ;
}
var purchaseResult = result . PurchaseResult ;
var items = result . Items ;
switch ( purchaseResult ) {
case ArchiHandler . PurchaseResponseCallback . EPurchaseResult . AlreadyOwned :
case ArchiHandler . PurchaseResponseCallback . EPurchaseResult . BaseGameRequired :
case ArchiHandler . PurchaseResponseCallback . EPurchaseResult . OnCooldown :
case ArchiHandler . PurchaseResponseCallback . EPurchaseResult . RegionLocked :
2016-01-25 09:42:59 +02:00
response . Append ( Environment . NewLine + "<" + currentBot . BotName + "> Key: " + key + " | Status: " + purchaseResult + " | Items: " + string . Join ( "" , items ) ) ;
if ( DistributeKeys ) {
do {
if ( iterator . MoveNext ( ) ) {
currentBot = iterator . Current ;
} else {
currentBot = null ;
}
2016-01-25 19:15:22 +01:00
} while ( currentBot = = this ) ;
2016-01-25 09:42:59 +02:00
if ( ! ForwardKeysToOtherBots ) {
key = reader . ReadLine ( ) ;
}
break ;
}
2016-01-10 20:36:56 +01:00
if ( ! ForwardKeysToOtherBots ) {
2016-01-25 09:42:59 +02:00
key = reader . ReadLine ( ) ;
2016-01-10 20:36:56 +01:00
break ;
}
2016-01-10 20:45:53 +01:00
bool alreadyHandled = false ;
2016-01-10 20:36:56 +01:00
foreach ( Bot bot in Bots . Values ) {
2016-01-10 20:45:53 +01:00
if ( alreadyHandled ) {
break ;
}
2016-01-10 20:36:56 +01:00
if ( bot = = this ) {
continue ;
}
ArchiHandler . PurchaseResponseCallback otherResult ;
try {
2016-01-10 20:53:32 +01:00
otherResult = await bot . ArchiHandler . RedeemKey ( key ) ;
2016-01-10 20:36:56 +01:00
} catch ( Exception e ) {
2016-01-24 18:08:27 +01:00
Logging . LogGenericException ( e , bot . BotName ) ;
2016-01-10 20:36:56 +01:00
break ; // We're done with this key
}
if ( otherResult = = null ) {
break ; // We're done with this key
}
var otherPurchaseResult = otherResult . PurchaseResult ;
var otherItems = otherResult . Items ;
2016-01-10 20:45:53 +01:00
switch ( otherPurchaseResult ) {
case ArchiHandler . PurchaseResponseCallback . EPurchaseResult . OK :
alreadyHandled = true ; // We're done with this key
2016-01-10 20:53:32 +01:00
response . Append ( Environment . NewLine + "<" + bot . BotName + "> Key: " + key + " | Status: " + otherPurchaseResult + " | Items: " + string . Join ( "" , otherItems ) ) ;
2016-01-10 20:45:53 +01:00
break ;
case ArchiHandler . PurchaseResponseCallback . EPurchaseResult . DuplicatedKey :
case ArchiHandler . PurchaseResponseCallback . EPurchaseResult . InvalidKey :
alreadyHandled = true ; // This key doesn't work, don't try to redeem it anymore
2016-01-10 20:53:32 +01:00
response . Append ( Environment . NewLine + "<" + bot . BotName + "> Key: " + key + " | Status: " + otherPurchaseResult + " | Items: " + string . Join ( "" , otherItems ) ) ;
break ;
default :
response . Append ( Environment . NewLine + "<" + bot . BotName + "> Key: " + key + " | Status: " + otherPurchaseResult + " | Items: " + string . Join ( "" , otherItems ) ) ;
2016-01-10 20:45:53 +01:00
break ;
2016-01-10 20:36:56 +01:00
}
}
2016-01-25 09:42:59 +02:00
key = reader . ReadLine ( ) ;
2016-01-10 20:36:56 +01:00
break ;
case ArchiHandler . PurchaseResponseCallback . EPurchaseResult . OK :
2016-01-25 09:42:59 +02:00
response . Append ( Environment . NewLine + "<" + currentBot . BotName + "> Key: " + key + " | Status: " + purchaseResult + " | Items: " + string . Join ( "" , items ) ) ;
if ( DistributeKeys ) {
do {
if ( iterator . MoveNext ( ) ) {
currentBot = iterator . Current ;
} else {
currentBot = null ;
}
2016-01-25 19:15:22 +01:00
} while ( currentBot = = this ) ;
2016-01-25 09:42:59 +02:00
}
key = reader . ReadLine ( ) ;
2016-01-10 20:36:56 +01:00
break ;
case ArchiHandler . PurchaseResponseCallback . EPurchaseResult . DuplicatedKey :
case ArchiHandler . PurchaseResponseCallback . EPurchaseResult . InvalidKey :
2016-01-25 09:42:59 +02:00
response . Append ( Environment . NewLine + "<" + currentBot . BotName + "> Key: " + key + " | Status: " + purchaseResult + " | Items: " + string . Join ( "" , items ) ) ;
if ( DistributeKeys & & ! ForwardKeysToOtherBots ) {
do {
if ( iterator . MoveNext ( ) ) {
currentBot = iterator . Current ;
} else {
currentBot = null ;
}
2016-01-25 19:15:22 +01:00
} while ( currentBot = = this ) ;
2016-01-25 09:42:59 +02:00
}
key = reader . ReadLine ( ) ;
2016-01-25 19:15:22 +01:00
break ;
2016-01-10 20:36:56 +01:00
}
}
2016-01-02 17:22:35 +01:00
}
2016-01-10 20:36:56 +01:00
if ( response . Length = = 0 ) {
2016-01-04 00:02:18 +01:00
return null ;
2016-01-02 17:22:35 +01:00
}
2016-01-10 20:36:56 +01:00
return response . ToString ( ) ;
}
2016-01-11 04:49:12 +01:00
internal static async Task < string > ResponseRedeem ( string botName , string message , bool validate ) {
2016-01-10 20:36:56 +01:00
if ( string . IsNullOrEmpty ( botName ) | | string . IsNullOrEmpty ( message ) ) {
2016-01-04 00:02:18 +01:00
return null ;
2016-01-02 17:22:35 +01:00
}
2016-01-10 20:36:56 +01:00
Bot bot ;
if ( ! Bots . TryGetValue ( botName , out bot ) ) {
return "Couldn't find any bot named " + botName + "!" ;
}
2016-01-02 17:22:35 +01:00
2016-01-11 04:49:12 +01:00
return await bot . ResponseRedeem ( message , validate ) . ConfigureAwait ( false ) ;
2016-01-02 17:22:35 +01:00
}
2016-02-24 06:06:09 +01:00
internal static string ResponseRejoinChat ( ) {
foreach ( Bot bot in Bots . Values ) {
bot . JoinMasterChat ( ) ;
}
return "Done!" ;
}
2016-02-16 08:08:06 +01:00
internal async Task < string > ResponseAddLicense ( HashSet < uint > gameIDs ) {
if ( gameIDs = = null | | gameIDs . Count = = 0 ) {
2016-02-05 20:10:15 +01:00
return null ;
}
2016-02-16 08:08:06 +01:00
StringBuilder result = new StringBuilder ( ) ;
foreach ( uint gameID in gameIDs ) {
SteamApps . FreeLicenseCallback callback ;
try {
callback = await SteamApps . RequestFreeLicense ( gameID ) ;
} catch ( Exception e ) {
2016-02-16 08:33:11 +01:00
Logging . LogGenericException ( e , BotName ) ;
2016-02-16 08:08:06 +01:00
continue ;
}
result . AppendLine ( "Result: " + callback . Result + " | Granted apps: " + string . Join ( ", " , callback . GrantedApps ) + " " + string . Join ( ", " , callback . GrantedPackages ) ) ;
}
return result . ToString ( ) ;
2016-02-05 20:10:15 +01:00
}
2016-02-16 08:08:06 +01:00
internal static async Task < string > ResponseAddLicense ( string botName , string games ) {
if ( string . IsNullOrEmpty ( botName ) | | string . IsNullOrEmpty ( games ) ) {
2016-01-22 10:56:06 +01:00
return null ;
}
Bot bot ;
if ( ! Bots . TryGetValue ( botName , out bot ) ) {
return "Couldn't find any bot named " + botName + "!" ;
}
2016-02-16 08:08:06 +01:00
string [ ] gameIDs = games . Split ( ',' ) ;
HashSet < uint > gamesToRedeem = new HashSet < uint > ( ) ;
foreach ( string game in gameIDs ) {
uint gameID ;
if ( ! uint . TryParse ( game , out gameID ) ) {
continue ;
}
gamesToRedeem . Add ( gameID ) ;
}
if ( gamesToRedeem . Count = = 0 ) {
return "Couldn't parse any games given!" ;
2016-01-22 10:56:06 +01:00
}
2016-02-16 08:08:06 +01:00
return await bot . ResponseAddLicense ( gamesToRedeem ) . ConfigureAwait ( false ) ;
2016-02-05 20:10:15 +01:00
}
2016-02-13 23:21:47 +01:00
internal async Task < string > ResponsePlay ( HashSet < uint > gameIDs ) {
if ( gameIDs = = null | | gameIDs . Count = = 0 ) {
return null ;
}
if ( gameIDs . Contains ( 0 ) ) {
2016-02-13 23:31:58 +01:00
if ( await CardsFarmer . SwitchToManualMode ( false ) . ConfigureAwait ( false ) ) {
ArchiHandler . PlayGames ( 0 ) ;
}
2016-02-13 23:21:47 +01:00
} else {
await CardsFarmer . SwitchToManualMode ( true ) . ConfigureAwait ( false ) ;
ArchiHandler . PlayGames ( gameIDs ) ;
}
2016-02-05 20:10:15 +01:00
return "Done!" ;
2016-01-22 10:56:06 +01:00
}
2016-02-13 23:21:47 +01:00
internal static async Task < string > ResponsePlay ( string botName , string games ) {
if ( string . IsNullOrEmpty ( botName ) | | string . IsNullOrEmpty ( games ) ) {
2016-01-22 10:45:01 +01:00
return null ;
}
Bot bot ;
if ( ! Bots . TryGetValue ( botName , out bot ) ) {
return "Couldn't find any bot named " + botName + "!" ;
}
2016-02-13 23:21:47 +01:00
string [ ] gameIDs = games . Split ( ',' ) ;
HashSet < uint > gamesToPlay = new HashSet < uint > ( ) ;
foreach ( string game in gameIDs ) {
uint gameID ;
if ( ! uint . TryParse ( game , out gameID ) ) {
continue ;
}
gamesToPlay . Add ( gameID ) ;
}
if ( gamesToPlay . Count = = 0 ) {
return "Couldn't parse any games given!" ;
2016-01-22 10:45:01 +01:00
}
2016-02-13 23:21:47 +01:00
return await bot . ResponsePlay ( gamesToPlay ) . ConfigureAwait ( false ) ;
2016-02-05 20:10:15 +01:00
}
2016-01-22 10:45:01 +01:00
2016-02-05 20:10:15 +01:00
internal async Task < string > ResponseStart ( ) {
if ( KeepRunning ) {
return "That bot instance is already running!" ;
}
await Start ( ) . ConfigureAwait ( false ) ;
2016-01-22 10:45:01 +01:00
return "Done!" ;
}
2016-02-05 20:10:15 +01:00
internal static async Task < string > ResponseStart ( string botName ) {
2016-01-04 00:02:18 +01:00
if ( string . IsNullOrEmpty ( botName ) ) {
return null ;
2015-11-01 02:04:44 +01:00
}
2016-02-05 20:10:15 +01:00
Bot bot ;
if ( ! Bots . TryGetValue ( botName , out bot ) ) {
return "Couldn't find any bot named " + botName + "!" ;
2015-11-01 02:04:44 +01:00
}
2016-02-05 20:10:15 +01:00
return await bot . ResponseStart ( ) . ConfigureAwait ( false ) ;
}
internal string ResponseStop ( ) {
if ( ! KeepRunning ) {
return "That bot instance is already inactive!" ;
2015-11-01 02:04:44 +01:00
}
2016-02-05 20:10:15 +01:00
Shutdown ( ) ;
return "Done!" ;
2015-11-01 02:04:44 +01:00
}
2016-02-05 20:10:15 +01:00
internal static string ResponseStop ( string botName ) {
2016-01-04 00:02:18 +01:00
if ( string . IsNullOrEmpty ( botName ) ) {
return null ;
2015-11-01 02:04:44 +01:00
}
2016-01-04 00:02:18 +01:00
Bot bot ;
if ( ! Bots . TryGetValue ( botName , out bot ) ) {
2016-02-05 20:10:15 +01:00
return "Couldn't find any bot named " + botName + "!" ;
2015-11-01 02:04:44 +01:00
}
2016-02-05 20:10:15 +01:00
return bot . ResponseStop ( ) ;
2015-11-01 02:04:44 +01:00
}
2016-01-04 00:02:18 +01:00
internal async Task < string > HandleMessage ( string message ) {
if ( string . IsNullOrEmpty ( message ) ) {
return null ;
2015-12-21 10:55:55 +01:00
}
2016-01-10 20:36:56 +01:00
if ( ! message . StartsWith ( "!" ) ) {
2016-01-11 04:49:12 +01:00
return await ResponseRedeem ( BotName , message , true ) . ConfigureAwait ( false ) ;
2015-12-21 10:55:55 +01:00
}
if ( ! message . Contains ( " " ) ) {
switch ( message ) {
case "!2fa" :
2016-02-05 20:10:15 +01:00
return Response2FA ( ) ;
2015-12-21 10:55:55 +01:00
case "!2faoff" :
2016-02-05 20:10:15 +01:00
return Response2FAOff ( ) ;
2015-12-21 10:55:55 +01:00
case "!exit" :
2016-02-03 05:57:58 +01:00
Program . Exit ( ) ;
2016-02-12 16:21:33 +01:00
return null ;
2016-02-24 06:06:09 +01:00
case "!rejoinchat" :
return ResponseRejoinChat ( ) ;
2015-12-21 10:55:55 +01:00
case "!restart" :
2016-02-03 05:57:58 +01:00
Program . Restart ( ) ;
2016-01-04 00:02:18 +01:00
return "Done" ;
2015-12-21 10:55:55 +01:00
case "!status" :
2016-01-10 19:35:03 +01:00
return ResponseStatus ( ) ;
2016-01-10 19:39:12 +02:00
case "!statusall" :
return ResponseStatusAll ( ) ;
2015-12-21 10:55:55 +01:00
case "!stop" :
2016-02-05 20:10:15 +01:00
return ResponseStop ( ) ;
2016-01-13 11:17:58 +02:00
case "!loot" :
2016-02-05 20:10:15 +01:00
return await ResponseSendTrade ( ) . ConfigureAwait ( false ) ;
2016-01-04 00:02:18 +01:00
default :
return "Unrecognized command: " + message ;
2015-12-21 10:55:55 +01:00
}
} else {
string [ ] args = message . Split ( ' ' ) ;
switch ( args [ 0 ] ) {
case "!2fa" :
2016-01-04 00:02:18 +01:00
return Response2FA ( args [ 1 ] ) ;
2015-12-21 10:55:55 +01:00
case "!2faoff" :
2016-01-04 00:02:18 +01:00
return Response2FAOff ( args [ 1 ] ) ;
2016-01-22 10:56:06 +01:00
case "!addlicense" :
if ( args . Length > 2 ) {
return await ResponseAddLicense ( args [ 1 ] , args [ 2 ] ) . ConfigureAwait ( false ) ;
} else {
return await ResponseAddLicense ( BotName , args [ 1 ] ) . ConfigureAwait ( false ) ;
}
2016-01-22 10:45:01 +01:00
case "!play" :
if ( args . Length > 2 ) {
return await ResponsePlay ( args [ 1 ] , args [ 2 ] ) . ConfigureAwait ( false ) ;
} else {
return await ResponsePlay ( BotName , args [ 1 ] ) . ConfigureAwait ( false ) ;
}
2015-12-21 10:55:55 +01:00
case "!redeem" :
2016-01-04 00:02:18 +01:00
if ( args . Length > 2 ) {
2016-01-22 11:01:33 +01:00
return await ResponseRedeem ( args [ 1 ] , args [ 2 ] , false ) . ConfigureAwait ( false ) ;
2016-01-04 00:02:18 +01:00
} else {
2016-01-22 11:01:33 +01:00
return await ResponseRedeem ( BotName , args [ 1 ] , false ) . ConfigureAwait ( false ) ;
2016-01-04 00:02:18 +01:00
}
2015-12-21 10:55:55 +01:00
case "!start" :
2016-02-05 20:10:15 +01:00
return await ResponseStart ( args [ 1 ] ) . ConfigureAwait ( false ) ;
2015-12-21 10:55:55 +01:00
case "!stop" :
2016-02-05 20:10:15 +01:00
return ResponseStop ( args [ 1 ] ) ;
2015-12-21 10:55:55 +01:00
case "!status" :
2016-01-04 00:02:18 +01:00
return ResponseStatus ( args [ 1 ] ) ;
2016-01-13 11:17:58 +02:00
case "!loot" :
return await ResponseSendTrade ( args [ 1 ] ) . ConfigureAwait ( false ) ;
2016-01-04 00:02:18 +01:00
default :
return "Unrecognized command: " + args [ 0 ] ;
2015-12-21 10:55:55 +01:00
}
}
}
2016-02-22 18:34:45 +01:00
private void HandleCallbacks ( ) {
TimeSpan timeSpan = TimeSpan . FromMilliseconds ( CallbackSleep ) ;
while ( KeepRunning ) {
CallbackManager . RunWaitCallbacks ( timeSpan ) ;
}
}
2016-01-04 00:02:18 +01:00
private async Task HandleMessage ( ulong steamID , string message ) {
if ( steamID = = 0 | | string . IsNullOrEmpty ( message ) ) {
return ;
}
SendMessage ( steamID , await HandleMessage ( message ) . ConfigureAwait ( false ) ) ;
}
2016-02-22 18:34:45 +01:00
private void SendMessage ( ulong steamID , string message ) {
if ( steamID = = 0 | | string . IsNullOrEmpty ( message ) ) {
return ;
}
// TODO: I really need something better
if ( steamID < 110300000000000000 ) {
SteamFriends . SendChatMessage ( steamID , EChatEntryType . ChatMsg , message ) ;
} else {
SteamFriends . SendChatRoomMessage ( steamID , EChatEntryType . ChatMsg , message ) ;
}
}
private bool LinkMobileAuthenticator ( ) {
if ( SteamGuardAccount ! = null ) {
return false ;
}
Logging . LogGenericInfo ( "Linking new ASF MobileAuthenticator..." , BotName ) ;
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 ( "Unhandled situation: " + loginResult , BotName ) ;
return false ;
}
}
AuthenticatorLinker authenticatorLinker = new AuthenticatorLinker ( userLogin . Session ) ;
AuthenticatorLinker . LinkResult linkResult ;
while ( ( linkResult = authenticatorLinker . AddAuthenticator ( ) ) ! = AuthenticatorLinker . LinkResult . AwaitingFinalization ) {
switch ( linkResult ) {
case AuthenticatorLinker . LinkResult . MustProvidePhoneNumber :
authenticatorLinker . PhoneNumber = Program . GetUserInput ( BotName , Program . EUserInputType . PhoneNumber ) ;
break ;
default :
Logging . LogGenericError ( "Unhandled situation: " + linkResult , BotName ) ;
return false ;
}
}
SteamGuardAccount = authenticatorLinker . LinkedAccount ;
try {
File . WriteAllText ( MobileAuthenticatorFile , JsonConvert . SerializeObject ( SteamGuardAccount ) ) ;
} catch ( Exception e ) {
Logging . LogGenericException ( e , BotName ) ;
return false ;
}
AuthenticatorLinker . FinalizeResult finalizeResult = authenticatorLinker . FinalizeAddAuthenticator ( Program . GetUserInput ( BotName , Program . EUserInputType . SMS ) ) ;
if ( finalizeResult ! = AuthenticatorLinker . FinalizeResult . Success ) {
Logging . LogGenericError ( "Unhandled situation: " + finalizeResult , BotName ) ;
DelinkMobileAuthenticator ( ) ;
return false ;
}
Logging . LogGenericInfo ( "Successfully linked ASF as new mobile authenticator for this account!" , BotName ) ;
Program . GetUserInput ( BotName , Program . EUserInputType . RevocationCode , SteamGuardAccount . RevocationCode ) ;
return true ;
}
private bool DelinkMobileAuthenticator ( ) {
if ( SteamGuardAccount = = null ) {
return false ;
}
bool result = SteamGuardAccount . DeactivateAuthenticator ( ) ;
SteamGuardAccount = null ;
try {
File . Delete ( MobileAuthenticatorFile ) ;
} catch ( Exception e ) {
Logging . LogGenericException ( e , BotName ) ;
}
return result ;
}
2016-02-24 06:06:09 +01:00
private void JoinMasterChat ( ) {
if ( SteamMasterClanID = = 0 ) {
return ;
}
SteamFriends . JoinChat ( SteamMasterClanID ) ;
}
2015-10-25 06:16:50 +01:00
private void OnConnected ( SteamClient . ConnectedCallback callback ) {
if ( callback = = null ) {
return ;
}
if ( callback . Result ! = EResult . OK ) {
2016-01-24 18:08:27 +01:00
Logging . LogGenericError ( "Unable to connect to Steam: " + callback . Result , BotName ) ;
2015-10-25 06:16:50 +01:00
return ;
}
2016-01-24 18:08:27 +01:00
Logging . LogGenericInfo ( "Connected to Steam!" , BotName ) ;
2015-10-25 06:16:50 +01:00
2015-12-11 22:53:28 +01:00
if ( File . Exists ( LoginKeyFile ) ) {
2016-02-12 16:51:06 +01:00
try {
LoginKey = File . ReadAllText ( LoginKeyFile ) ;
} catch ( Exception e ) {
Logging . LogGenericException ( e , BotName ) ;
}
2015-12-11 22:53:28 +01:00
}
2015-10-25 06:16:50 +01:00
byte [ ] sentryHash = null ;
if ( File . Exists ( SentryFile ) ) {
2016-02-12 16:51:06 +01:00
try {
byte [ ] sentryFileContent = File . ReadAllBytes ( SentryFile ) ;
sentryHash = CryptoHelper . SHAHash ( sentryFileContent ) ;
} catch ( Exception e ) {
Logging . LogGenericException ( e , BotName ) ;
}
2015-10-25 06:16:50 +01:00
}
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-21 10:06:37 +01:00
SteamUser . LogOn ( new SteamUser . LogOnDetails {
2015-12-16 01:20:54 +01:00
Username = SteamLogin ,
Password = SteamPassword ,
AuthCode = AuthCode ,
2015-12-21 10:06:37 +01:00
LoginID = LoginID ,
2015-12-16 01:20:54 +01:00
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
2016-01-24 18:08:27 +01:00
Logging . LogGenericInfo ( "Disconnected from Steam!" , BotName ) ;
2015-12-22 16:51:37 +01:00
await CardsFarmer . StopFarming ( ) . ConfigureAwait ( false ) ;
2015-12-23 10:58:14 +01:00
if ( ! KeepRunning ) {
return ;
}
2015-12-22 16:51:37 +01:00
// If we initiated disconnect, do not attempt to reconnect
if ( callback . UserInitiated ) {
2015-11-01 02:04:44 +01:00
return ;
}
2015-12-23 10:58:14 +01:00
if ( InvalidPassword ) {
InvalidPassword = false ;
if ( ! string . IsNullOrEmpty ( LoginKey ) ) { // InvalidPassword means usually that login key has expired, if we used it
LoginKey = null ;
2016-02-12 16:51:06 +01:00
try {
File . Delete ( LoginKeyFile ) ;
} catch ( Exception e ) {
Logging . LogGenericException ( e , BotName ) ;
}
2016-01-24 18:08:27 +01:00
Logging . LogGenericInfo ( "Removed expired login key" , BotName ) ;
2015-12-23 10:58:14 +01:00
} else { // If we didn't use login key, InvalidPassword usually means we got captcha or other network-based throttling
2016-01-24 18:08:27 +01:00
Logging . LogGenericInfo ( "Will retry after 25 minutes..." , BotName ) ;
2015-12-23 10:58:14 +01:00
await Utilities . SleepAsync ( 25 * 60 * 1000 ) . ConfigureAwait ( false ) ; // Captcha disappears after around 20 minutes, so we make it 25
}
} else if ( LoggedInElsewhere ) {
2015-12-09 20:55:43 +01:00
LoggedInElsewhere = false ;
2016-01-24 18:08:27 +01:00
Logging . LogGenericWarning ( "Account is being used elsewhere, will try reconnecting in 30 minutes..." , BotName ) ;
2015-12-22 16:51:37 +01:00
await Utilities . SleepAsync ( 30 * 60 * 1000 ) . ConfigureAwait ( false ) ;
2015-12-09 20:55:43 +01:00
}
2016-01-24 18:08:27 +01:00
Logging . LogGenericInfo ( "Reconnecting..." , BotName ) ;
2015-12-22 16:51:37 +01:00
2015-12-21 09:28:59 +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-10-25 06:16:50 +01:00
SteamClient . Connect ( ) ;
}
2016-01-22 10:56:06 +01:00
private void OnFreeLicense ( SteamApps . FreeLicenseCallback callback ) {
if ( callback = = null ) {
return ;
}
}
2015-12-21 19:28:59 +01:00
private void OnChatInvite ( SteamFriends . ChatInviteCallback callback ) {
if ( callback = = null ) {
return ;
}
2016-02-12 16:51:06 +01:00
if ( callback . PatronID ! = SteamMasterID ) {
2015-12-21 19:28:59 +01:00
return ;
}
SteamFriends . JoinChat ( callback . ChatRoomID ) ;
}
private async void OnChatMsg ( SteamFriends . ChatMsgCallback callback ) {
if ( callback = = null ) {
return ;
}
if ( callback . ChatMsgType ! = EChatEntryType . ChatMsg ) {
return ;
}
2016-02-12 16:51:06 +01:00
if ( callback . ChatterID ! = SteamMasterID ) {
2015-12-21 19:28:59 +01:00
return ;
}
2016-02-12 16:21:33 +01:00
switch ( callback . Message ) {
case "!leave" :
SteamFriends . LeaveChat ( callback . ChatRoomID ) ;
break ;
default :
await HandleMessage ( callback . ChatRoomID , callback . Message ) . ConfigureAwait ( false ) ;
break ;
}
2015-12-21 19:28:59 +01:00
}
2015-10-25 06:16:50 +01:00
private void OnFriendsList ( SteamFriends . FriendsListCallback callback ) {
if ( callback = = null ) {
return ;
}
foreach ( var friend in callback . FriendList ) {
if ( friend . Relationship ! = EFriendRelationship . RequestRecipient ) {
continue ;
}
2016-02-12 16:51:06 +01:00
switch ( friend . SteamID . AccountType ) {
2015-10-25 06:16:50 +01:00
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 :
2016-02-12 16:51:06 +01:00
if ( friend . SteamID = = SteamMasterID ) {
SteamFriends . AddFriend ( friend . SteamID ) ;
2015-10-25 06:16:50 +01:00
}
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 ;
}
2016-02-12 16:51:06 +01:00
if ( callback . Sender ! = SteamMasterID ) {
2015-12-21 19:28:59 +01:00
return ;
}
2016-02-12 16:51:06 +01:00
await HandleMessage ( callback . Sender , callback . Message ) . ConfigureAwait ( false ) ;
2015-12-21 10:55:55 +01:00
}
private async void OnFriendMsgHistory ( SteamFriends . FriendMsgHistoryCallback callback ) {
if ( callback = = null ) {
2015-10-25 06:16:50 +01:00
return ;
}
2015-12-21 10:55:55 +01:00
if ( callback . Result ! = EResult . OK ) {
2015-10-25 06:16:50 +01:00
return ;
}
2016-02-12 16:51:06 +01:00
if ( callback . SteamID ! = SteamMasterID ) {
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
2016-02-12 16:51:06 +01:00
if ( callback . Messages . Count = = 0 ) {
2015-11-01 02:04:44 +01:00
return ;
}
2015-12-21 10:55:55 +01:00
// Get last message
2016-02-12 16:51:06 +01:00
var lastMessage = callback . Messages [ callback . Messages . Count - 1 ] ;
2015-12-21 10:55:55 +01:00
// If message is read already, return
if ( ! lastMessage . Unread ) {
return ;
2015-10-31 05:27:30 +01:00
}
2015-12-21 10:55:55 +01:00
// If message is too old, return
if ( DateTime . UtcNow . Subtract ( lastMessage . Timestamp ) . TotalMinutes > 1 ) {
return ;
}
// Handle the message
2016-02-12 16:51:06 +01:00
await HandleMessage ( callback . SteamID , lastMessage . Message ) . ConfigureAwait ( false ) ;
2015-10-25 06:16:50 +01:00
}
private void OnAccountInfo ( SteamUser . AccountInfoCallback callback ) {
if ( callback = = null ) {
return ;
}
2015-12-20 19:48:58 +01:00
if ( ! FarmOffline ) {
2015-12-20 19:01:05 +01:00
SteamFriends . SetPersonaState ( EPersonaState . Online ) ;
}
2015-10-25 06:16:50 +01:00
}
private void OnLoggedOff ( SteamUser . LoggedOffCallback callback ) {
if ( callback = = null ) {
return ;
}
2016-01-24 18:08:27 +01:00
Logging . LogGenericInfo ( "Logged off of Steam: " + callback . Result , BotName ) ;
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 ;
}
2016-02-12 16:51:06 +01:00
switch ( callback . Result ) {
2015-10-25 06:16:50 +01:00
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-23 10:58:14 +01:00
InvalidPassword = true ;
2016-02-12 16:51:06 +01:00
Logging . LogGenericWarning ( "Unable to login to Steam: " + callback . Result , BotName ) ;
2015-11-26 01:22:56 +01:00
break ;
2015-10-25 06:16:50 +01:00
case EResult . OK :
2016-01-24 18:08:27 +01:00
Logging . LogGenericInfo ( "Successfully logged on!" , BotName ) ;
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" ) ) {
2015-12-21 10:00:29 +01:00
await 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
}
2016-02-22 18:34:45 +01:00
if ( ! await ArchiWebHandler . Init ( SteamClient , callback . WebAPIUserNonce , SteamParentalPIN ) . ConfigureAwait ( false ) ) {
2015-12-25 17:49:00 +01:00
await Restart ( ) . ConfigureAwait ( false ) ;
return ;
}
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 ) ;
2016-02-24 06:06:09 +01:00
JoinMasterChat ( ) ;
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 ) {
2015-12-21 10:06:37 +01:00
await ArchiWebHandler . JoinClan ( ArchiSCFarmGroup ) . ConfigureAwait ( false ) ;
SteamFriends . JoinChat ( ArchiSCFarmGroup ) ;
2015-10-29 16:38:16 +01:00
}
2015-12-13 15:25:00 +01:00
Trading . CheckTrades ( ) ;
2016-01-22 10:45:01 +01:00
var start = Task . Run ( async ( ) = > await CardsFarmer . StartFarming ( ) . ConfigureAwait ( false ) ) ;
2015-10-25 06:16:50 +01:00
break ;
2015-12-23 16:34:57 +01:00
case EResult . NoConnection :
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 :
2016-02-12 16:51:06 +01:00
Logging . LogGenericWarning ( "Unable to login to Steam: " + callback . Result , BotName ) ;
2015-10-25 06:16:50 +01:00
break ;
2015-12-23 10:58:14 +01:00
default : // Unexpected result, shutdown immediately
2016-02-12 16:51:06 +01:00
Logging . LogGenericWarning ( "Unable to login to Steam: " + callback . Result , BotName ) ;
2016-02-05 20:10:15 +01:00
Shutdown ( ) ;
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 ;
}
2016-02-12 16:51:06 +01:00
try {
File . WriteAllText ( LoginKeyFile , callback . LoginKey ) ;
} catch ( Exception e ) {
Logging . LogGenericException ( e , BotName ) ;
}
2015-12-11 22:53:28 +01:00
SteamUser . AcceptNewLoginKey ( callback ) ;
}
2015-10-25 06:16:50 +01:00
private void OnMachineAuth ( SteamUser . UpdateMachineAuthCallback callback ) {
if ( callback = = null ) {
return ;
}
2016-02-12 16:51:06 +01:00
try {
2016-02-20 17:07:05 +01:00
int fileSize ;
byte [ ] sentryHash ;
2016-02-12 16:51:06 +01:00
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 ) ;
}
2015-10-25 06:16:50 +01:00
}
2016-02-12 16:51:06 +01:00
// 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 ,
} ) ;
} catch ( Exception e ) {
Logging . LogGenericException ( e , BotName ) ;
}
2015-10-25 06:16:50 +01:00
}
2016-01-02 17:22:35 +01:00
private void OnNotifications ( ArchiHandler . NotificationsCallback callback ) {
2015-10-25 06:16:50 +01:00
if ( callback = = null ) {
return ;
}
2016-01-02 17:22:35 +01:00
bool checkTrades = false ;
foreach ( var notification in callback . Notifications ) {
switch ( notification . NotificationType ) {
case ArchiHandler . NotificationsCallback . Notification . ENotificationType . Trading :
checkTrades = true ;
break ;
}
}
if ( checkTrades ) {
Trading . CheckTrades ( ) ;
2015-10-25 06:16:50 +01:00
}
}
2015-12-20 19:48:58 +01:00
private void OnOfflineMessage ( ArchiHandler . OfflineMessageCallback callback ) {
if ( callback = = null ) {
return ;
}
if ( ! HandleOfflineMessages ) {
return ;
}
2015-12-21 10:55:55 +01:00
SteamFriends . RequestOfflineMessages ( ) ;
2015-12-20 19:50:54 +01:00
}
2015-12-20 19:48:58 +01:00
2015-10-25 06:16:50 +01:00
private async void OnPurchaseResponse ( ArchiHandler . PurchaseResponseCallback callback ) {
if ( callback = = null ) {
return ;
}
2016-02-12 16:51:06 +01:00
if ( callback . PurchaseResult = = ArchiHandler . PurchaseResponseCallback . EPurchaseResult . OK ) {
2015-12-22 18:32:48 +01:00
// We will restart CF module to recalculate current status and decide about new optimal approach
await CardsFarmer . RestartFarming ( ) . ConfigureAwait ( false ) ;
2015-10-25 06:16:50 +01:00
}
}
2016-02-22 18:34:45 +01:00
private bool ReadConfig ( ) {
if ( ! File . Exists ( ConfigFile ) ) {
return false ;
}
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 "SteamTradeToken" :
SteamTradeToken = value ;
break ;
case "SteamParentalPIN" :
SteamParentalPIN = value ;
break ;
case "SteamMasterID" :
SteamMasterID = ulong . Parse ( value ) ;
break ;
case "SteamMasterClanID" :
SteamMasterClanID = ulong . Parse ( value ) ;
break ;
case "StartOnLaunch" :
StartOnLaunch = bool . Parse ( value ) ;
break ;
case "UseAsfAsMobileAuthenticator" :
UseAsfAsMobileAuthenticator = bool . Parse ( value ) ;
break ;
case "CardDropsRestricted" :
CardDropsRestricted = bool . Parse ( value ) ;
break ;
case "FarmOffline" :
FarmOffline = bool . Parse ( value ) ;
break ;
case "HandleOfflineMessages" :
HandleOfflineMessages = bool . Parse ( value ) ;
break ;
case "ForwardKeysToOtherBots" :
ForwardKeysToOtherBots = bool . Parse ( value ) ;
break ;
case "DistributeKeys" :
DistributeKeys = bool . Parse ( value ) ;
break ;
case "ShutdownOnFarmingFinished" :
ShutdownOnFarmingFinished = bool . Parse ( value ) ;
break ;
case "SendOnFarmingFinished" :
SendOnFarmingFinished = bool . Parse ( value ) ;
break ;
case "SendTradePeriod" :
SendTradePeriod = byte . Parse ( value ) ;
break ;
case "Blacklist" :
Blacklist . Clear ( ) ;
foreach ( string appID in value . Split ( ',' ) ) {
Blacklist . Add ( uint . Parse ( appID ) ) ;
}
break ;
case "Statistics" :
Statistics = bool . Parse ( value ) ;
break ;
default :
Logging . LogGenericWarning ( "Unrecognized config value: " + key + "=" + value , BotName ) ;
break ;
}
}
}
} catch ( Exception e ) {
Logging . LogGenericException ( e , BotName ) ;
Logging . LogGenericError ( "Your config for this bot instance is invalid, it won't run!" , BotName ) ;
return false ;
}
return true ;
}
2015-10-25 06:16:50 +01:00
}
}