Files
ArchiSteamFarm/ArchiSteamFarm/Bot.cs
2016-01-01 12:46:49 +01:00

967 lines
28 KiB
C#
Executable File

/*
_ _ _ ____ _ _____
/ \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
/ _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
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.
*/
using Newtonsoft.Json;
using SteamAuth;
using SteamKit2;
using SteamKit2.Internal;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Security.Cryptography;
using System.Threading.Tasks;
using System.Xml;
namespace ArchiSteamFarm {
internal sealed class Bot {
private const ulong ArchiSCFarmGroup = 103582791440160998;
private const ushort CallbackSleep = 500; // In miliseconds
private static readonly ConcurrentDictionary<string, Bot> Bots = new ConcurrentDictionary<string, Bot>();
private static readonly uint LoginID = MsgClientLogon.ObfuscationMask; // This must be the same for all ASF bots and all ASF processes
internal static readonly HashSet<uint> GlobalBlacklist = new HashSet<uint> { 303700, 335590, 368020, 425280 };
private readonly string ConfigFile, LoginKeyFile, MobileAuthenticatorFile, SentryFile;
internal readonly string BotName;
internal readonly ArchiHandler ArchiHandler;
internal readonly ArchiWebHandler ArchiWebHandler;
internal readonly CallbackManager CallbackManager;
internal readonly CardsFarmer CardsFarmer;
internal readonly SteamClient SteamClient;
internal readonly SteamFriends SteamFriends;
internal readonly SteamUser SteamUser;
internal readonly Trading Trading;
private bool KeepRunning = true;
private bool InvalidPassword = false;
private bool LoggedInElsewhere = false;
private string AuthCode, LoginKey, TwoFactorAuth;
internal SteamGuardAccount SteamGuardAccount { get; private set; }
// Config variables
internal bool Enabled { get; private set; } = false;
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";
internal ulong SteamMasterID { get; private set; } = 0;
internal ulong SteamMasterClanID { get; private set; } = 0;
internal bool CardDropsRestricted { get; private set; } = false;
internal bool FarmOffline { get; private set; } = false;
internal bool HandleOfflineMessages { get; private set; } = false;
internal bool UseAsfAsMobileAuthenticator { get; private set; } = false;
internal bool ShutdownOnFarmingFinished { get; private set; } = false;
internal HashSet<uint> Blacklist { get; private set; } = new HashSet<uint>();
internal bool Statistics { get; private set; } = true;
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;
}
internal static int GetRunningBotsCount() {
return Bots.Count;
}
internal static async Task ShutdownAllBots() {
List<Task> tasks = new List<Task>();
foreach (Bot bot in Bots.Values) {
tasks.Add(Task.Run(async () => await bot.Shutdown().ConfigureAwait(false)));
}
await Task.WhenAll(tasks).ConfigureAwait(false);
}
internal Bot(string botName) {
if (Bots.ContainsKey(botName)) {
return;
}
BotName = botName;
ConfigFile = Path.Combine(Program.ConfigDirectory, BotName + ".xml");
LoginKeyFile = Path.Combine(Program.ConfigDirectory, BotName + ".key");
MobileAuthenticatorFile = Path.Combine(Program.ConfigDirectory, BotName + ".auth");
SentryFile = Path.Combine(Program.ConfigDirectory, BotName + ".bin");
if (!ReadConfig()) {
return;
}
if (!Enabled) {
return;
}
Bots.AddOrUpdate(BotName, this, (key, value) => this);
// 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.ChatInviteCallback>(OnChatInvite);
CallbackManager.Subscribe<SteamFriends.ChatMsgCallback>(OnChatMsg);
CallbackManager.Subscribe<SteamFriends.FriendsListCallback>(OnFriendsList);
CallbackManager.Subscribe<SteamFriends.FriendMsgCallback>(OnFriendMsg);
CallbackManager.Subscribe<SteamFriends.FriendMsgHistoryCallback>(OnFriendMsgHistory);
if (UseAsfAsMobileAuthenticator && File.Exists(MobileAuthenticatorFile)) {
SteamGuardAccount = JsonConvert.DeserializeObject<SteamGuardAccount>(File.ReadAllText(MobileAuthenticatorFile));
}
SteamUser = SteamClient.GetHandler<SteamUser>();
CallbackManager.Subscribe<SteamUser.AccountInfoCallback>(OnAccountInfo);
CallbackManager.Subscribe<SteamUser.LoggedOffCallback>(OnLoggedOff);
CallbackManager.Subscribe<SteamUser.LoggedOnCallback>(OnLoggedOn);
CallbackManager.Subscribe<SteamUser.LoginKeyCallback>(OnLoginKey);
CallbackManager.Subscribe<SteamUser.UpdateMachineAuthCallback>(OnMachineAuth);
CallbackManager.Subscribe<ArchiHandler.NotificationCallback>(OnNotification);
CallbackManager.Subscribe<ArchiHandler.OfflineMessageCallback>(OnOfflineMessage);
CallbackManager.Subscribe<ArchiHandler.PurchaseResponseCallback>(OnPurchaseResponse);
ArchiWebHandler = new ArchiWebHandler(this, SteamApiKey);
CardsFarmer = new CardsFarmer(this);
Trading = new Trading(this);
// Start
var handleCallbacks = Task.Run(() => HandleCallbacks());
var start = Task.Run(async () => await Start().ConfigureAwait(false));
}
internal async Task AcceptAllConfirmations() {
if (SteamGuardAccount == null) {
return;
}
await SteamGuardAccount.RefreshSessionAsync().ConfigureAwait(false);
try {
foreach (Confirmation confirmation in await SteamGuardAccount.FetchConfirmationsAsync().ConfigureAwait(false)) {
if (SteamGuardAccount.AcceptConfirmation(confirmation)) {
Logging.LogGenericInfo(BotName, "Accepting confirmation: Success!");
} else {
Logging.LogGenericWarning(BotName, "Accepting confirmation: Failed!");
}
}
} 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");
}
}
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);
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(BotName, "Unhandled situation: " + linkResult);
return false;
}
}
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;
}
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 "SteamParentalPIN":
SteamParentalPIN = value;
break;
case "SteamMasterID":
SteamMasterID = ulong.Parse(value);
break;
case "SteamMasterClanID":
SteamMasterClanID = ulong.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 "ShutdownOnFarmingFinished":
ShutdownOnFarmingFinished = bool.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(BotName, "Unrecognized config value: " + key + "=" + value);
break;
}
}
}
} catch (Exception e) {
Logging.LogGenericException(BotName, e);
Logging.LogGenericError(BotName, "Your config for this bot instance is invalid, it won't run!");
return false;
}
return true;
}
internal async Task Restart() {
await Stop().ConfigureAwait(false);
await Start().ConfigureAwait(false);
}
internal async Task Start() {
if (SteamClient.IsConnected) {
return;
}
Logging.LogGenericInfo(BotName, "Starting...");
// 2FA tokens are expiring soon, use limiter only when we don't have any pending
if (TwoFactorAuth == null) {
await Program.LimitSteamRequestsAsync().ConfigureAwait(false);
}
SteamClient.Connect();
}
internal async Task Stop() {
if (!SteamClient.IsConnected) {
return;
}
await Utilities.SleepAsync(0); // TODO: This is here only to make VS happy, for now
Logging.LogGenericInfo(BotName, "Stopping...");
SteamClient.Disconnect();
}
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;
}
}
bot.KeepRunning = false;
await bot.Stop().ConfigureAwait(false);
Bots.TryRemove(bot.BotName, out bot);
Program.OnBotShutdown();
return true;
}
internal async Task OnFarmingFinished() {
if (ShutdownOnFarmingFinished) {
await Shutdown().ConfigureAwait(false);
}
}
private void HandleCallbacks() {
TimeSpan timeSpan = TimeSpan.FromMilliseconds(CallbackSleep);
while (KeepRunning) {
CallbackManager.RunWaitCallbacks(timeSpan);
}
}
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 void ResponseStatus(ulong steamID, string botName = null) {
if (steamID == 0) {
return;
}
Bot bot;
if (string.IsNullOrEmpty(botName)) {
bot = this;
} else {
if (!Bots.TryGetValue(botName, out bot)) {
SendMessage(steamID, "Couldn't find any bot named " + botName + "!");
return;
}
}
if (bot.CardsFarmer.CurrentGamesFarming.Count > 0) {
SendMessage(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");
}
SendMessage(steamID, "Currently " + Bots.Count + " bots are running");
}
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)) {
SendMessage(steamID, "Couldn't find any bot named " + botName + "!");
return;
}
}
if (bot.SteamGuardAccount == null) {
SendMessage(steamID, "That bot doesn't have ASF 2FA enabled!");
return;
}
long timeLeft = 30 - TimeAligner.GetSteamTime() % 30;
SendMessage(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)) {
SendMessage(steamID, "Couldn't find any bot named " + botName + "!");
return;
}
}
if (bot.SteamGuardAccount == null) {
SendMessage(steamID, "That bot doesn't have ASF 2FA enabled!");
return;
}
if (bot.DelinkMobileAuthenticator()) {
SendMessage(steamID, "Done! Bot is no longer using ASF 2FA");
} else {
SendMessage(steamID, "Something went wrong!");
}
}
private void ResponseStart(ulong steamID, string botName) {
if (steamID == 0 || string.IsNullOrEmpty(botName)) {
return;
}
if (Bots.ContainsKey(botName)) {
SendMessage(steamID, "That bot instance is already running!");
return;
}
new Bot(botName);
if (Bots.ContainsKey(botName)) {
SendMessage(steamID, "Done!");
} else {
SendMessage(steamID, "That bot instance failed to start, make sure that XML config exists and bot is active!");
}
}
private async Task ResponseStop(ulong steamID, string botName) {
if (steamID == 0 || string.IsNullOrEmpty(botName)) {
return;
}
if (!Bots.ContainsKey(botName)) {
SendMessage(steamID, "That bot instance is already inactive!");
return;
}
if (await Shutdown(botName).ConfigureAwait(false)) {
SendMessage(steamID, "Done!");
} else {
SendMessage(steamID, "That bot instance failed to shutdown!");
}
}
private async Task HandleMessage(ulong steamID, string message) {
if (IsValidCdKey(message)) {
ArchiHandler.RedeemKey(message);
return;
}
if (!message.StartsWith("!")) {
return;
}
if (!message.Contains(" ")) {
switch (message) {
case "!2fa":
Response2FA(steamID);
break;
case "!2faoff":
Response2FAOff(steamID);
break;
case "!exit":
await ShutdownAllBots().ConfigureAwait(false);
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]) {
case "!2fa":
Response2FA(steamID, args[1]);
break;
case "!2faoff":
Response2FAOff(steamID, args[1]);
break;
case "!redeem":
ArchiHandler.RedeemKey(args[1]);
break;
case "!start":
ResponseStart(steamID, args[1]);
break;
case "!stop":
await ResponseStop(steamID, args[1]).ConfigureAwait(false);
break;
case "!status":
ResponseStatus(steamID, args[1]);
break;
}
}
}
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!");
if (File.Exists(LoginKeyFile)) {
LoginKey = File.ReadAllText(LoginKeyFile);
}
byte[] sentryHash = null;
if (File.Exists(SentryFile)) {
byte[] sentryFileContent = File.ReadAllBytes(SentryFile);
sentryHash = CryptoHelper.SHAHash(sentryFileContent);
}
if (SteamLogin.Equals("null")) {
SteamLogin = Program.GetUserInput(BotName, Program.EUserInputType.Login);
}
if (SteamPassword.Equals("null") && string.IsNullOrEmpty(LoginKey)) {
SteamPassword = Program.GetUserInput(BotName, Program.EUserInputType.Password);
}
SteamUser.LogOn(new SteamUser.LogOnDetails {
Username = SteamLogin,
Password = SteamPassword,
AuthCode = AuthCode,
LoginID = LoginID,
LoginKey = LoginKey,
TwoFactorCode = TwoFactorAuth,
SentryFileHash = sentryHash,
ShouldRememberPassword = true
});
}
private async void OnDisconnected(SteamClient.DisconnectedCallback callback) {
if (callback == null) {
return;
}
Logging.LogGenericInfo(BotName, "Disconnected from Steam!");
await CardsFarmer.StopFarming().ConfigureAwait(false);
if (!KeepRunning) {
return;
}
// If we initiated disconnect, do not attempt to reconnect
if (callback.UserInitiated) {
return;
}
if (InvalidPassword) {
InvalidPassword = false;
if (!string.IsNullOrEmpty(LoginKey)) { // InvalidPassword means usually that login key has expired, if we used it
LoginKey = null;
File.Delete(LoginKeyFile);
Logging.LogGenericInfo(BotName, "Removed expired login key");
} 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
}
} else if (LoggedInElsewhere) {
LoggedInElsewhere = false;
Logging.LogGenericWarning(BotName, "Account is being used elsewhere, will try reconnecting in 30 minutes...");
await Utilities.SleepAsync(30 * 60 * 1000).ConfigureAwait(false);
}
Logging.LogGenericInfo(BotName, "Reconnecting...");
// 2FA tokens are expiring soon, use limiter only when we don't have any pending
if (TwoFactorAuth == null) {
await Program.LimitSteamRequestsAsync().ConfigureAwait(false);
}
SteamClient.Connect();
}
private void OnChatInvite(SteamFriends.ChatInviteCallback callback) {
if (callback == null) {
return;
}
ulong steamID = callback.PatronID;
if (steamID != SteamMasterID) {
return;
}
SteamFriends.JoinChat(callback.ChatRoomID);
}
private async void OnChatMsg(SteamFriends.ChatMsgCallback callback) {
if (callback == null) {
return;
}
if (callback.ChatMsgType != EChatEntryType.ChatMsg) {
return;
}
ulong steamID = callback.ChatterID;
if (steamID != SteamMasterID) {
return;
}
await HandleMessage(callback.ChatRoomID, callback.Message).ConfigureAwait(false);
}
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:
// TODO: Accept clan invites from master?
break;
default:
if (steamID == SteamMasterID) {
SteamFriends.AddFriend(steamID);
}
break;
}
}
}
private async void OnFriendMsg(SteamFriends.FriendMsgCallback callback) {
if (callback == null) {
return;
}
if (callback.EntryType != EChatEntryType.ChatMsg) {
return;
}
ulong steamID = callback.Sender;
if (steamID != SteamMasterID) {
return;
}
await HandleMessage(steamID, callback.Message).ConfigureAwait(false);
}
private async void OnFriendMsgHistory(SteamFriends.FriendMsgHistoryCallback callback) {
if (callback == null) {
return;
}
if (callback.Result != EResult.OK) {
return;
}
ulong steamID = callback.SteamID;
if (steamID != SteamMasterID) {
return;
}
var messages = callback.Messages;
if (messages.Count == 0) {
return;
}
// Get last message
var lastMessage = messages[messages.Count - 1];
// If message is read already, return
if (!lastMessage.Unread) {
return;
}
// If message is too old, return
if (DateTime.UtcNow.Subtract(lastMessage.Timestamp).TotalMinutes > 1) {
return;
}
// Handle the message
await HandleMessage(steamID, lastMessage.Message).ConfigureAwait(false);
}
private void OnAccountInfo(SteamUser.AccountInfoCallback callback) {
if (callback == null) {
return;
}
if (!FarmOffline) {
SteamFriends.SetPersonaState(EPersonaState.Online);
}
}
private void OnLoggedOff(SteamUser.LoggedOffCallback callback) {
if (callback == null) {
return;
}
Logging.LogGenericInfo(BotName, "Logged off of Steam: " + callback.Result);
switch (callback.Result) {
case EResult.AlreadyLoggedInElsewhere:
case EResult.LoggedInElsewhere:
case EResult.LogonSessionReplaced:
LoggedInElsewhere = true;
break;
}
}
private async void OnLoggedOn(SteamUser.LoggedOnCallback callback) {
if (callback == null) {
return;
}
EResult result = callback.Result;
switch (result) {
case EResult.AccountLogonDenied:
AuthCode = Program.GetUserInput(SteamLogin, Program.EUserInputType.SteamGuard);
break;
case EResult.AccountLoginDeniedNeedTwoFactor:
if (SteamGuardAccount == null) {
TwoFactorAuth = Program.GetUserInput(SteamLogin, Program.EUserInputType.TwoFactorAuthentication);
} else {
TwoFactorAuth = SteamGuardAccount.GenerateSteamGuardCode();
}
break;
case EResult.InvalidPassword:
InvalidPassword = true;
Logging.LogGenericWarning(BotName, "Unable to login to Steam: " + result);
break;
case EResult.OK:
Logging.LogGenericInfo(BotName, "Successfully logged on!");
if (UseAsfAsMobileAuthenticator && TwoFactorAuth == null && SteamGuardAccount == null) {
LinkMobileAuthenticator();
}
// Reset one-time-only access tokens
AuthCode = null;
TwoFactorAuth = null;
if (!SteamNickname.Equals("null")) {
await SteamFriends.SetPersonaName(SteamNickname);
}
if (SteamParentalPIN.Equals("null")) {
SteamParentalPIN = Program.GetUserInput(BotName, Program.EUserInputType.SteamParentalPIN);
}
if (!await ArchiWebHandler.Init(SteamClient, callback.WebAPIUserNonce, callback.VanityURL, SteamParentalPIN).ConfigureAwait(false)) {
await Restart().ConfigureAwait(false);
return;
}
if (SteamMasterClanID != 0) {
await ArchiWebHandler.JoinClan(SteamMasterClanID).ConfigureAwait(false);
SteamFriends.JoinChat(SteamMasterClanID);
}
if (Statistics) {
await ArchiWebHandler.JoinClan(ArchiSCFarmGroup).ConfigureAwait(false);
SteamFriends.JoinChat(ArchiSCFarmGroup);
}
Trading.CheckTrades();
await CardsFarmer.StartFarming().ConfigureAwait(false);
break;
case EResult.NoConnection:
case EResult.ServiceUnavailable:
case EResult.Timeout:
case EResult.TryAnotherCM:
Logging.LogGenericWarning(BotName, "Unable to login to Steam: " + result);
break;
default: // Unexpected result, shutdown immediately
Logging.LogGenericWarning(BotName, "Unable to login to Steam: " + result);
await Shutdown().ConfigureAwait(false);
break;
}
}
private void OnLoginKey(SteamUser.LoginKeyCallback callback) {
if (callback == null) {
return;
}
File.WriteAllText(LoginKeyFile, callback.LoginKey);
SteamUser.AcceptNewLoginKey(callback);
}
private void OnMachineAuth(SteamUser.UpdateMachineAuthCallback callback) {
if (callback == null) {
return;
}
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,
});
}
private void OnNotification(ArchiHandler.NotificationCallback callback) {
if (callback == null) {
return;
}
switch (callback.NotificationType) {
case ArchiHandler.NotificationCallback.ENotificationType.Trading:
Trading.CheckTrades();
break;
}
}
private void OnOfflineMessage(ArchiHandler.OfflineMessageCallback callback) {
if (callback == null) {
return;
}
if (!HandleOfflineMessages) {
return;
}
SteamFriends.RequestOfflineMessages();
}
private async void OnPurchaseResponse(ArchiHandler.PurchaseResponseCallback callback) {
if (callback == null) {
return;
}
var purchaseResult = callback.PurchaseResult;
var items = callback.Items;
SendMessage(SteamMasterID, "Status: " + purchaseResult + " | Items: " + string.Join("", items));
if (purchaseResult == ArchiHandler.PurchaseResponseCallback.EPurchaseResult.OK) {
// We will restart CF module to recalculate current status and decide about new optimal approach
await CardsFarmer.RestartFarming().ConfigureAwait(false);
}
}
}
}