Further enhance new 2FA, closes #161

This commit is contained in:
JustArchi
2016-06-19 07:25:02 +02:00
parent 38f48841bd
commit fdc705e955
4 changed files with 165 additions and 13 deletions

View File

@@ -241,16 +241,53 @@ namespace ArchiSteamFarm {
Start().Forget();
}
internal async Task AcceptConfirmations(bool accept) {
internal async Task AcceptConfirmations(bool accept, Steam.ConfirmationDetails.EType acceptedType = Steam.ConfirmationDetails.EType.Unknown, ulong acceptedSteamID = 0, HashSet<ulong> acceptedTradeIDs = null) {
if (BotDatabase.MobileAuthenticator == null) {
return;
}
HashSet<MobileAuthenticator.Confirmation> confirmations = await BotDatabase.MobileAuthenticator.GetConfirmations().ConfigureAwait(false);
if (confirmations == null) {
if ((confirmations == null) || (confirmations.Count == 0)) {
return;
}
if (acceptedType != Steam.ConfirmationDetails.EType.Unknown) {
if (confirmations.RemoveWhere(confirmation => confirmation.Type != acceptedType) > 0) {
confirmations.TrimExcess();
if (confirmations.Count == 0) {
return;
}
}
}
if ((acceptedSteamID != 0) || ((acceptedTradeIDs != null) && (acceptedTradeIDs.Count > 0))) {
HashSet<MobileAuthenticator.Confirmation> ignoredConfirmations = new HashSet<MobileAuthenticator.Confirmation>();
// TODO: This could be potentially multi-threaded like below
foreach (MobileAuthenticator.Confirmation confirmation in confirmations) {
Steam.ConfirmationDetails details = await BotDatabase.MobileAuthenticator.GetConfirmationDetails(confirmation).ConfigureAwait(false);
if (details == null) {
ignoredConfirmations.Add(confirmation);
continue;
}
if ((acceptedSteamID != 0) && (acceptedSteamID != details.OtherSteamID64)) {
ignoredConfirmations.Add(confirmation);
continue;
}
if ((acceptedTradeIDs != null) && !acceptedTradeIDs.Contains(details.TradeOfferID)) {
ignoredConfirmations.Add(confirmation);
}
}
confirmations.ExceptWith(ignoredConfirmations);
confirmations.TrimExcess();
if (confirmations.Count == 0) {
return;
}
}
await confirmations.ForEachAsync(async confirmation => await BotDatabase.MobileAuthenticator.HandleConfirmation(confirmation, accept).ConfigureAwait(false)).ConfigureAwait(false);
}
@@ -613,7 +650,7 @@ namespace ArchiSteamFarm {
return "Trade offer failed due to error!";
}
await AcceptConfirmations(true).ConfigureAwait(false);
await AcceptConfirmations(true, Steam.ConfirmationDetails.EType.Trade, BotConfig.SteamMasterID).ConfigureAwait(false);
return "Trade offer sent successfully!";
}

View File

@@ -341,9 +341,95 @@ namespace ArchiSteamFarm.JSON {
[SuppressMessage("ReSharper", "ClassNeverInstantiated.Global")]
[SuppressMessage("ReSharper", "UnusedAutoPropertyAccessor.Local")]
internal sealed class ConfirmationDetails {
internal enum EType : byte {
Unknown,
Trade,
Market,
Other
}
[JsonProperty(PropertyName = "success", Required = Required.Always)]
internal bool Success { get; private set; }
private EType _Type;
private EType Type {
get {
if (_Type != EType.Unknown) {
return _Type;
}
if (HtmlDocument == null) {
Logging.LogNullError(nameof(HtmlDocument));
return EType.Unknown;
}
HtmlNode testNode = HtmlDocument.DocumentNode.SelectSingleNode("//div[@class='mobileconf_listing_prices']");
if (testNode != null) {
_Type = EType.Market;
return _Type;
}
testNode = HtmlDocument.DocumentNode.SelectSingleNode("//div[@class='mobileconf_trade_area']");
if (testNode != null) {
_Type = EType.Trade;
return _Type;
}
_Type = EType.Other;
return _Type;
}
}
private ulong _TradeOfferID;
internal ulong TradeOfferID {
get {
if (_TradeOfferID != 0) {
return _TradeOfferID;
}
if (Type != EType.Trade) {
return 0;
}
if (HtmlDocument == null) {
Logging.LogNullError(nameof(HtmlDocument));
return 0;
}
HtmlNode htmlNode = HtmlDocument.DocumentNode.SelectSingleNode("//div[@class='tradeoffer']");
if (htmlNode == null) {
Logging.LogNullError(nameof(htmlNode));
return 0;
}
string id = htmlNode.GetAttributeValue("id", null);
if (string.IsNullOrEmpty(id)) {
Logging.LogNullError(nameof(id));
return 0;
}
int index = id.IndexOf('_');
if (index < 0) {
Logging.LogNullError(nameof(index));
return 0;
}
index++;
if (id.Length <= index) {
Logging.LogNullError(nameof(id.Length));
return 0;
}
id = id.Substring(index);
if (ulong.TryParse(id, out _TradeOfferID) && (_TradeOfferID != 0)) {
return _TradeOfferID;
}
Logging.LogNullError(nameof(_TradeOfferID));
return 0;
}
}
private ulong _OtherSteamID64;
internal ulong OtherSteamID64 {
get {
@@ -351,6 +437,10 @@ namespace ArchiSteamFarm.JSON {
return _OtherSteamID64;
}
if (Type != EType.Trade) {
return 0;
}
if (OtherSteamID3 == 0) {
Logging.LogNullError(nameof(OtherSteamID3));
return 0;
@@ -371,6 +461,10 @@ namespace ArchiSteamFarm.JSON {
return _OtherSteamID3;
}
if (Type != EType.Trade) {
return 0;
}
if (HtmlDocument == null) {
Logging.LogNullError(nameof(HtmlDocument));
return 0;

View File

@@ -13,14 +13,16 @@ namespace ArchiSteamFarm {
internal sealed class Confirmation {
internal readonly uint ID;
internal readonly ulong Key;
internal readonly Steam.ConfirmationDetails.EType Type;
internal Confirmation(uint id, ulong key) {
if ((id == 0) || (key == 0)) {
throw new ArgumentNullException(nameof(id) + " || " + nameof(key));
internal Confirmation(uint id, ulong key, Steam.ConfirmationDetails.EType type) {
if ((id == 0) || (key == 0) || (type == Steam.ConfirmationDetails.EType.Unknown)) {
throw new ArgumentNullException(nameof(id) + " || " + nameof(key) + " || " + nameof(type));
}
ID = id;
Key = key;
Type = type;
}
}
@@ -146,14 +148,14 @@ namespace ArchiSteamFarm {
return null;
}
HtmlNodeCollection confirmations = htmlDocument.DocumentNode.SelectNodes("//div[@class='mobileconf_list_entry']");
if (confirmations == null) {
HtmlNodeCollection confirmationNodes = htmlDocument.DocumentNode.SelectNodes("//div[@class='mobileconf_list_entry']");
if (confirmationNodes == null) {
return null;
}
HashSet<Confirmation> result = new HashSet<Confirmation>();
foreach (HtmlNode confirmation in confirmations) {
string idString = confirmation.GetAttributeValue("data-confid", null);
foreach (HtmlNode confirmationNode in confirmationNodes) {
string idString = confirmationNode.GetAttributeValue("data-confid", null);
if (string.IsNullOrEmpty(idString)) {
Logging.LogNullError(nameof(idString), Bot.BotName);
continue;
@@ -165,7 +167,7 @@ namespace ArchiSteamFarm {
continue;
}
string keyString = confirmation.GetAttributeValue("data-key", null);
string keyString = confirmationNode.GetAttributeValue("data-key", null);
if (string.IsNullOrEmpty(keyString)) {
Logging.LogNullError(nameof(keyString), Bot.BotName);
continue;
@@ -177,7 +179,24 @@ namespace ArchiSteamFarm {
continue;
}
result.Add(new Confirmation(id, key));
HtmlNode descriptionNode = confirmationNode.SelectSingleNode(".//div[@class='mobileconf_list_entry_description']/div");
if (descriptionNode == null) {
Logging.LogNullError(nameof(descriptionNode), Bot.BotName);
continue;
}
Steam.ConfirmationDetails.EType type;
string description = descriptionNode.InnerText;
if (description.Equals("Sell - Market Listing")) {
type = Steam.ConfirmationDetails.EType.Market;
} else if (description.StartsWith("Trade with ", StringComparison.Ordinal)) {
type = Steam.ConfirmationDetails.EType.Trade;
} else {
type = Steam.ConfirmationDetails.EType.Other;
}
result.Add(new Confirmation(id, key, type));
}
return result;

View File

@@ -94,7 +94,9 @@ namespace ArchiSteamFarm {
}
await tradeOffers.ForEachAsync(ParseTrade).ConfigureAwait(false);
await Bot.AcceptConfirmations(true).ConfigureAwait(false);
HashSet<ulong> tradeIDs = new HashSet<ulong>(tradeOffers.Select(tradeOffer => tradeOffer.TradeOfferID));
await Bot.AcceptConfirmations(true, Steam.ConfirmationDetails.EType.Trade, 0, tradeIDs).ConfigureAwait(false);
}
private async Task ParseTrade(Steam.TradeOffer tradeOffer) {