Skip to content

Commit

Permalink
Squashed commit of ASF 2FA
Browse files Browse the repository at this point in the history
  • Loading branch information
JustArchi committed Dec 11, 2015
1 parent 9a2a37f commit b3162ce
Show file tree
Hide file tree
Showing 23 changed files with 1,441 additions and 29 deletions.
6 changes: 6 additions & 0 deletions ArchiSteamFarm.sln
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ VisualStudioVersion = 14.0.23107.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ArchiSteamFarm", "ArchiSteamFarm\ArchiSteamFarm.csproj", "{35AF7887-08B9-40E8-A5EA-797D8B60B30C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SteamAuth", "SteamAuth\SteamAuth.csproj", "{5AD0934E-F6C4-4AE5-83AF-C788313B2A87}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -15,6 +17,10 @@ Global
{35AF7887-08B9-40E8-A5EA-797D8B60B30C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{35AF7887-08B9-40E8-A5EA-797D8B60B30C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{35AF7887-08B9-40E8-A5EA-797D8B60B30C}.Release|Any CPU.Build.0 = Release|Any CPU
{5AD0934E-F6C4-4AE5-83AF-C788313B2A87}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5AD0934E-F6C4-4AE5-83AF-C788313B2A87}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5AD0934E-F6C4-4AE5-83AF-C788313B2A87}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5AD0934E-F6C4-4AE5-83AF-C788313B2A87}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
6 changes: 3 additions & 3 deletions ArchiSteamFarm/App.config
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2"/>
</startup>
</configuration>
</configuration>
9 changes: 8 additions & 1 deletion ArchiSteamFarm/ArchiSteamFarm.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>ArchiSteamFarm</RootNamespace>
<AssemblyName>ArchiSteamFarm</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<IsWebBootstrapper>false</IsWebBootstrapper>
<PublishUrl>publish\</PublishUrl>
Expand All @@ -26,6 +26,7 @@
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
<UseApplicationTrust>false</UseApplicationTrust>
<BootstrapperEnabled>true</BootstrapperEnabled>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
Expand Down Expand Up @@ -123,6 +124,12 @@
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\SteamAuth\SteamAuth.csproj">
<Project>{5ad0934e-f6c4-4ae5-83af-c788313b2a87}</Project>
<Name>SteamAuth</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<PreBuildEvent>
Expand Down
4 changes: 3 additions & 1 deletion ArchiSteamFarm/ArchiWebHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,9 @@ internal List<SteamTradeOffer> GetTradeOffers() {
is_our_offer = trade["is_our_offer"].AsBoolean(),
time_created = trade["time_created"].AsInteger(),
time_updated = trade["time_updated"].AsInteger(),
from_real_time_trade = trade["from_real_time_trade"].AsBoolean()
from_real_time_trade = trade["from_real_time_trade"].AsBoolean(),
escrow_end_date = trade["escrow_end_date"].AsInteger(),
confirmation_method = (SteamTradeOffer.ETradeOfferConfirmationMethod) trade["confirmation_method"].AsInteger()
};
foreach (KeyValue item in trade["items_to_give"].Children) {
tradeOffer.items_to_give.Add(new SteamItem {
Expand Down
196 changes: 191 additions & 5 deletions ArchiSteamFarm/Bot.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ limitations under the License.
*/

using Newtonsoft.Json;
using SteamAuth;
using SteamKit2;
using System;
using System.Collections.Concurrent;
Expand All @@ -37,20 +39,21 @@ internal sealed class Bot {

private static readonly ConcurrentDictionary<string, Bot> Bots = new ConcurrentDictionary<string, Bot>();

private readonly string ConfigFile, SentryFile;
private readonly string ConfigFile, LoginKeyFile, MobileAuthenticatorFile, SentryFile;

internal readonly string BotName;

private bool LoggedInElsewhere = false;
private bool IsRunning = false;
private string AuthCode, TwoFactorAuth;
private string AuthCode, LoginKey, TwoFactorAuth;

internal ArchiHandler ArchiHandler { get; private set; }
internal ArchiWebHandler ArchiWebHandler { get; private set; }
internal CallbackManager CallbackManager { get; private set; }
internal CardsFarmer CardsFarmer { get; private set; }
internal SteamClient SteamClient { get; private set; }
internal SteamFriends SteamFriends { get; private set; }
internal SteamGuardAccount SteamGuardAccount { get; private set; }
internal SteamUser SteamUser { get; private set; }
internal Trading Trading { get; private set; }

Expand All @@ -64,6 +67,7 @@ internal sealed class Bot {
internal ulong SteamMasterID { get; private set; } = 0;
internal ulong SteamMasterClanID { get; private set; } = 0;
internal bool CardDropsRestricted { 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> { 303700, 335590, 368020 };
internal bool Statistics { get; private set; } = true;
Expand Down Expand Up @@ -106,6 +110,8 @@ internal Bot(string botName) {
BotName = botName;

ConfigFile = Path.Combine(Program.ConfigDirectoryPath, BotName + ".xml");
LoginKeyFile = Path.Combine(Program.ConfigDirectoryPath, BotName + ".key");
MobileAuthenticatorFile = Path.Combine(Program.ConfigDirectoryPath, BotName + ".auth");
SentryFile = Path.Combine(Program.ConfigDirectoryPath, BotName + ".bin");

if (!ReadConfig()) {
Expand All @@ -132,10 +138,15 @@ internal Bot(string botName) {
CallbackManager.Subscribe<SteamFriends.FriendsListCallback>(OnFriendsList);
CallbackManager.Subscribe<SteamFriends.FriendMsgCallback>(OnFriendMsg);

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);
Expand All @@ -149,6 +160,90 @@ internal Bot(string botName) {
var fireAndForget = Task.Run(async () => await Start().ConfigureAwait(false));
}

internal void AcceptAllConfirmations() {
if (SteamGuardAccount == null) {
return;
}

foreach (Confirmation confirmation in SteamGuardAccount.FetchConfirmations()) {
if (SteamGuardAccount.AcceptConfirmation(confirmation)) {
Logging.LogGenericInfo(BotName, "Accepting confirmation: Success!");
} else {
Logging.LogGenericWarning(BotName, "Accepting confirmation: Failed!");
}
}
}

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 = authenticatorLinker.AddAuthenticator();
switch (linkResult) {
case AuthenticatorLinker.LinkResult.AwaitingFinalization:
Logging.LogGenericInfo(BotName, "OK: " + linkResult);
break;
case AuthenticatorLinker.LinkResult.MustProvidePhoneNumber:
while (linkResult == AuthenticatorLinker.LinkResult.MustProvidePhoneNumber) {
authenticatorLinker.PhoneNumber = Program.GetUserInput(BotName, Program.EUserInputType.PhoneNumber);
linkResult = authenticatorLinker.AddAuthenticator();
}
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;
Expand Down Expand Up @@ -196,6 +291,9 @@ private bool ReadConfig() {
case "SteamMasterClanID":
SteamMasterClanID = ulong.Parse(value);
break;
case "UseAsfAsMobileAuthenticator":
UseAsfAsMobileAuthenticator = bool.Parse(value);
break;
case "CardDropsRestricted":
CardDropsRestricted = bool.Parse(value);
break;
Expand Down Expand Up @@ -316,6 +414,59 @@ private void ResponseStatus(ulong steamID, string botName = null) {
SendMessageToUser(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)) {
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!");
}
}

private void ResponseStart(ulong steamID, string botNameToStart) {
if (steamID == 0 || string.IsNullOrEmpty(botNameToStart)) {
return;
Expand Down Expand Up @@ -367,6 +518,10 @@ private void OnConnected(SteamClient.ConnectedCallback callback) {

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);
Expand All @@ -377,16 +532,18 @@ private void OnConnected(SteamClient.ConnectedCallback callback) {
SteamLogin = Program.GetUserInput(BotName, Program.EUserInputType.Login);
}

if (SteamPassword.Equals("null")) {
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,
LoginKey = LoginKey,
TwoFactorCode = TwoFactorAuth,
SentryFileHash = sentryHash
SentryFileHash = sentryHash,
ShouldRememberPassword = true
});
}

Expand Down Expand Up @@ -477,6 +634,12 @@ private async void OnFriendMsg(SteamFriends.FriendMsgCallback callback) {

if (!message.Contains(" ")) {
switch (message) {
case "!2fa":
Response2FA(steamID);
break;
case "!2faoff":
Response2FAOff(steamID);
break;
case "!exit":
await ShutdownAllBots().ConfigureAwait(false);
break;
Expand All @@ -498,6 +661,12 @@ private async void OnFriendMsg(SteamFriends.FriendMsgCallback callback) {
} 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;
Expand Down Expand Up @@ -549,7 +718,11 @@ private async void OnLoggedOn(SteamUser.LoggedOnCallback callback) {
AuthCode = Program.GetUserInput(SteamLogin, Program.EUserInputType.SteamGuard);
break;
case EResult.AccountLoginDeniedNeedTwoFactor:
TwoFactorAuth = Program.GetUserInput(SteamLogin, Program.EUserInputType.TwoFactorAuthentication);
if (SteamGuardAccount == null) {
TwoFactorAuth = Program.GetUserInput(SteamLogin, Program.EUserInputType.TwoFactorAuthentication);
} else {
TwoFactorAuth = SteamGuardAccount.GenerateSteamGuardCode();
}
break;
case EResult.InvalidPassword:
Logging.LogGenericWarning(BotName, "Unable to login to Steam: " + result + ", will retry after a longer while");
Expand All @@ -560,6 +733,10 @@ private async void OnLoggedOn(SteamUser.LoggedOnCallback callback) {
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;
Expand Down Expand Up @@ -600,6 +777,15 @@ private async void OnLoggedOn(SteamUser.LoggedOnCallback callback) {
}
}

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;
Expand Down
Loading

0 comments on commit b3162ce

Please sign in to comment.