|
|
|
|
@@ -38,6 +38,7 @@ namespace ArchiSteamFarm {
|
|
|
|
|
internal sealed class Bot {
|
|
|
|
|
private const ulong ArchiSCFarmGroup = 103582791440160998;
|
|
|
|
|
private const ushort CallbackSleep = 500; // In miliseconds
|
|
|
|
|
private const ushort MaxSteamMessageLength = 2048;
|
|
|
|
|
|
|
|
|
|
internal static readonly Dictionary<string, Bot> Bots = new Dictionary<string, Bot>();
|
|
|
|
|
|
|
|
|
|
@@ -63,7 +64,7 @@ namespace ArchiSteamFarm {
|
|
|
|
|
internal bool KeepRunning { get; private set; }
|
|
|
|
|
|
|
|
|
|
private bool InvalidPassword, LoggedInElsewhere;
|
|
|
|
|
private string AuthCode, TwoFactorAuth;
|
|
|
|
|
private string AuthCode, TwoFactorCode;
|
|
|
|
|
|
|
|
|
|
internal static async Task RefreshCMs(uint cellID) {
|
|
|
|
|
bool initialized = false;
|
|
|
|
|
@@ -163,7 +164,7 @@ namespace ArchiSteamFarm {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ArchiHandler = new ArchiHandler();
|
|
|
|
|
ArchiHandler = new ArchiHandler(this);
|
|
|
|
|
SteamClient.AddHandler(ArchiHandler);
|
|
|
|
|
|
|
|
|
|
CallbackManager = new CallbackManager(SteamClient);
|
|
|
|
|
@@ -199,7 +200,7 @@ namespace ArchiSteamFarm {
|
|
|
|
|
|
|
|
|
|
if (AcceptConfirmationsTimer == null && BotConfig.AcceptConfirmationsPeriod > 0) {
|
|
|
|
|
AcceptConfirmationsTimer = new Timer(
|
|
|
|
|
async e => await AcceptConfirmations().ConfigureAwait(false),
|
|
|
|
|
async e => await AcceptConfirmations(true).ConfigureAwait(false),
|
|
|
|
|
null,
|
|
|
|
|
TimeSpan.FromMinutes(BotConfig.AcceptConfirmationsPeriod), // Delay
|
|
|
|
|
TimeSpan.FromMinutes(BotConfig.AcceptConfirmationsPeriod) // Period
|
|
|
|
|
@@ -223,7 +224,7 @@ namespace ArchiSteamFarm {
|
|
|
|
|
Start().Forget();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal async Task AcceptConfirmations(Confirmation.ConfirmationType allowedConfirmationType = Confirmation.ConfirmationType.Unknown) {
|
|
|
|
|
internal async Task AcceptConfirmations(bool confirm, Confirmation.ConfirmationType allowedConfirmationType = Confirmation.ConfirmationType.Unknown) {
|
|
|
|
|
if (BotDatabase.SteamGuardAccount == null) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
@@ -243,10 +244,14 @@ namespace ArchiSteamFarm {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BotDatabase.SteamGuardAccount.AcceptConfirmation(confirmation);
|
|
|
|
|
if (confirm) {
|
|
|
|
|
BotDatabase.SteamGuardAccount.AcceptConfirmation(confirmation);
|
|
|
|
|
} else {
|
|
|
|
|
BotDatabase.SteamGuardAccount.DenyConfirmation(confirmation);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} catch (SteamGuardAccount.WGTokenInvalidException) {
|
|
|
|
|
Logging.LogGenericWarning("Accepting confirmation: Failed!", BotName);
|
|
|
|
|
Logging.LogGenericWarning("Handling 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);
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
@@ -267,13 +272,21 @@ namespace ArchiSteamFarm {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var userNonce = await SteamUser.RequestWebAPIUserNonce();
|
|
|
|
|
if (userNonce == null || userNonce.Result != EResult.OK || string.IsNullOrEmpty(userNonce.Nonce)) {
|
|
|
|
|
SteamUser.WebAPIUserNonceCallback callback;
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
callback = await SteamUser.RequestWebAPIUserNonce();
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
Logging.LogGenericException(e, BotName);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (callback == null || callback.Result != EResult.OK || string.IsNullOrEmpty(callback.Nonce)) {
|
|
|
|
|
Start().Forget();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!ArchiWebHandler.Init(SteamClient, userNonce.Nonce, BotConfig.SteamParentalPIN)) {
|
|
|
|
|
if (!ArchiWebHandler.Init(SteamClient, callback.Nonce, BotConfig.SteamParentalPIN)) {
|
|
|
|
|
Start().Forget();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
@@ -297,17 +310,19 @@ namespace ArchiSteamFarm {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!message.StartsWith("!")) {
|
|
|
|
|
return await ResponseRedeem(steamID, BotName, message, true).ConfigureAwait(false);
|
|
|
|
|
return await ResponseRedeem(steamID, message.Replace(",", Environment.NewLine), true).ConfigureAwait(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!message.Contains(" ")) {
|
|
|
|
|
switch (message) {
|
|
|
|
|
case "!2fa":
|
|
|
|
|
return Response2FA(steamID);
|
|
|
|
|
case "!2fano":
|
|
|
|
|
return await Response2FAConfirm(steamID, false).ConfigureAwait(false);
|
|
|
|
|
case "!2faoff":
|
|
|
|
|
return Response2FAOff(steamID);
|
|
|
|
|
case "!2faok":
|
|
|
|
|
return await Response2FAOK(steamID).ConfigureAwait(false);
|
|
|
|
|
return await Response2FAConfirm(steamID, true).ConfigureAwait(false);
|
|
|
|
|
case "!exit":
|
|
|
|
|
return ResponseExit(steamID);
|
|
|
|
|
case "!farm":
|
|
|
|
|
@@ -338,10 +353,12 @@ namespace ArchiSteamFarm {
|
|
|
|
|
switch (args[0]) {
|
|
|
|
|
case "!2fa":
|
|
|
|
|
return Response2FA(steamID, args[1]);
|
|
|
|
|
case "!2fano":
|
|
|
|
|
return await Response2FAConfirm(steamID, args[1], false).ConfigureAwait(false);
|
|
|
|
|
case "!2faoff":
|
|
|
|
|
return Response2FAOff(steamID, args[1]);
|
|
|
|
|
case "!2faok":
|
|
|
|
|
return await Response2FAOK(steamID, args[1]).ConfigureAwait(false);
|
|
|
|
|
return await Response2FAConfirm(steamID, args[1], true).ConfigureAwait(false);
|
|
|
|
|
case "!addlicense":
|
|
|
|
|
if (args.Length > 2) {
|
|
|
|
|
return await ResponseAddLicense(steamID, args[1], args[2]).ConfigureAwait(false);
|
|
|
|
|
@@ -370,9 +387,9 @@ namespace ArchiSteamFarm {
|
|
|
|
|
}
|
|
|
|
|
case "!redeem":
|
|
|
|
|
if (args.Length > 2) {
|
|
|
|
|
return await ResponseRedeem(steamID, args[1], args[2], false).ConfigureAwait(false);
|
|
|
|
|
return await ResponseRedeem(steamID, args[1], args[2].Replace(",", Environment.NewLine), false).ConfigureAwait(false);
|
|
|
|
|
} else {
|
|
|
|
|
return await ResponseRedeem(steamID, BotName, args[1], false).ConfigureAwait(false);
|
|
|
|
|
return await ResponseRedeem(steamID, BotName, args[1].Replace(",", Environment.NewLine), false).ConfigureAwait(false);
|
|
|
|
|
}
|
|
|
|
|
case "!start":
|
|
|
|
|
return await ResponseStart(steamID, args[1]).ConfigureAwait(false);
|
|
|
|
|
@@ -393,7 +410,7 @@ namespace ArchiSteamFarm {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 2FA tokens are expiring soon, use limiter only when we don't have any pending
|
|
|
|
|
if (TwoFactorAuth == null) {
|
|
|
|
|
if (TwoFactorCode == null) {
|
|
|
|
|
await Program.LimitSteamRequestsAsync().ConfigureAwait(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -444,7 +461,9 @@ namespace ArchiSteamFarm {
|
|
|
|
|
// But here we're dealing with WinAuth authenticator
|
|
|
|
|
Logging.LogGenericInfo("ASF requires a few more steps to complete authenticator import...", BotName);
|
|
|
|
|
|
|
|
|
|
InitializeLoginAndPassword();
|
|
|
|
|
if (!InitializeLoginAndPassword()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
UserLogin userLogin = new UserLogin(BotConfig.SteamLogin, BotConfig.SteamPassword);
|
|
|
|
|
LoginResult loginResult;
|
|
|
|
|
@@ -452,6 +471,9 @@ namespace ArchiSteamFarm {
|
|
|
|
|
switch (loginResult) {
|
|
|
|
|
case LoginResult.Need2FA:
|
|
|
|
|
userLogin.TwoFactorCode = Program.GetUserInput(Program.EUserInputType.TwoFactorAuthentication, BotName);
|
|
|
|
|
if (string.IsNullOrEmpty(userLogin.TwoFactorCode)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
Logging.LogGenericError("Unhandled situation: " + loginResult, BotName);
|
|
|
|
|
@@ -572,7 +594,7 @@ namespace ArchiSteamFarm {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (await ArchiWebHandler.SendTradeOffer(inventory, BotConfig.SteamMasterID, BotConfig.SteamTradeToken).ConfigureAwait(false)) {
|
|
|
|
|
await AcceptConfirmations(Confirmation.ConfirmationType.Trade).ConfigureAwait(false);
|
|
|
|
|
await AcceptConfirmations(true, Confirmation.ConfirmationType.Trade).ConfigureAwait(false);
|
|
|
|
|
return "Trade offer sent successfully!";
|
|
|
|
|
} else {
|
|
|
|
|
return "Trade offer failed due to error!";
|
|
|
|
|
@@ -647,7 +669,7 @@ namespace ArchiSteamFarm {
|
|
|
|
|
return bot.Response2FAOff(steamID);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async Task<string> Response2FAOK(ulong steamID) {
|
|
|
|
|
private async Task<string> Response2FAConfirm(ulong steamID, bool confirm) {
|
|
|
|
|
if (steamID == 0 || !IsMaster(steamID)) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
@@ -656,11 +678,11 @@ namespace ArchiSteamFarm {
|
|
|
|
|
return "That bot doesn't have ASF 2FA enabled!";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
await AcceptConfirmations().ConfigureAwait(false);
|
|
|
|
|
await AcceptConfirmations(confirm).ConfigureAwait(false);
|
|
|
|
|
return "Done!";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static async Task<string> Response2FAOK(ulong steamID, string botName) {
|
|
|
|
|
private static async Task<string> Response2FAConfirm(ulong steamID, string botName, bool confirm) {
|
|
|
|
|
if (steamID == 0 || string.IsNullOrEmpty(botName)) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
@@ -670,7 +692,7 @@ namespace ArchiSteamFarm {
|
|
|
|
|
return "Couldn't find any bot named " + botName + "!";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return await bot.Response2FAOK(steamID).ConfigureAwait(false);
|
|
|
|
|
return await bot.Response2FAConfirm(steamID, confirm).ConfigureAwait(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static string ResponseExit(ulong steamID) {
|
|
|
|
|
@@ -682,8 +704,13 @@ namespace ArchiSteamFarm {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Environment.Exit(0);
|
|
|
|
|
return null;
|
|
|
|
|
// Schedule the task after some time so user can receive response
|
|
|
|
|
Task.Run(async () => {
|
|
|
|
|
await Utilities.SleepAsync(1000).ConfigureAwait(false);
|
|
|
|
|
Program.Exit();
|
|
|
|
|
}).Forget();
|
|
|
|
|
|
|
|
|
|
return "Done!";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private string ResponseFarm(ulong steamID) {
|
|
|
|
|
@@ -739,117 +766,82 @@ namespace ArchiSteamFarm {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
StringBuilder response = new StringBuilder();
|
|
|
|
|
using (StringReader reader = new StringReader(message)) {
|
|
|
|
|
using (StringReader reader = new StringReader(message))
|
|
|
|
|
using (IEnumerator<Bot> iterator = Bots.Values.GetEnumerator()) {
|
|
|
|
|
string key = reader.ReadLine();
|
|
|
|
|
IEnumerator<Bot> iterator = Bots.Values.GetEnumerator();
|
|
|
|
|
Bot currentBot = this;
|
|
|
|
|
while (key != null) {
|
|
|
|
|
if (currentBot == null) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while (!string.IsNullOrEmpty(key) && currentBot != null) {
|
|
|
|
|
if (validate && !IsValidCdKey(key)) {
|
|
|
|
|
key = reader.ReadLine();
|
|
|
|
|
continue;
|
|
|
|
|
key = reader.ReadLine(); // Next key
|
|
|
|
|
continue; // Without changing the bot
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ArchiHandler.PurchaseResponseCallback result = await currentBot.ArchiHandler.RedeemKey(key).ConfigureAwait(false);
|
|
|
|
|
if (result == null) {
|
|
|
|
|
break;
|
|
|
|
|
if (currentBot.SteamClient.IsConnected) {
|
|
|
|
|
ArchiHandler.PurchaseResponseCallback result = await currentBot.ArchiHandler.RedeemKey(key).ConfigureAwait(false);
|
|
|
|
|
if (result != null) {
|
|
|
|
|
switch (result.PurchaseResult) {
|
|
|
|
|
case ArchiHandler.PurchaseResponseCallback.EPurchaseResult.DuplicatedKey:
|
|
|
|
|
case ArchiHandler.PurchaseResponseCallback.EPurchaseResult.InvalidKey:
|
|
|
|
|
case ArchiHandler.PurchaseResponseCallback.EPurchaseResult.OK:
|
|
|
|
|
response.Append(Environment.NewLine + "<" + currentBot.BotName + "> Key: " + key + " | Status: " + result.PurchaseResult + " | Items: " + string.Join("", result.Items));
|
|
|
|
|
|
|
|
|
|
key = reader.ReadLine(); // Next key
|
|
|
|
|
break; // Next bot (if needed)
|
|
|
|
|
case ArchiHandler.PurchaseResponseCallback.EPurchaseResult.AlreadyOwned:
|
|
|
|
|
case ArchiHandler.PurchaseResponseCallback.EPurchaseResult.BaseGameRequired:
|
|
|
|
|
case ArchiHandler.PurchaseResponseCallback.EPurchaseResult.OnCooldown:
|
|
|
|
|
case ArchiHandler.PurchaseResponseCallback.EPurchaseResult.RegionLocked:
|
|
|
|
|
response.Append(Environment.NewLine + "<" + currentBot.BotName + "> Key: " + key + " | Status: " + result.PurchaseResult + " | Items: " + string.Join("", result.Items));
|
|
|
|
|
|
|
|
|
|
if (!BotConfig.ForwardKeysToOtherBots) {
|
|
|
|
|
key = reader.ReadLine(); // Next key
|
|
|
|
|
break; // Next bot (if needed)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (BotConfig.DistributeKeys) {
|
|
|
|
|
break; // Next bot, without changing key
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool alreadyHandled = false;
|
|
|
|
|
foreach (Bot bot in Bots.Values) {
|
|
|
|
|
if (bot == this || !bot.SteamClient.IsConnected) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ArchiHandler.PurchaseResponseCallback otherResult = await bot.ArchiHandler.RedeemKey(key).ConfigureAwait(false);
|
|
|
|
|
if (otherResult == null) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (otherResult.PurchaseResult) {
|
|
|
|
|
case ArchiHandler.PurchaseResponseCallback.EPurchaseResult.DuplicatedKey:
|
|
|
|
|
case ArchiHandler.PurchaseResponseCallback.EPurchaseResult.InvalidKey:
|
|
|
|
|
case ArchiHandler.PurchaseResponseCallback.EPurchaseResult.OK:
|
|
|
|
|
alreadyHandled = true; // This key is already handled, as we either redeemed it or we're sure it's dupe/invalid
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
response.Append(Environment.NewLine + "<" + bot.BotName + "> Key: " + key + " | Status: " + otherResult.PurchaseResult + " | Items: " + string.Join("", otherResult.Items));
|
|
|
|
|
|
|
|
|
|
if (alreadyHandled) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
key = reader.ReadLine(); // Next key
|
|
|
|
|
break; // Next bot (if needed)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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:
|
|
|
|
|
response.Append(Environment.NewLine + "<" + currentBot.BotName + "> Key: " + key + " | Status: " + purchaseResult + " | Items: " + string.Join("", items));
|
|
|
|
|
if (BotConfig.DistributeKeys) {
|
|
|
|
|
do {
|
|
|
|
|
if (iterator.MoveNext()) {
|
|
|
|
|
currentBot = iterator.Current;
|
|
|
|
|
} else {
|
|
|
|
|
currentBot = null;
|
|
|
|
|
}
|
|
|
|
|
} while (currentBot == this);
|
|
|
|
|
|
|
|
|
|
if (!BotConfig.ForwardKeysToOtherBots) {
|
|
|
|
|
key = reader.ReadLine();
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
if (BotConfig.DistributeKeys) {
|
|
|
|
|
do {
|
|
|
|
|
if (iterator.MoveNext()) {
|
|
|
|
|
currentBot = iterator.Current;
|
|
|
|
|
} else {
|
|
|
|
|
currentBot = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!BotConfig.ForwardKeysToOtherBots) {
|
|
|
|
|
key = reader.ReadLine();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool alreadyHandled = false;
|
|
|
|
|
foreach (Bot bot in Bots.Values) {
|
|
|
|
|
if (alreadyHandled) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (bot == this) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ArchiHandler.PurchaseResponseCallback otherResult = await bot.ArchiHandler.RedeemKey(key).ConfigureAwait(false);
|
|
|
|
|
|
|
|
|
|
if (otherResult == null) {
|
|
|
|
|
break; // We're done with this key
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var otherPurchaseResult = otherResult.PurchaseResult;
|
|
|
|
|
var otherItems = otherResult.Items;
|
|
|
|
|
|
|
|
|
|
switch (otherPurchaseResult) {
|
|
|
|
|
case ArchiHandler.PurchaseResponseCallback.EPurchaseResult.OK:
|
|
|
|
|
alreadyHandled = true; // We're done with this key
|
|
|
|
|
response.Append(Environment.NewLine + "<" + bot.BotName + "> Key: " + key + " | Status: " + otherPurchaseResult + " | Items: " + string.Join("", otherItems));
|
|
|
|
|
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
|
|
|
|
|
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));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
key = reader.ReadLine();
|
|
|
|
|
break;
|
|
|
|
|
case ArchiHandler.PurchaseResponseCallback.EPurchaseResult.OK:
|
|
|
|
|
response.Append(Environment.NewLine + "<" + currentBot.BotName + "> Key: " + key + " | Status: " + purchaseResult + " | Items: " + string.Join("", items));
|
|
|
|
|
if (BotConfig.DistributeKeys) {
|
|
|
|
|
do {
|
|
|
|
|
if (iterator.MoveNext()) {
|
|
|
|
|
currentBot = iterator.Current;
|
|
|
|
|
} else {
|
|
|
|
|
currentBot = null;
|
|
|
|
|
}
|
|
|
|
|
} while (currentBot == this);
|
|
|
|
|
}
|
|
|
|
|
key = reader.ReadLine();
|
|
|
|
|
break;
|
|
|
|
|
case ArchiHandler.PurchaseResponseCallback.EPurchaseResult.DuplicatedKey:
|
|
|
|
|
case ArchiHandler.PurchaseResponseCallback.EPurchaseResult.InvalidKey:
|
|
|
|
|
response.Append(Environment.NewLine + "<" + currentBot.BotName + "> Key: " + key + " | Status: " + purchaseResult + " | Items: " + string.Join("", items));
|
|
|
|
|
if (BotConfig.DistributeKeys && !BotConfig.ForwardKeysToOtherBots) {
|
|
|
|
|
do {
|
|
|
|
|
if (iterator.MoveNext()) {
|
|
|
|
|
currentBot = iterator.Current;
|
|
|
|
|
} else {
|
|
|
|
|
currentBot = null;
|
|
|
|
|
}
|
|
|
|
|
} while (currentBot == this);
|
|
|
|
|
}
|
|
|
|
|
key = reader.ReadLine();
|
|
|
|
|
break;
|
|
|
|
|
} while (currentBot == this || (currentBot != null && !currentBot.SteamClient.IsConnected));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@@ -899,8 +891,13 @@ namespace ArchiSteamFarm {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Program.Restart();
|
|
|
|
|
return null;
|
|
|
|
|
// Schedule the task after some time so user can receive response
|
|
|
|
|
Task.Run(async () => {
|
|
|
|
|
await Utilities.SleepAsync(1000).ConfigureAwait(false);
|
|
|
|
|
Program.Restart();
|
|
|
|
|
}).Forget();
|
|
|
|
|
|
|
|
|
|
return "Done!";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async Task<string> ResponseAddLicense(ulong steamID, HashSet<uint> gameIDs) {
|
|
|
|
|
@@ -949,8 +946,8 @@ namespace ArchiSteamFarm {
|
|
|
|
|
return await bot.ResponseAddLicense(steamID, gamesToRedeem).ConfigureAwait(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async Task<string> ResponseOwns(ulong steamID, string games) {
|
|
|
|
|
if (steamID == 0 || string.IsNullOrEmpty(games) || !IsMaster(steamID)) {
|
|
|
|
|
private async Task<string> ResponseOwns(ulong steamID, string query) {
|
|
|
|
|
if (steamID == 0 || string.IsNullOrEmpty(query) || !IsMaster(steamID)) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -959,37 +956,42 @@ namespace ArchiSteamFarm {
|
|
|
|
|
return "List of owned games is empty!";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check if this is uint
|
|
|
|
|
uint appID;
|
|
|
|
|
if (uint.TryParse(games, out appID)) {
|
|
|
|
|
string ownedName;
|
|
|
|
|
if (ownedGames.TryGetValue(appID, out ownedName)) {
|
|
|
|
|
return "Owned already: " + appID + " | " + ownedName;
|
|
|
|
|
} else {
|
|
|
|
|
return "Not owned yet: " + appID;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
StringBuilder response = new StringBuilder();
|
|
|
|
|
|
|
|
|
|
// This is a string
|
|
|
|
|
foreach (KeyValuePair<uint, string> game in ownedGames) {
|
|
|
|
|
if (game.Value.IndexOf(games, StringComparison.OrdinalIgnoreCase) < 0) {
|
|
|
|
|
string[] games = query.Split(',');
|
|
|
|
|
foreach (string game in games) {
|
|
|
|
|
// Check if this is appID
|
|
|
|
|
uint appID;
|
|
|
|
|
if (uint.TryParse(game, out appID)) {
|
|
|
|
|
string ownedName;
|
|
|
|
|
if (ownedGames.TryGetValue(appID, out ownedName)) {
|
|
|
|
|
response.Append(Environment.NewLine + "Owned already: " + appID + " | " + ownedName);
|
|
|
|
|
} else {
|
|
|
|
|
response.Append(Environment.NewLine + "Not owned yet: " + appID);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
response.AppendLine(Environment.NewLine + "Owned already: " + game.Key + " | " + game.Value);
|
|
|
|
|
// This is a string, so check our entire library
|
|
|
|
|
foreach (KeyValuePair<uint, string> ownedGame in ownedGames) {
|
|
|
|
|
if (ownedGame.Value.IndexOf(game, StringComparison.OrdinalIgnoreCase) < 0) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
response.Append(Environment.NewLine + "Owned already: " + ownedGame.Key + " | " + ownedGame.Value);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (response.Length > 0) {
|
|
|
|
|
return response.ToString();
|
|
|
|
|
} else {
|
|
|
|
|
return "Not owned yet: " + games;
|
|
|
|
|
return "Not owned yet: " + query;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static async Task<string> ResponseOwns(ulong steamID, string botName, string games) {
|
|
|
|
|
if (steamID == 0 || string.IsNullOrEmpty(botName) || string.IsNullOrEmpty(games)) {
|
|
|
|
|
private static async Task<string> ResponseOwns(ulong steamID, string botName, string query) {
|
|
|
|
|
if (steamID == 0 || string.IsNullOrEmpty(botName) || string.IsNullOrEmpty(query)) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -998,7 +1000,7 @@ namespace ArchiSteamFarm {
|
|
|
|
|
return "Couldn't find any bot named " + botName + "!";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return await bot.ResponseOwns(steamID, games).ConfigureAwait(false);
|
|
|
|
|
return await bot.ResponseOwns(steamID, query).ConfigureAwait(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async Task<string> ResponsePlay(ulong steamID, HashSet<uint> gameIDs) {
|
|
|
|
|
@@ -1147,9 +1149,31 @@ namespace ArchiSteamFarm {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (new SteamID(steamID).IsChatAccount) {
|
|
|
|
|
SteamFriends.SendChatRoomMessage(steamID, EChatEntryType.ChatMsg, message);
|
|
|
|
|
SendMessageToChannel(steamID, message);
|
|
|
|
|
} else {
|
|
|
|
|
SteamFriends.SendChatMessage(steamID, EChatEntryType.ChatMsg, message);
|
|
|
|
|
SendMessageToUser(steamID, message);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void SendMessageToChannel(ulong steamID, string message) {
|
|
|
|
|
if (steamID == 0 || string.IsNullOrEmpty(message)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < message.Length; i += MaxSteamMessageLength - 6) {
|
|
|
|
|
string messagePart = (i > 0 ? "..." : "") + message.Substring(i, Math.Min(MaxSteamMessageLength - 6, message.Length - i)) + (MaxSteamMessageLength - 6 < message.Length - i ? "..." : "");
|
|
|
|
|
SteamFriends.SendChatRoomMessage(steamID, EChatEntryType.ChatMsg, messagePart);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void SendMessageToUser(ulong steamID, string message) {
|
|
|
|
|
if (steamID == 0 || string.IsNullOrEmpty(message)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < message.Length; i += MaxSteamMessageLength - 6) {
|
|
|
|
|
string messagePart = (i > 0 ? "..." : "") + message.Substring(i, Math.Min(MaxSteamMessageLength - 6, message.Length - i)) + (MaxSteamMessageLength - 6 < message.Length - i ? "..." : "");
|
|
|
|
|
SteamFriends.SendChatMessage(steamID, EChatEntryType.ChatMsg, messagePart);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -1160,7 +1184,9 @@ namespace ArchiSteamFarm {
|
|
|
|
|
|
|
|
|
|
Logging.LogGenericInfo("Linking new ASF MobileAuthenticator...", BotName);
|
|
|
|
|
|
|
|
|
|
InitializeLoginAndPassword();
|
|
|
|
|
if (!InitializeLoginAndPassword()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
UserLogin userLogin = new UserLogin(BotConfig.SteamLogin, BotConfig.SteamPassword);
|
|
|
|
|
LoginResult loginResult;
|
|
|
|
|
@@ -1168,6 +1194,9 @@ namespace ArchiSteamFarm {
|
|
|
|
|
switch (loginResult) {
|
|
|
|
|
case LoginResult.NeedEmail:
|
|
|
|
|
userLogin.EmailCode = Program.GetUserInput(Program.EUserInputType.SteamGuard, BotName);
|
|
|
|
|
if (string.IsNullOrEmpty(userLogin.EmailCode)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
Logging.LogGenericError("Unhandled situation: " + loginResult, BotName);
|
|
|
|
|
@@ -1182,6 +1211,9 @@ namespace ArchiSteamFarm {
|
|
|
|
|
switch (linkResult) {
|
|
|
|
|
case AuthenticatorLinker.LinkResult.MustProvidePhoneNumber:
|
|
|
|
|
authenticatorLinker.PhoneNumber = Program.GetUserInput(Program.EUserInputType.PhoneNumber, BotName);
|
|
|
|
|
if (string.IsNullOrEmpty(authenticatorLinker.PhoneNumber)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
Logging.LogGenericError("Unhandled situation: " + linkResult, BotName);
|
|
|
|
|
@@ -1191,7 +1223,12 @@ namespace ArchiSteamFarm {
|
|
|
|
|
|
|
|
|
|
BotDatabase.SteamGuardAccount = authenticatorLinker.LinkedAccount;
|
|
|
|
|
|
|
|
|
|
AuthenticatorLinker.FinalizeResult finalizeResult = authenticatorLinker.FinalizeAddAuthenticator(Program.GetUserInput(Program.EUserInputType.SMS, BotName));
|
|
|
|
|
string sms = Program.GetUserInput(Program.EUserInputType.SMS, BotName);
|
|
|
|
|
if (string.IsNullOrEmpty(sms)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AuthenticatorLinker.FinalizeResult finalizeResult = authenticatorLinker.FinalizeAddAuthenticator(sms);
|
|
|
|
|
if (finalizeResult != AuthenticatorLinker.FinalizeResult.Success) {
|
|
|
|
|
Logging.LogGenericError("Unhandled situation: " + finalizeResult, BotName);
|
|
|
|
|
DelinkMobileAuthenticator();
|
|
|
|
|
@@ -1227,14 +1264,22 @@ namespace ArchiSteamFarm {
|
|
|
|
|
SteamFriends.JoinChat(BotConfig.SteamMasterClanID);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void InitializeLoginAndPassword() {
|
|
|
|
|
private bool InitializeLoginAndPassword() {
|
|
|
|
|
if (string.IsNullOrEmpty(BotConfig.SteamLogin)) {
|
|
|
|
|
BotConfig.SteamLogin = Program.GetUserInput(Program.EUserInputType.Login, BotName);
|
|
|
|
|
if (string.IsNullOrEmpty(BotConfig.SteamLogin)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (string.IsNullOrEmpty(BotConfig.SteamPassword) && string.IsNullOrEmpty(BotDatabase.LoginKey)) {
|
|
|
|
|
BotConfig.SteamPassword = Program.GetUserInput(Program.EUserInputType.Password, BotName);
|
|
|
|
|
if (string.IsNullOrEmpty(BotConfig.SteamPassword)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void OnConnected(SteamClient.ConnectedCallback callback) {
|
|
|
|
|
@@ -1265,10 +1310,18 @@ namespace ArchiSteamFarm {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
InitializeLoginAndPassword();
|
|
|
|
|
if (!InitializeLoginAndPassword()) {
|
|
|
|
|
Stop();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Logging.LogGenericInfo("Logging in...", BotName);
|
|
|
|
|
|
|
|
|
|
// If we have ASF 2FA enabled, we can always provide TwoFactorCode, and save a request
|
|
|
|
|
if (BotDatabase.SteamGuardAccount != null) {
|
|
|
|
|
TwoFactorCode = BotDatabase.SteamGuardAccount.GenerateSteamGuardCode();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO: Please remove me immediately after https://github.com/SteamRE/SteamKit/issues/254 gets fixed
|
|
|
|
|
if (Program.GlobalConfig.HackIgnoreMachineID) {
|
|
|
|
|
Logging.LogGenericWarning("Using workaround for broken GenerateMachineID()!", BotName);
|
|
|
|
|
@@ -1278,7 +1331,7 @@ namespace ArchiSteamFarm {
|
|
|
|
|
AuthCode = AuthCode,
|
|
|
|
|
LoginID = LoginID,
|
|
|
|
|
LoginKey = BotDatabase.LoginKey,
|
|
|
|
|
TwoFactorCode = TwoFactorAuth,
|
|
|
|
|
TwoFactorCode = TwoFactorCode,
|
|
|
|
|
SentryFileHash = sentryHash,
|
|
|
|
|
ShouldRememberPassword = true
|
|
|
|
|
});
|
|
|
|
|
@@ -1291,7 +1344,7 @@ namespace ArchiSteamFarm {
|
|
|
|
|
AuthCode = AuthCode,
|
|
|
|
|
LoginID = LoginID,
|
|
|
|
|
LoginKey = BotDatabase.LoginKey,
|
|
|
|
|
TwoFactorCode = TwoFactorAuth,
|
|
|
|
|
TwoFactorCode = TwoFactorCode,
|
|
|
|
|
SentryFileHash = sentryHash,
|
|
|
|
|
ShouldRememberPassword = true
|
|
|
|
|
});
|
|
|
|
|
@@ -1338,7 +1391,7 @@ namespace ArchiSteamFarm {
|
|
|
|
|
Logging.LogGenericInfo("Reconnecting...", BotName);
|
|
|
|
|
|
|
|
|
|
// 2FA tokens are expiring soon, use limiter only when we don't have any pending
|
|
|
|
|
if (TwoFactorAuth == null) {
|
|
|
|
|
if (TwoFactorCode == null) {
|
|
|
|
|
await Program.LimitSteamRequestsAsync().ConfigureAwait(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -1493,12 +1546,20 @@ namespace ArchiSteamFarm {
|
|
|
|
|
switch (callback.Result) {
|
|
|
|
|
case EResult.AccountLogonDenied:
|
|
|
|
|
AuthCode = Program.GetUserInput(Program.EUserInputType.SteamGuard, BotName);
|
|
|
|
|
if (string.IsNullOrEmpty(AuthCode)) {
|
|
|
|
|
Stop();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case EResult.AccountLoginDeniedNeedTwoFactor:
|
|
|
|
|
if (BotDatabase.SteamGuardAccount == null) {
|
|
|
|
|
TwoFactorAuth = Program.GetUserInput(Program.EUserInputType.TwoFactorAuthentication, BotName);
|
|
|
|
|
TwoFactorCode = Program.GetUserInput(Program.EUserInputType.TwoFactorAuthentication, BotName);
|
|
|
|
|
if (string.IsNullOrEmpty(TwoFactorCode)) {
|
|
|
|
|
Stop();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
TwoFactorAuth = BotDatabase.SteamGuardAccount.GenerateSteamGuardCode();
|
|
|
|
|
Logging.LogGenericWarning("2FA code was invalid despite of using ASF 2FA. Invalid authenticator or bad timing?", BotName);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case EResult.InvalidPassword:
|
|
|
|
|
@@ -1517,18 +1578,22 @@ namespace ArchiSteamFarm {
|
|
|
|
|
string maFilePath = Path.Combine(Program.ConfigDirectory, callback.ClientSteamID.ConvertToUInt64() + ".maFile");
|
|
|
|
|
if (File.Exists(maFilePath)) {
|
|
|
|
|
ImportAuthenticator(maFilePath);
|
|
|
|
|
} else if (TwoFactorAuth == null && BotConfig.UseAsfAsMobileAuthenticator) {
|
|
|
|
|
} else if (TwoFactorCode == null && BotConfig.UseAsfAsMobileAuthenticator) {
|
|
|
|
|
LinkMobileAuthenticator();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Reset one-time-only access tokens
|
|
|
|
|
AuthCode = TwoFactorAuth = null;
|
|
|
|
|
AuthCode = TwoFactorCode = null;
|
|
|
|
|
|
|
|
|
|
ResetGamesPlayed();
|
|
|
|
|
|
|
|
|
|
if (string.IsNullOrEmpty(BotConfig.SteamParentalPIN)) {
|
|
|
|
|
BotConfig.SteamParentalPIN = Program.GetUserInput(Program.EUserInputType.SteamParentalPIN, BotName);
|
|
|
|
|
if (string.IsNullOrEmpty(BotConfig.SteamParentalPIN)) {
|
|
|
|
|
Stop();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!ArchiWebHandler.Init(SteamClient, callback.WebAPIUserNonce, BotConfig.SteamParentalPIN)) {
|
|
|
|
|
|