mirror of
https://github.com/JustArchiNET/ArchiSteamFarm.git
synced 2026-01-01 14:10:53 +00:00
676 lines
21 KiB
C#
676 lines
21 KiB
C#
/*
|
|
_ _ _ ____ _ _____
|
|
/ \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
|
|
/ _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
|
|
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
|
|
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
|
|
|
|
Copyright 2015-2016 Ł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 Newtonsoft.Json.Linq;
|
|
using HtmlAgilityPack;
|
|
using SteamKit2;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Net;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using System.Xml;
|
|
using System.Threading;
|
|
|
|
namespace ArchiSteamFarm {
|
|
internal sealed class ArchiWebHandler {
|
|
private const string SteamCommunity = "steamcommunity.com";
|
|
private const byte MinSessionTTL = 15; // Assume session is valid for at least that amount of seconds
|
|
|
|
private static string SteamCommunityURL = "https://" + SteamCommunity;
|
|
|
|
private static int Timeout = GlobalConfig.DefaultHttpTimeout * 1000;
|
|
|
|
private readonly Bot Bot;
|
|
private readonly SemaphoreSlim SessionSemaphore = new SemaphoreSlim(1);
|
|
private readonly WebBrowser WebBrowser;
|
|
|
|
private DateTime LastSessionRefreshCheck = DateTime.MinValue;
|
|
|
|
internal static void Init() {
|
|
Timeout = Program.GlobalConfig.HttpTimeout * 1000;
|
|
SteamCommunityURL = (Program.GlobalConfig.ForceHttp ? "http://" : "https://") + SteamCommunity;
|
|
}
|
|
|
|
internal ArchiWebHandler(Bot bot) {
|
|
if (bot == null) {
|
|
throw new ArgumentNullException("bot");
|
|
}
|
|
|
|
Bot = bot;
|
|
|
|
WebBrowser = new WebBrowser(bot.BotName);
|
|
}
|
|
|
|
internal bool Init(SteamClient steamClient, string webAPIUserNonce, string parentalPin) {
|
|
if (steamClient == null || steamClient.SteamID == null || string.IsNullOrEmpty(webAPIUserNonce)) {
|
|
return false;
|
|
}
|
|
|
|
ulong steamID = steamClient.SteamID;
|
|
|
|
string sessionID = Convert.ToBase64String(Encoding.UTF8.GetBytes(steamID.ToString()));
|
|
|
|
// Generate an AES session key
|
|
byte[] sessionKey = CryptoHelper.GenerateRandomBlock(32);
|
|
|
|
// RSA encrypt it with the public key for the universe we're on
|
|
byte[] cryptedSessionKey;
|
|
using (RSACrypto rsa = new RSACrypto(KeyDictionary.GetPublicKey(steamClient.ConnectedUniverse))) {
|
|
cryptedSessionKey = rsa.Encrypt(sessionKey);
|
|
}
|
|
|
|
// Copy our login key
|
|
byte[] loginKey = new byte[webAPIUserNonce.Length];
|
|
Array.Copy(Encoding.ASCII.GetBytes(webAPIUserNonce), loginKey, webAPIUserNonce.Length);
|
|
|
|
// AES encrypt the loginkey with our session key
|
|
byte[] cryptedLoginKey = CryptoHelper.SymmetricEncrypt(loginKey, sessionKey);
|
|
|
|
// Do the magic
|
|
Logging.LogGenericInfo("Logging in to ISteamUserAuth...", Bot.BotName);
|
|
|
|
KeyValue authResult;
|
|
using (dynamic iSteamUserAuth = WebAPI.GetInterface("ISteamUserAuth")) {
|
|
iSteamUserAuth.Timeout = Timeout;
|
|
|
|
try {
|
|
authResult = iSteamUserAuth.AuthenticateUser(
|
|
steamid: steamID,
|
|
sessionkey: Encoding.ASCII.GetString(WebUtility.UrlEncodeToBytes(cryptedSessionKey, 0, cryptedSessionKey.Length)),
|
|
encrypted_loginkey: Encoding.ASCII.GetString(WebUtility.UrlEncodeToBytes(cryptedLoginKey, 0, cryptedLoginKey.Length)),
|
|
method: WebRequestMethods.Http.Post,
|
|
secure: !Program.GlobalConfig.ForceHttp
|
|
);
|
|
} catch (Exception e) {
|
|
Logging.LogGenericException(e, Bot.BotName);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (authResult == null) {
|
|
return false;
|
|
}
|
|
|
|
Logging.LogGenericInfo("Success!", Bot.BotName);
|
|
|
|
WebBrowser.CookieContainer.Add(new Cookie("sessionid", sessionID, "/", "." + SteamCommunity));
|
|
|
|
string steamLogin = authResult["token"].Value;
|
|
WebBrowser.CookieContainer.Add(new Cookie("steamLogin", steamLogin, "/", "." + SteamCommunity));
|
|
|
|
string steamLoginSecure = authResult["tokensecure"].Value;
|
|
WebBrowser.CookieContainer.Add(new Cookie("steamLoginSecure", steamLoginSecure, "/", "." + SteamCommunity));
|
|
|
|
if (!UnlockParentalAccount(parentalPin).Result) {
|
|
return false;
|
|
}
|
|
|
|
LastSessionRefreshCheck = DateTime.Now;
|
|
return true;
|
|
}
|
|
|
|
internal async Task<bool?> IsLoggedIn() {
|
|
HtmlDocument htmlDocument = null;
|
|
for (byte i = 0; i < WebBrowser.MaxRetries && htmlDocument == null; i++) {
|
|
htmlDocument = await WebBrowser.UrlGetToHtmlDocument(SteamCommunityURL + "/my/profile").ConfigureAwait(false);
|
|
}
|
|
|
|
if (htmlDocument == null) {
|
|
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
|
|
return null;
|
|
}
|
|
|
|
HtmlNode htmlNode = htmlDocument.DocumentNode.SelectSingleNode("//span[@id='account_pulldown']");
|
|
return htmlNode != null;
|
|
}
|
|
|
|
internal async Task<bool> RefreshSessionIfNeeded() {
|
|
DateTime now = DateTime.Now;
|
|
if (now.Subtract(LastSessionRefreshCheck).TotalSeconds < MinSessionTTL) {
|
|
return true;
|
|
}
|
|
|
|
await SessionSemaphore.WaitAsync().ConfigureAwait(false);
|
|
|
|
now = DateTime.Now;
|
|
if (now.Subtract(LastSessionRefreshCheck).TotalSeconds < MinSessionTTL) {
|
|
SessionSemaphore.Release();
|
|
return true;
|
|
}
|
|
|
|
bool result;
|
|
|
|
bool? isLoggedIn = await IsLoggedIn().ConfigureAwait(false);
|
|
if (isLoggedIn.GetValueOrDefault(true)) {
|
|
result = true;
|
|
now = DateTime.Now;
|
|
LastSessionRefreshCheck = now;
|
|
} else {
|
|
Logging.LogGenericInfo("Refreshing our session!", Bot.BotName);
|
|
result = await Bot.RefreshSession().ConfigureAwait(false);
|
|
}
|
|
|
|
SessionSemaphore.Release();
|
|
return result;
|
|
}
|
|
|
|
internal async Task<Dictionary<uint, string>> GetOwnedGames() {
|
|
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
|
return null;
|
|
}
|
|
|
|
string request = SteamCommunityURL + "/my/games/?xml=1";
|
|
|
|
XmlDocument response = null;
|
|
for (byte i = 0; i < WebBrowser.MaxRetries && response == null; i++) {
|
|
response = await WebBrowser.UrlGetToXML(request).ConfigureAwait(false);
|
|
}
|
|
|
|
if (response == null) {
|
|
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
|
|
return null;
|
|
}
|
|
|
|
XmlNodeList xmlNodeList = response.SelectNodes("gamesList/games/game");
|
|
if (xmlNodeList == null || xmlNodeList.Count == 0) {
|
|
return null;
|
|
}
|
|
|
|
Dictionary<uint, string> result = new Dictionary<uint, string>(xmlNodeList.Count);
|
|
foreach (XmlNode xmlNode in xmlNodeList) {
|
|
XmlNode appNode = xmlNode.SelectSingleNode("appID");
|
|
if (appNode == null) {
|
|
continue;
|
|
}
|
|
|
|
uint appID;
|
|
if (!uint.TryParse(appNode.InnerText, out appID)) {
|
|
continue;
|
|
}
|
|
|
|
XmlNode nameNode = xmlNode.SelectSingleNode("name");
|
|
if (nameNode == null) {
|
|
continue;
|
|
}
|
|
|
|
result[appID] = nameNode.InnerText;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
internal HashSet<Steam.TradeOffer> GetTradeOffers() {
|
|
if (string.IsNullOrEmpty(Bot.BotConfig.SteamApiKey)) {
|
|
return null;
|
|
}
|
|
|
|
KeyValue response = null;
|
|
using (dynamic iEconService = WebAPI.GetInterface("IEconService", Bot.BotConfig.SteamApiKey)) {
|
|
iEconService.Timeout = Timeout;
|
|
|
|
for (byte i = 0; i < WebBrowser.MaxRetries && response == null; i++) {
|
|
try {
|
|
response = iEconService.GetTradeOffers(
|
|
get_received_offers: 1,
|
|
active_only: 1,
|
|
get_descriptions: 1,
|
|
secure: !Program.GlobalConfig.ForceHttp
|
|
);
|
|
} catch (Exception e) {
|
|
Logging.LogGenericException(e, Bot.BotName);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (response == null) {
|
|
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
|
|
return null;
|
|
}
|
|
|
|
Dictionary<Tuple<ulong, ulong>, uint> appIDMap = new Dictionary<Tuple<ulong, ulong>, uint>();
|
|
Dictionary<Tuple<ulong, ulong>, Steam.Item.EType> typeMap = new Dictionary<Tuple<ulong, ulong>, Steam.Item.EType>();
|
|
foreach (KeyValue description in response["descriptions"].Children) {
|
|
ulong classID = description["classid"].AsUnsignedLong();
|
|
if (classID == 0) {
|
|
continue;
|
|
}
|
|
|
|
ulong instanceID = description["instanceid"].AsUnsignedLong();
|
|
|
|
Tuple<ulong, ulong> key = new Tuple<ulong, ulong>(classID, instanceID);
|
|
|
|
if (!appIDMap.ContainsKey(key)) {
|
|
string hashName = description["market_hash_name"].Value;
|
|
if (!string.IsNullOrEmpty(hashName)) {
|
|
int index = hashName.IndexOf('-');
|
|
if (index < 1) {
|
|
continue;
|
|
}
|
|
|
|
uint appID;
|
|
if (!uint.TryParse(hashName.Substring(0, index), out appID)) {
|
|
continue;
|
|
}
|
|
|
|
appIDMap[key] = appID;
|
|
}
|
|
}
|
|
|
|
if (!typeMap.ContainsKey(key)) {
|
|
string type = description["type"].Value;
|
|
if (!string.IsNullOrEmpty(type)) {
|
|
switch (type) {
|
|
case "Booster Pack":
|
|
typeMap[key] = Steam.Item.EType.BoosterPack;
|
|
break;
|
|
case "Coupon":
|
|
typeMap[key] = Steam.Item.EType.Coupon;
|
|
break;
|
|
case "Gift":
|
|
typeMap[key] = Steam.Item.EType.Gift;
|
|
break;
|
|
case "Steam Gems":
|
|
typeMap[key] = Steam.Item.EType.SteamGems;
|
|
break;
|
|
default:
|
|
if (type.EndsWith("Emoticon", StringComparison.Ordinal)) {
|
|
typeMap[key] = Steam.Item.EType.Emoticon;
|
|
} else if (type.EndsWith("Foil Trading Card", StringComparison.Ordinal)) {
|
|
typeMap[key] = Steam.Item.EType.FoilTradingCard;
|
|
} else if (type.EndsWith("Profile Background", StringComparison.Ordinal)) {
|
|
typeMap[key] = Steam.Item.EType.ProfileBackground;
|
|
} else if (type.EndsWith("Trading Card", StringComparison.Ordinal)) {
|
|
typeMap[key] = Steam.Item.EType.TradingCard;
|
|
} else {
|
|
typeMap[key] = Steam.Item.EType.Unknown;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
HashSet<Steam.TradeOffer> result = new HashSet<Steam.TradeOffer>();
|
|
foreach (KeyValue trade in response["trade_offers_received"].Children) {
|
|
// TODO: Correct some of these when SK2 with https://github.com/SteamRE/SteamKit/pull/255 gets released
|
|
Steam.TradeOffer tradeOffer = new Steam.TradeOffer {
|
|
TradeOfferID = trade["tradeofferid"].AsUnsignedLong(),
|
|
OtherSteamID3 = (uint) trade["accountid_other"].AsUnsignedLong(),
|
|
State = trade["trade_offer_state"].AsEnum<Steam.TradeOffer.ETradeOfferState>()
|
|
};
|
|
|
|
foreach (KeyValue item in trade["items_to_give"].Children) {
|
|
Steam.Item steamItem = new Steam.Item {
|
|
AppID = (uint) item["appid"].AsUnsignedLong(),
|
|
ContextID = item["contextid"].AsUnsignedLong(),
|
|
AssetID = item["assetid"].AsUnsignedLong(),
|
|
ClassID = item["classid"].AsUnsignedLong(),
|
|
InstanceID = item["instanceid"].AsUnsignedLong(),
|
|
Amount = (uint) item["amount"].AsUnsignedLong()
|
|
};
|
|
|
|
Tuple<ulong, ulong> key = new Tuple<ulong, ulong>(steamItem.ClassID, steamItem.InstanceID);
|
|
|
|
uint realAppID;
|
|
if (appIDMap.TryGetValue(key, out realAppID)) {
|
|
steamItem.RealAppID = realAppID;
|
|
}
|
|
|
|
Steam.Item.EType type;
|
|
if (typeMap.TryGetValue(key, out type)) {
|
|
steamItem.Type = type;
|
|
}
|
|
|
|
tradeOffer.ItemsToGive.Add(steamItem);
|
|
}
|
|
|
|
foreach (KeyValue item in trade["items_to_receive"].Children) {
|
|
Steam.Item steamItem = new Steam.Item {
|
|
AppID = (uint) item["appid"].AsUnsignedLong(),
|
|
ContextID = item["contextid"].AsUnsignedLong(),
|
|
AssetID = item["assetid"].AsUnsignedLong(),
|
|
ClassID = item["classid"].AsUnsignedLong(),
|
|
InstanceID = item["instanceid"].AsUnsignedLong(),
|
|
Amount = (uint) item["amount"].AsUnsignedLong()
|
|
};
|
|
|
|
Tuple<ulong, ulong> key = new Tuple<ulong, ulong>(steamItem.ClassID, steamItem.InstanceID);
|
|
|
|
uint realAppID;
|
|
if (appIDMap.TryGetValue(key, out realAppID)) {
|
|
steamItem.RealAppID = realAppID;
|
|
}
|
|
|
|
Steam.Item.EType type;
|
|
if (typeMap.TryGetValue(key, out type)) {
|
|
steamItem.Type = type;
|
|
}
|
|
|
|
tradeOffer.ItemsToReceive.Add(steamItem);
|
|
}
|
|
|
|
result.Add(tradeOffer);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
internal async Task<bool> JoinClan(ulong clanID) {
|
|
if (clanID == 0) {
|
|
return false;
|
|
}
|
|
|
|
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
|
return false;
|
|
}
|
|
|
|
string sessionID = WebBrowser.CookieContainer.GetCookieValue(SteamCommunityURL, "sessionid");
|
|
if (string.IsNullOrEmpty(sessionID)) {
|
|
Logging.LogNullError("sessionID");
|
|
return false;
|
|
}
|
|
|
|
string request = SteamCommunityURL + "/gid/" + clanID;
|
|
|
|
Dictionary<string, string> data = new Dictionary<string, string>(2) {
|
|
{ "sessionID", sessionID },
|
|
{ "action", "join" }
|
|
};
|
|
|
|
bool result = false;
|
|
for (byte i = 0; i < WebBrowser.MaxRetries && !result; i++) {
|
|
result = await WebBrowser.UrlPost(request, data).ConfigureAwait(false);
|
|
}
|
|
|
|
if (!result) {
|
|
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
internal async Task<bool> AcceptTradeOffer(ulong tradeID) {
|
|
if (tradeID == 0) {
|
|
return false;
|
|
}
|
|
|
|
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
|
return false;
|
|
}
|
|
|
|
string sessionID = WebBrowser.CookieContainer.GetCookieValue(SteamCommunityURL, "sessionid");
|
|
if (string.IsNullOrEmpty(sessionID)) {
|
|
Logging.LogNullError("sessionID");
|
|
return false;
|
|
}
|
|
|
|
string referer = SteamCommunityURL + "/tradeoffer/" + tradeID;
|
|
string request = referer + "/accept";
|
|
|
|
Dictionary<string, string> data = new Dictionary<string, string>(3) {
|
|
{ "sessionid", sessionID },
|
|
{ "serverid", "1" },
|
|
{ "tradeofferid", tradeID.ToString() }
|
|
};
|
|
|
|
bool result = false;
|
|
for (byte i = 0; i < WebBrowser.MaxRetries && !result; i++) {
|
|
result = await WebBrowser.UrlPost(request, data, referer).ConfigureAwait(false);
|
|
}
|
|
|
|
if (!result) {
|
|
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
internal async Task<HashSet<Steam.Item>> GetMyTradableInventory() {
|
|
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
|
return null;
|
|
}
|
|
|
|
JObject jObject = null;
|
|
for (byte i = 0; i < WebBrowser.MaxRetries && jObject == null; i++) {
|
|
jObject = await WebBrowser.UrlGetToJObject(SteamCommunityURL + "/my/inventory/json/" + Steam.Item.SteamAppID + "/" + Steam.Item.SteamContextID + "?trading=1").ConfigureAwait(false);
|
|
}
|
|
|
|
if (jObject == null) {
|
|
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
|
|
return null;
|
|
}
|
|
|
|
IEnumerable<JToken> jTokens = jObject.SelectTokens("$.rgInventory.*");
|
|
if (jTokens == null) {
|
|
Logging.LogNullError("jTokens", Bot.BotName);
|
|
return null;
|
|
}
|
|
|
|
HashSet<Steam.Item> result = new HashSet<Steam.Item>();
|
|
foreach (JToken jToken in jTokens) {
|
|
try {
|
|
result.Add(JsonConvert.DeserializeObject<Steam.Item>(jToken.ToString()));
|
|
} catch (Exception e) {
|
|
Logging.LogGenericException(e, Bot.BotName);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
internal async Task<bool> SendTradeOffer(HashSet<Steam.Item> inventory, ulong partnerID, string token = null) {
|
|
if (inventory == null || inventory.Count == 0 || partnerID == 0) {
|
|
return false;
|
|
}
|
|
|
|
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
|
return false;
|
|
}
|
|
|
|
string sessionID = WebBrowser.CookieContainer.GetCookieValue(SteamCommunityURL, "sessionid");
|
|
if (string.IsNullOrEmpty(sessionID)) {
|
|
Logging.LogNullError("sessionID");
|
|
return false;
|
|
}
|
|
|
|
HashSet<Steam.TradeOfferRequest> trades = new HashSet<Steam.TradeOfferRequest>();
|
|
|
|
Steam.TradeOfferRequest singleTrade = null;
|
|
|
|
byte itemID = 0;
|
|
foreach (Steam.Item item in inventory) {
|
|
if (itemID % Trading.MaxItemsPerTrade == 0) {
|
|
if (trades.Count >= Trading.MaxTradesPerAccount) {
|
|
break;
|
|
}
|
|
|
|
singleTrade = new Steam.TradeOfferRequest();
|
|
trades.Add(singleTrade);
|
|
itemID = 0;
|
|
}
|
|
|
|
singleTrade.ItemsToGive.Assets.Add(new Steam.Item() {
|
|
AppID = Steam.Item.SteamAppID,
|
|
ContextID = Steam.Item.SteamContextID,
|
|
Amount = item.Amount,
|
|
AssetID = item.AssetID
|
|
});
|
|
|
|
itemID++;
|
|
}
|
|
|
|
string referer = SteamCommunityURL + "/tradeoffer/new";
|
|
string request = referer + "/send";
|
|
|
|
foreach (Steam.TradeOfferRequest trade in trades) {
|
|
Dictionary<string, string> data = new Dictionary<string, string>(6) {
|
|
{ "sessionid", sessionID },
|
|
{ "serverid", "1" },
|
|
{ "partner", partnerID.ToString() },
|
|
{ "tradeoffermessage", "Sent by ASF" },
|
|
{ "json_tradeoffer", JsonConvert.SerializeObject(trade) },
|
|
{ "trade_offer_create_params", string.IsNullOrEmpty(token) ? "" : $"{{\"trade_offer_access_token\":\"{token}\"}}" }
|
|
};
|
|
|
|
bool result = false;
|
|
for (byte i = 0; i < WebBrowser.MaxRetries && !result; i++) {
|
|
result = await WebBrowser.UrlPost(request, data, referer).ConfigureAwait(false);
|
|
}
|
|
|
|
if (!result) {
|
|
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
internal async Task<HtmlDocument> GetBadgePage(byte page) {
|
|
if (page == 0) {
|
|
return null;
|
|
}
|
|
|
|
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
|
return null;
|
|
}
|
|
|
|
HtmlDocument htmlDocument = null;
|
|
for (byte i = 0; i < WebBrowser.MaxRetries && htmlDocument == null; i++) {
|
|
htmlDocument = await WebBrowser.UrlGetToHtmlDocument(SteamCommunityURL + "/my/badges?p=" + page).ConfigureAwait(false);
|
|
}
|
|
|
|
if (htmlDocument == null) {
|
|
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
|
|
return null;
|
|
}
|
|
|
|
return htmlDocument;
|
|
}
|
|
|
|
internal async Task<HtmlDocument> GetGameCardsPage(ulong appID) {
|
|
if (appID == 0) {
|
|
return null;
|
|
}
|
|
|
|
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
|
return null;
|
|
}
|
|
|
|
HtmlDocument htmlDocument = null;
|
|
for (byte i = 0; i < WebBrowser.MaxRetries && htmlDocument == null; i++) {
|
|
htmlDocument = await WebBrowser.UrlGetToHtmlDocument(SteamCommunityURL + "/my/gamecards/" + appID).ConfigureAwait(false);
|
|
}
|
|
|
|
if (htmlDocument == null) {
|
|
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
|
|
return null;
|
|
}
|
|
|
|
return htmlDocument;
|
|
}
|
|
|
|
internal async Task<bool> MarkInventory() {
|
|
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
|
return false;
|
|
}
|
|
|
|
bool result = false;
|
|
for (byte i = 0; i < WebBrowser.MaxRetries && !result; i++) {
|
|
result = await WebBrowser.UrlGet(SteamCommunityURL + "/my/inventory").ConfigureAwait(false);
|
|
}
|
|
|
|
if (!result) {
|
|
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
internal async Task<bool> AcceptGift(ulong gid) {
|
|
if (gid == 0) {
|
|
return false;
|
|
}
|
|
|
|
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
|
return false;
|
|
}
|
|
|
|
string sessionID = WebBrowser.CookieContainer.GetCookieValue(SteamCommunityURL, "sessionid");
|
|
if (string.IsNullOrEmpty(sessionID)) {
|
|
Logging.LogNullError("sessionID");
|
|
return false;
|
|
}
|
|
|
|
string request = SteamCommunityURL + "/gifts/" + gid + "/acceptunpack";
|
|
Dictionary<string, string> data = new Dictionary<string, string>(1) {
|
|
{ "sessionid", sessionID }
|
|
};
|
|
|
|
bool result = false;
|
|
for (byte i = 0; i < WebBrowser.MaxRetries && !result; i++) {
|
|
result = await WebBrowser.UrlPost(request, data).ConfigureAwait(false);
|
|
}
|
|
|
|
if (!result) {
|
|
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private async Task<bool> UnlockParentalAccount(string parentalPin) {
|
|
if (string.IsNullOrEmpty(parentalPin) || parentalPin.Equals("0")) {
|
|
return true;
|
|
}
|
|
|
|
Logging.LogGenericInfo("Unlocking parental account...", Bot.BotName);
|
|
Dictionary<string, string> data = new Dictionary<string, string>(1) {
|
|
{ "pin", parentalPin }
|
|
};
|
|
|
|
string referer = SteamCommunityURL;
|
|
string request = referer + "/parental/ajaxunlock";
|
|
|
|
bool result = false;
|
|
for (byte i = 0; i < WebBrowser.MaxRetries && !result; i++) {
|
|
result = await WebBrowser.UrlPost(request, data, referer).ConfigureAwait(false);
|
|
}
|
|
|
|
if (!result) {
|
|
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
|
|
return false;
|
|
}
|
|
|
|
Logging.LogGenericInfo("Success!", Bot.BotName);
|
|
return true;
|
|
}
|
|
}
|
|
}
|