internal async Task <HtmlDocument> UrlGetToHtmlDocumentRetry(string request, string referer = null) { if (string.IsNullOrEmpty(request)) { Logging.LogNullError(nameof(request)); return(null); } HtmlDocument result = null; for (byte i = 0; (i < MaxRetries) && (result == null); i++) { result = await UrlGetToHtmlDocument(request, referer).ConfigureAwait(false); } if (result != null) { return(result); } Logging.LogGenericWTF("Request failed even after " + MaxRetries + " tries", Identifier); return(null); }
internal async Task <bool> UrlHeadRetry(string request, string referer = null) { if (string.IsNullOrEmpty(request)) { Logging.LogNullError(nameof(request), Identifier); return(false); } bool result = false; for (byte i = 0; (i < MaxRetries) && !result; i++) { result = await UrlHead(request, referer).ConfigureAwait(false); } if (result) { return(true); } Logging.LogGenericWTF("Request failed even after " + MaxRetries + " tries", Identifier); return(false); }
internal async Task <bool> JoinClan(ulong clanID) { if (clanID == 0) { return(false); } string sessionID; if (!Cookie.TryGetValue("sessionid", out sessionID)) { return(false); } string request = SteamCommunityURL + "/gid/" + clanID; Dictionary <string, string> data = new Dictionary <string, string>(2) { { "sessionID", sessionID }, { "action", "join" } }; HttpResponseMessage response = null; for (byte i = 0; i < WebBrowser.MaxRetries && response == null; i++) { response = await WebBrowser.UrlPost(request, data, Cookie).ConfigureAwait(false); } if (response == null) { Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName); return(false); } return(true); }
internal bool DeclineTradeOffer(ulong tradeID) { if ((tradeID == 0) || string.IsNullOrEmpty(Bot.BotConfig.SteamApiKey)) { // TODO: Correct this when Mono 4.4+ will be a latest stable one | https://bugzilla.xamarin.com/show_bug.cgi?id=39455 Logging.LogNullError("tradeID || SteamApiKey", Bot.BotName); //Logging.LogNullError(nameof(tradeID) + " || " + nameof(Bot.BotConfig.SteamApiKey), Bot.BotName); return(false); } 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.DeclineTradeOffer( tradeofferid: tradeID.ToString(), method: WebRequestMethods.Http.Post, secure: !Program.GlobalConfig.ForceHttp ); } catch (Exception e) { Logging.LogGenericException(e, Bot.BotName); } } } if (response != null) { return(true); } Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName); return(false); }
internal async Task <List <Steam.Item> > GetMyTradableInventory() { JObject jObject = null; for (byte i = 0; i < WebBrowser.MaxRetries && jObject == null; i++) { jObject = await WebBrowser.UrlGetToJObject(SteamCommunityURL + "/my/inventory/json/753/6?trading=1", Cookie).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); } List <Steam.Item> result = new List <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?> IsLoggedIn() { if (SteamID == 0) { return(false); } HtmlDocument htmlDocument = null; for (byte i = 0; i < WebBrowser.MaxRetries && htmlDocument == null; i++) { htmlDocument = await WebBrowser.UrlGetToHtmlDocument(SteamCommunityURL + "/my/profile", Cookie).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> MarkInventory() { if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) { return(false); } string request = SteamCommunityURL + "/my/inventory"; bool result = false; for (byte i = 0; (i < WebBrowser.MaxRetries) && !result; i++) { result = await WebBrowser.UrlHead(request).ConfigureAwait(false); } if (result) { return(true); } Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName); return(false); }
internal async Task <bool> SendTradeOffer(List <Steam.Item> inventory, ulong partnerID, string token = null) { if (inventory == null || inventory.Count == 0 || partnerID == 0) { return(false); } string sessionID; if (!Cookie.TryGetValue("sessionid", out sessionID)) { return(false); } List <Steam.TradeOfferRequest> trades = new List <Steam.TradeOfferRequest>(1 + inventory.Count / Trading.MaxItemsPerTrade); Steam.TradeOfferRequest singleTrade = null; for (ushort i = 0; i < inventory.Count; i++) { if (i % Trading.MaxItemsPerTrade == 0) { if (trades.Count >= Trading.MaxTradesPerAccount) { break; } singleTrade = new Steam.TradeOfferRequest(); trades.Add(singleTrade); } Steam.Item item = inventory[i]; singleTrade.me.assets.Add(new Steam.Item() { appid = "753", contextid = "6", amount = item.amount, assetid = item.id }); } 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}\"}}" } }; HttpResponseMessage response = null; for (byte i = 0; i < WebBrowser.MaxRetries && response == null; i++) { response = await WebBrowser.UrlPost(request, data, Cookie, referer).ConfigureAwait(false); } if (response == null) { Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName); return(false); } } return(true); }
internal List <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, 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); } List <Steam.TradeOffer> result = new List <Steam.TradeOffer>(); foreach (KeyValue trade in response["trade_offers_received"].Children) { Steam.TradeOffer tradeOffer = new Steam.TradeOffer { tradeofferid = trade["tradeofferid"].AsString(), accountid_other = (uint)trade["accountid_other"].AsUnsignedLong(), // TODO: Correct this when SK2 with https://github.com/SteamRE/SteamKit/pull/255 gets released trade_offer_state = trade["trade_offer_state"].AsEnum <Steam.TradeOffer.ETradeOfferState>() }; foreach (KeyValue item in trade["items_to_give"].Children) { tradeOffer.items_to_give.Add(new Steam.Item { appid = item["appid"].AsString(), contextid = item["contextid"].AsString(), assetid = item["assetid"].AsString(), classid = item["classid"].AsString(), instanceid = item["instanceid"].AsString(), amount = item["amount"].AsString(), }); } foreach (KeyValue item in trade["items_to_receive"].Children) { tradeOffer.items_to_receive.Add(new Steam.Item { appid = item["appid"].AsString(), contextid = item["contextid"].AsString(), assetid = item["assetid"].AsString(), classid = item["classid"].AsString(), instanceid = item["instanceid"].AsString(), amount = item["amount"].AsString(), }); } result.Add(tradeOffer); } return(result); }
internal HashSet <Steam.TradeOffer> GetTradeOffers() { if (string.IsNullOrEmpty(Bot.BotConfig.SteamApiKey)) { // TODO: Correct this when Mono 4.4+ will be a latest stable one | https://bugzilla.xamarin.com/show_bug.cgi?id=39455 Logging.LogNullError("SteamApiKey", Bot.BotName); //Logging.LogNullError(nameof(Bot.BotConfig.SteamApiKey), Bot.BotName); 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 <ulong, Tuple <uint, Steam.Item.EType> > descriptions = new Dictionary <ulong, Tuple <uint, Steam.Item.EType> >(); foreach (KeyValue description in response["descriptions"].Children) { ulong classID = description["classid"].AsUnsignedLong(); if (classID == 0) { Logging.LogNullError(nameof(classID), Bot.BotName); continue; } if (descriptions.ContainsKey(classID)) { continue; } uint appID = 0; string hashName = description["market_hash_name"].Value; if (!string.IsNullOrEmpty(hashName)) { appID = GetAppIDFromMarketHashName(hashName); } if (appID == 0) { appID = (uint)description["appid"].AsUnsignedLong(); } Steam.Item.EType type = Steam.Item.EType.Unknown; string descriptionType = description["type"].Value; if (!string.IsNullOrEmpty(descriptionType)) { type = GetItemType(descriptionType); } descriptions[classID] = new Tuple <uint, Steam.Item.EType>(appID, type); } HashSet <Steam.TradeOffer> result = new HashSet <Steam.TradeOffer>(); foreach (KeyValue trade in response["trade_offers_received"].Children) { 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 (Steam.Item steamItem in trade["items_to_give"].Children.Select(item => 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 <uint, Steam.Item.EType> description; if (descriptions.TryGetValue(steamItem.ClassID, out description)) { steamItem.RealAppID = description.Item1; steamItem.Type = description.Item2; } tradeOffer.ItemsToGive.Add(steamItem); } foreach (Steam.Item steamItem in trade["items_to_receive"].Children.Select(item => 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 <uint, Steam.Item.EType> description; if (descriptions.TryGetValue(steamItem.ClassID, out description)) { steamItem.RealAppID = description.Item1; steamItem.Type = description.Item2; } tradeOffer.ItemsToReceive.Add(steamItem); } result.Add(tradeOffer); } return(result); }
internal static async Task CheckForUpdate(bool updateOverride = false) { string oldExeFile = ExecutableFile + ".old"; // We booted successfully so we can now remove old exe file if (File.Exists(oldExeFile)) { // It's entirely possible that old process is still running, allow at least a second before trying to remove the file await Utilities.SleepAsync(1000).ConfigureAwait(false); try { File.Delete(oldExeFile); } catch (Exception e) { Logging.LogGenericException(e); Logging.LogGenericError("Could not remove old ASF binary, please remove " + oldExeFile + " manually in order for update function to work!"); } } if (GlobalConfig.UpdateChannel == GlobalConfig.EUpdateChannel.Unknown) { return; } string releaseURL = GithubReleaseURL; if (GlobalConfig.UpdateChannel == GlobalConfig.EUpdateChannel.Stable) { releaseURL += "/latest"; } string response = null; Logging.LogGenericInfo("Checking new version..."); for (byte i = 0; (i < WebBrowser.MaxRetries) && string.IsNullOrEmpty(response); i++) { response = await WebBrowser.UrlGetToContent(releaseURL).ConfigureAwait(false); } if (string.IsNullOrEmpty(response)) { Logging.LogGenericWarning("Could not check latest version!"); return; } GitHub.ReleaseResponse releaseResponse; if (GlobalConfig.UpdateChannel == GlobalConfig.EUpdateChannel.Stable) { try { releaseResponse = JsonConvert.DeserializeObject <GitHub.ReleaseResponse>(response); } catch (JsonException e) { Logging.LogGenericException(e); return; } } else { List <GitHub.ReleaseResponse> releases; try { releases = JsonConvert.DeserializeObject <List <GitHub.ReleaseResponse> >(response); } catch (JsonException e) { Logging.LogGenericException(e); return; } if ((releases == null) || (releases.Count == 0)) { Logging.LogGenericWarning("Could not check latest version!"); return; } releaseResponse = releases[0]; } if (string.IsNullOrEmpty(releaseResponse.Tag)) { Logging.LogGenericWarning("Could not check latest version!"); return; } Version newVersion = new Version(releaseResponse.Tag); Logging.LogGenericInfo("Local version: " + Version + " | Remote version: " + newVersion); if (Version.CompareTo(newVersion) >= 0) // If local version is the same or newer than remote version { if ((AutoUpdatesTimer != null) || !GlobalConfig.AutoUpdates) { return; } Logging.LogGenericInfo("ASF will automatically check for new versions every 24 hours"); AutoUpdatesTimer = new Timer( async e => await CheckForUpdate().ConfigureAwait(false), null, TimeSpan.FromDays(1), // Delay TimeSpan.FromDays(1) // Period ); return; } if (!updateOverride && !GlobalConfig.AutoUpdates) { Logging.LogGenericInfo("New version is available!"); Logging.LogGenericInfo("Consider updating yourself!"); await Utilities.SleepAsync(5000).ConfigureAwait(false); return; } if (File.Exists(oldExeFile)) { Logging.LogGenericWarning("Refusing to proceed with auto update as old " + oldExeFile + " binary could not be removed, please remove it manually"); return; } // Auto update logic starts here if (releaseResponse.Assets == null) { Logging.LogGenericWarning("Could not proceed with update because that version doesn't include assets!"); return; } GitHub.ReleaseResponse.Asset binaryAsset = releaseResponse.Assets.FirstOrDefault(asset => !string.IsNullOrEmpty(asset.Name) && asset.Name.Equals(ExecutableName, StringComparison.OrdinalIgnoreCase)); if (binaryAsset == null) { Logging.LogGenericWarning("Could not proceed with update because there is no asset that relates to currently running binary!"); return; } if (string.IsNullOrEmpty(binaryAsset.DownloadURL)) { Logging.LogGenericWarning("Could not proceed with update because download URL is empty!"); return; } byte[] result = null; for (byte i = 0; (i < WebBrowser.MaxRetries) && (result == null); i++) { Logging.LogGenericInfo("Downloading new version..."); result = await WebBrowser.UrlGetToBytes(binaryAsset.DownloadURL).ConfigureAwait(false); } if (result == null) { Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries"); return; } string newExeFile = ExecutableFile + ".new"; // Firstly we create new exec try { File.WriteAllBytes(newExeFile, result); } catch (Exception e) { Logging.LogGenericException(e); return; } // Now we move current -> old try { File.Move(ExecutableFile, oldExeFile); } catch (Exception e) { Logging.LogGenericException(e); try { // Cleanup File.Delete(newExeFile); } catch { // Ignored } return; } // Now we move new -> current try { File.Move(newExeFile, ExecutableFile); } catch (Exception e) { Logging.LogGenericException(e); try { // Cleanup File.Move(oldExeFile, ExecutableFile); File.Delete(newExeFile); } catch { // Ignored } return; } Logging.LogGenericInfo("Update process finished!"); if (GlobalConfig.AutoRestart) { Logging.LogGenericInfo("Restarting..."); await Utilities.SleepAsync(5000).ConfigureAwait(false); Restart(); } else { Logging.LogGenericInfo("Exiting..."); await Utilities.SleepAsync(5000).ConfigureAwait(false); Exit(); } }
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); } Steam.TradeOfferRequest singleTrade = new Steam.TradeOfferRequest(); HashSet <Steam.TradeOfferRequest> trades = new HashSet <Steam.TradeOfferRequest> { singleTrade }; byte itemID = 0; foreach (Steam.Item item in inventory) { if (itemID >= Trading.MaxItemsPerTrade) { 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) { continue; } 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); } HashSet <Steam.Item> result = new HashSet <Steam.Item>(); ushort nextPage = 0; while (true) { string request = SteamCommunityURL + "/my/inventory/json/" + Steam.Item.SteamAppID + "/" + Steam.Item.SteamContextID + "?trading=1&start=" + nextPage; JObject jObject = null; for (byte i = 0; (i < WebBrowser.MaxRetries) && (jObject == null); i++) { jObject = await WebBrowser.UrlGetToJObject(request).ConfigureAwait(false); } if (jObject == null) { Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName); return(null); } IEnumerable <JToken> descriptions = jObject.SelectTokens("$.rgDescriptions.*"); if (descriptions == null) { return(null); } Dictionary <Tuple <ulong, ulong>, Tuple <uint, Steam.Item.EType> > descriptionMap = new Dictionary <Tuple <ulong, ulong>, Tuple <uint, Steam.Item.EType> >(); foreach (JToken description in descriptions) { string classIDString = description["classid"].ToString(); if (string.IsNullOrEmpty(classIDString)) { continue; } ulong classID; if (!ulong.TryParse(classIDString, out classID) || (classID == 0)) { continue; } string instanceIDString = description["instanceid"].ToString(); if (string.IsNullOrEmpty(instanceIDString)) { continue; } ulong instanceID; if (!ulong.TryParse(instanceIDString, out instanceID)) { continue; } Tuple <ulong, ulong> key = new Tuple <ulong, ulong>(classID, instanceID); if (descriptionMap.ContainsKey(key)) { continue; } uint appID = 0; Steam.Item.EType type = Steam.Item.EType.Unknown; string hashName = description["market_hash_name"].ToString(); if (!string.IsNullOrEmpty(hashName)) { appID = GetAppIDFromMarketHashName(hashName); } string descriptionType = description["type"].ToString(); if (!string.IsNullOrEmpty(descriptionType)) { type = GetItemType(descriptionType); } descriptionMap[key] = new Tuple <uint, Steam.Item.EType>(appID, type); } IEnumerable <JToken> items = jObject.SelectTokens("$.rgInventory.*"); if (items == null) { return(null); } foreach (JToken item in items) { Steam.Item steamItem; try { steamItem = JsonConvert.DeserializeObject <Steam.Item>(item.ToString()); } catch (JsonException e) { Logging.LogGenericException(e, Bot.BotName); continue; } if (steamItem == null) { continue; } Tuple <ulong, ulong> key = new Tuple <ulong, ulong>(steamItem.ClassID, steamItem.InstanceID); Tuple <uint, Steam.Item.EType> description; if (descriptionMap.TryGetValue(key, out description)) { steamItem.RealAppID = description.Item1; steamItem.Type = description.Item2; } result.Add(steamItem); } bool more; if (!bool.TryParse(jObject["more"].ToString(), out more) || !more) { break; } if (!ushort.TryParse(jObject["more_start"].ToString(), out nextPage)) { break; } } return(result); }
internal List <SteamTradeOffer> GetTradeOffers() { if (ApiKey == null) { return(null); } KeyValue response = null; using (dynamic iEconService = WebAPI.GetInterface("IEconService", ApiKey)) { iEconService.Timeout = Timeout; for (byte i = 0; i < WebBrowser.MaxRetries && response == null; i++) { try { response = iEconService.GetTradeOffers( get_received_offers: 1, active_only: 1, secure: true ); } catch (Exception e) { Logging.LogGenericException(e, Bot.BotName); } } } if (response == null) { Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName); return(null); } List <SteamTradeOffer> result = new List <SteamTradeOffer>(); foreach (KeyValue trade in response["trade_offers_received"].Children) { SteamTradeOffer tradeOffer = new SteamTradeOffer { tradeofferid = trade["tradeofferid"].AsString(), accountid_other = trade["accountid_other"].AsInteger(), trade_offer_state = trade["trade_offer_state"].AsEnum <SteamTradeOffer.ETradeOfferState>() }; foreach (KeyValue item in trade["items_to_give"].Children) { tradeOffer.items_to_give.Add(new SteamItem { appid = item["appid"].AsString(), contextid = item["contextid"].AsString(), assetid = item["assetid"].AsString(), classid = item["classid"].AsString(), instanceid = item["instanceid"].AsString(), amount = item["amount"].AsString(), }); } foreach (KeyValue item in trade["items_to_receive"].Children) { tradeOffer.items_to_receive.Add(new SteamItem { appid = item["appid"].AsString(), contextid = item["contextid"].AsString(), assetid = item["assetid"].AsString(), classid = item["classid"].AsString(), instanceid = item["instanceid"].AsString(), amount = item["amount"].AsString(), }); } result.Add(tradeOffer); } return(result); }