Skip to content

Commit fdc705e

Browse files
committed
Further enhance new 2FA, closes #161
1 parent 38f4884 commit fdc705e

File tree

4 files changed

+165
-13
lines changed

4 files changed

+165
-13
lines changed

ArchiSteamFarm/Bot.cs

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -241,16 +241,53 @@ internal Bot(string botName) {
241241
Start().Forget();
242242
}
243243

244-
internal async Task AcceptConfirmations(bool accept) {
244+
internal async Task AcceptConfirmations(bool accept, Steam.ConfirmationDetails.EType acceptedType = Steam.ConfirmationDetails.EType.Unknown, ulong acceptedSteamID = 0, HashSet<ulong> acceptedTradeIDs = null) {
245245
if (BotDatabase.MobileAuthenticator == null) {
246246
return;
247247
}
248248

249249
HashSet<MobileAuthenticator.Confirmation> confirmations = await BotDatabase.MobileAuthenticator.GetConfirmations().ConfigureAwait(false);
250-
if (confirmations == null) {
250+
if ((confirmations == null) || (confirmations.Count == 0)) {
251251
return;
252252
}
253253

254+
if (acceptedType != Steam.ConfirmationDetails.EType.Unknown) {
255+
if (confirmations.RemoveWhere(confirmation => confirmation.Type != acceptedType) > 0) {
256+
confirmations.TrimExcess();
257+
if (confirmations.Count == 0) {
258+
return;
259+
}
260+
}
261+
}
262+
263+
if ((acceptedSteamID != 0) || ((acceptedTradeIDs != null) && (acceptedTradeIDs.Count > 0))) {
264+
HashSet<MobileAuthenticator.Confirmation> ignoredConfirmations = new HashSet<MobileAuthenticator.Confirmation>();
265+
// TODO: This could be potentially multi-threaded like below
266+
foreach (MobileAuthenticator.Confirmation confirmation in confirmations) {
267+
Steam.ConfirmationDetails details = await BotDatabase.MobileAuthenticator.GetConfirmationDetails(confirmation).ConfigureAwait(false);
268+
if (details == null) {
269+
ignoredConfirmations.Add(confirmation);
270+
continue;
271+
}
272+
273+
if ((acceptedSteamID != 0) && (acceptedSteamID != details.OtherSteamID64)) {
274+
ignoredConfirmations.Add(confirmation);
275+
continue;
276+
}
277+
278+
if ((acceptedTradeIDs != null) && !acceptedTradeIDs.Contains(details.TradeOfferID)) {
279+
ignoredConfirmations.Add(confirmation);
280+
}
281+
}
282+
283+
confirmations.ExceptWith(ignoredConfirmations);
284+
confirmations.TrimExcess();
285+
286+
if (confirmations.Count == 0) {
287+
return;
288+
}
289+
}
290+
254291
await confirmations.ForEachAsync(async confirmation => await BotDatabase.MobileAuthenticator.HandleConfirmation(confirmation, accept).ConfigureAwait(false)).ConfigureAwait(false);
255292
}
256293

@@ -613,7 +650,7 @@ private async Task<string> ResponseSendTrade(ulong steamID) {
613650
return "Trade offer failed due to error!";
614651
}
615652

616-
await AcceptConfirmations(true).ConfigureAwait(false);
653+
await AcceptConfirmations(true, Steam.ConfirmationDetails.EType.Trade, BotConfig.SteamMasterID).ConfigureAwait(false);
617654
return "Trade offer sent successfully!";
618655
}
619656

ArchiSteamFarm/JSON/Steam.cs

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,16 +341,106 @@ internal sealed class ConfirmationResponse {
341341
[SuppressMessage("ReSharper", "ClassNeverInstantiated.Global")]
342342
[SuppressMessage("ReSharper", "UnusedAutoPropertyAccessor.Local")]
343343
internal sealed class ConfirmationDetails {
344+
internal enum EType : byte {
345+
Unknown,
346+
Trade,
347+
Market,
348+
Other
349+
}
350+
344351
[JsonProperty(PropertyName = "success", Required = Required.Always)]
345352
internal bool Success { get; private set; }
346353

354+
private EType _Type;
355+
private EType Type {
356+
get {
357+
if (_Type != EType.Unknown) {
358+
return _Type;
359+
}
360+
361+
if (HtmlDocument == null) {
362+
Logging.LogNullError(nameof(HtmlDocument));
363+
return EType.Unknown;
364+
}
365+
366+
HtmlNode testNode = HtmlDocument.DocumentNode.SelectSingleNode("//div[@class='mobileconf_listing_prices']");
367+
if (testNode != null) {
368+
_Type = EType.Market;
369+
return _Type;
370+
}
371+
372+
testNode = HtmlDocument.DocumentNode.SelectSingleNode("//div[@class='mobileconf_trade_area']");
373+
if (testNode != null) {
374+
_Type = EType.Trade;
375+
return _Type;
376+
}
377+
378+
_Type = EType.Other;
379+
return _Type;
380+
}
381+
}
382+
383+
private ulong _TradeOfferID;
384+
internal ulong TradeOfferID {
385+
get {
386+
if (_TradeOfferID != 0) {
387+
return _TradeOfferID;
388+
}
389+
390+
if (Type != EType.Trade) {
391+
return 0;
392+
}
393+
394+
if (HtmlDocument == null) {
395+
Logging.LogNullError(nameof(HtmlDocument));
396+
return 0;
397+
}
398+
399+
HtmlNode htmlNode = HtmlDocument.DocumentNode.SelectSingleNode("//div[@class='tradeoffer']");
400+
if (htmlNode == null) {
401+
Logging.LogNullError(nameof(htmlNode));
402+
return 0;
403+
}
404+
405+
string id = htmlNode.GetAttributeValue("id", null);
406+
if (string.IsNullOrEmpty(id)) {
407+
Logging.LogNullError(nameof(id));
408+
return 0;
409+
}
410+
411+
int index = id.IndexOf('_');
412+
if (index < 0) {
413+
Logging.LogNullError(nameof(index));
414+
return 0;
415+
}
416+
417+
index++;
418+
if (id.Length <= index) {
419+
Logging.LogNullError(nameof(id.Length));
420+
return 0;
421+
}
422+
423+
id = id.Substring(index);
424+
if (ulong.TryParse(id, out _TradeOfferID) && (_TradeOfferID != 0)) {
425+
return _TradeOfferID;
426+
}
427+
428+
Logging.LogNullError(nameof(_TradeOfferID));
429+
return 0;
430+
}
431+
}
432+
347433
private ulong _OtherSteamID64;
348434
internal ulong OtherSteamID64 {
349435
get {
350436
if (_OtherSteamID64 != 0) {
351437
return _OtherSteamID64;
352438
}
353439

440+
if (Type != EType.Trade) {
441+
return 0;
442+
}
443+
354444
if (OtherSteamID3 == 0) {
355445
Logging.LogNullError(nameof(OtherSteamID3));
356446
return 0;
@@ -371,6 +461,10 @@ private uint OtherSteamID3 {
371461
return _OtherSteamID3;
372462
}
373463

464+
if (Type != EType.Trade) {
465+
return 0;
466+
}
467+
374468
if (HtmlDocument == null) {
375469
Logging.LogNullError(nameof(HtmlDocument));
376470
return 0;

ArchiSteamFarm/MobileAuthenticator.cs

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,16 @@ internal sealed class MobileAuthenticator {
1313
internal sealed class Confirmation {
1414
internal readonly uint ID;
1515
internal readonly ulong Key;
16+
internal readonly Steam.ConfirmationDetails.EType Type;
1617

17-
internal Confirmation(uint id, ulong key) {
18-
if ((id == 0) || (key == 0)) {
19-
throw new ArgumentNullException(nameof(id) + " || " + nameof(key));
18+
internal Confirmation(uint id, ulong key, Steam.ConfirmationDetails.EType type) {
19+
if ((id == 0) || (key == 0) || (type == Steam.ConfirmationDetails.EType.Unknown)) {
20+
throw new ArgumentNullException(nameof(id) + " || " + nameof(key) + " || " + nameof(type));
2021
}
2122

2223
ID = id;
2324
Key = key;
25+
Type = type;
2426
}
2527
}
2628

@@ -146,14 +148,14 @@ internal async Task<HashSet<Confirmation>> GetConfirmations() {
146148
return null;
147149
}
148150

149-
HtmlNodeCollection confirmations = htmlDocument.DocumentNode.SelectNodes("//div[@class='mobileconf_list_entry']");
150-
if (confirmations == null) {
151+
HtmlNodeCollection confirmationNodes = htmlDocument.DocumentNode.SelectNodes("//div[@class='mobileconf_list_entry']");
152+
if (confirmationNodes == null) {
151153
return null;
152154
}
153155

154156
HashSet<Confirmation> result = new HashSet<Confirmation>();
155-
foreach (HtmlNode confirmation in confirmations) {
156-
string idString = confirmation.GetAttributeValue("data-confid", null);
157+
foreach (HtmlNode confirmationNode in confirmationNodes) {
158+
string idString = confirmationNode.GetAttributeValue("data-confid", null);
157159
if (string.IsNullOrEmpty(idString)) {
158160
Logging.LogNullError(nameof(idString), Bot.BotName);
159161
continue;
@@ -165,7 +167,7 @@ internal async Task<HashSet<Confirmation>> GetConfirmations() {
165167
continue;
166168
}
167169

168-
string keyString = confirmation.GetAttributeValue("data-key", null);
170+
string keyString = confirmationNode.GetAttributeValue("data-key", null);
169171
if (string.IsNullOrEmpty(keyString)) {
170172
Logging.LogNullError(nameof(keyString), Bot.BotName);
171173
continue;
@@ -177,7 +179,24 @@ internal async Task<HashSet<Confirmation>> GetConfirmations() {
177179
continue;
178180
}
179181

180-
result.Add(new Confirmation(id, key));
182+
HtmlNode descriptionNode = confirmationNode.SelectSingleNode(".//div[@class='mobileconf_list_entry_description']/div");
183+
if (descriptionNode == null) {
184+
Logging.LogNullError(nameof(descriptionNode), Bot.BotName);
185+
continue;
186+
}
187+
188+
Steam.ConfirmationDetails.EType type;
189+
190+
string description = descriptionNode.InnerText;
191+
if (description.Equals("Sell - Market Listing")) {
192+
type = Steam.ConfirmationDetails.EType.Market;
193+
} else if (description.StartsWith("Trade with ", StringComparison.Ordinal)) {
194+
type = Steam.ConfirmationDetails.EType.Trade;
195+
} else {
196+
type = Steam.ConfirmationDetails.EType.Other;
197+
}
198+
199+
result.Add(new Confirmation(id, key, type));
181200
}
182201

183202
return result;

ArchiSteamFarm/Trading.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,9 @@ private async Task ParseActiveTrades() {
9494
}
9595

9696
await tradeOffers.ForEachAsync(ParseTrade).ConfigureAwait(false);
97-
await Bot.AcceptConfirmations(true).ConfigureAwait(false);
97+
98+
HashSet<ulong> tradeIDs = new HashSet<ulong>(tradeOffers.Select(tradeOffer => tradeOffer.TradeOfferID));
99+
await Bot.AcceptConfirmations(true, Steam.ConfirmationDetails.EType.Trade, 0, tradeIDs).ConfigureAwait(false);
98100
}
99101

100102
private async Task ParseTrade(Steam.TradeOffer tradeOffer) {

0 commit comments

Comments
 (0)