internal async Task <HashSet <Steam.Item> > GetMySteamInventory(bool tradable)
        {
            if (!await RefreshSessionIfNeeded().ConfigureAwait(false))
            {
                return(null);
            }

            HashSet <Steam.Item> result = new HashSet <Steam.Item>();

            string request     = SteamCommunityURL + "/my/inventory/json/" + Steam.Item.SteamAppID + "/" + Steam.Item.SteamContextID + "?l=english&trading=" + (tradable ? "1" : "0") + "&start=";
            uint   currentPage = 0;

            while (true)
            {
                JObject jObject = await WebBrowser.UrlGetToJObjectRetry(request + currentPage).ConfigureAwait(false);

                IEnumerable <JToken> descriptions = jObject?.SelectTokens("$.rgDescriptions.*");
                if (descriptions == null)
                {
                    return(null);                    // OK, empty inventory
                }

                Dictionary <ulong, Tuple <uint, Steam.Item.EType> > descriptionMap = new Dictionary <ulong, Tuple <uint, Steam.Item.EType> >();
                foreach (JToken description in descriptions.Where(description => description != null))
                {
                    string classIDString = description["classid"].ToString();
                    if (string.IsNullOrEmpty(classIDString))
                    {
                        Logging.LogNullError(nameof(classIDString), Bot.BotName);
                        return(null);
                    }

                    ulong classID;
                    if (!ulong.TryParse(classIDString, out classID) || (classID == 0))
                    {
                        Logging.LogNullError(nameof(classID), Bot.BotName);
                        return(null);
                    }

                    if (descriptionMap.ContainsKey(classID))
                    {
                        continue;
                    }

                    uint appID = 0;

                    string hashName = description["market_hash_name"].ToString();
                    if (!string.IsNullOrEmpty(hashName))
                    {
                        appID = GetAppIDFromMarketHashName(hashName);
                    }

                    if (appID == 0)
                    {
                        string appIDString = description["appid"].ToString();
                        if (string.IsNullOrEmpty(appIDString))
                        {
                            Logging.LogNullError(nameof(appIDString), Bot.BotName);
                            return(null);
                        }

                        if (!uint.TryParse(appIDString, out appID))
                        {
                            Logging.LogNullError(nameof(appID), Bot.BotName);
                            return(null);
                        }
                    }

                    Steam.Item.EType type = Steam.Item.EType.Unknown;

                    string descriptionType = description["type"].ToString();
                    if (!string.IsNullOrEmpty(descriptionType))
                    {
                        type = GetItemType(descriptionType);
                    }

                    descriptionMap[classID] = new Tuple <uint, Steam.Item.EType>(appID, type);
                }

                IEnumerable <JToken> items = jObject.SelectTokens("$.rgInventory.*");
                if (items == null)
                {
                    Logging.LogNullError(nameof(items), Bot.BotName);
                    return(null);
                }

                foreach (JToken item in items.Where(item => item != null))
                {
                    Steam.Item steamItem;

                    try {
                        steamItem = item.ToObject <Steam.Item>();
                    } catch (JsonException e) {
                        Logging.LogGenericException(e, Bot.BotName);
                        return(null);
                    }

                    if (steamItem == null)
                    {
                        Logging.LogNullError(nameof(steamItem), Bot.BotName);
                        return(null);
                    }

                    Tuple <uint, Steam.Item.EType> description;
                    if (descriptionMap.TryGetValue(steamItem.ClassID, 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;                     // OK, last page
                }

                uint nextPage;
                if (!uint.TryParse(jObject["more_start"].ToString(), out nextPage) || (nextPage <= currentPage))
                {
                    Logging.LogNullError(nameof(nextPage), Bot.BotName);
                    return(null);
                }

                currentPage = nextPage;
            }

            return(result);
        }