Пример #1
0
        private async Task ParseTrade(Steam.TradeOffer tradeOffer)
        {
            if (tradeOffer == null)
            {
                Logging.LogNullError(nameof(tradeOffer), Bot.BotName);
                return;
            }

            if (tradeOffer.State != Steam.TradeOffer.ETradeOfferState.Active)
            {
                return;
            }

            if (await ShouldAcceptTrade(tradeOffer).ConfigureAwait(false))
            {
                Logging.LogGenericInfo("Accepting trade: " + tradeOffer.TradeOfferID, Bot.BotName);
                await Bot.ArchiWebHandler.AcceptTradeOffer(tradeOffer.TradeOfferID).ConfigureAwait(false);
            }
            else if (Bot.BotConfig.IsBotAccount)
            {
                Logging.LogGenericInfo("Rejecting trade: " + tradeOffer.TradeOfferID, Bot.BotName);
                Bot.ArchiWebHandler.DeclineTradeOffer(tradeOffer.TradeOfferID);
            }
            else
            {
                Logging.LogGenericInfo("Ignoring trade: " + tradeOffer.TradeOfferID, Bot.BotName);
            }
        }
Пример #2
0
        private async Task <ParseTradeResult> ParseTrade(Steam.TradeOffer tradeOffer)
        {
            if (tradeOffer == null)
            {
                Bot.ArchiLogger.LogNullError(nameof(tradeOffer));
                return(null);
            }

            if (tradeOffer.State != Steam.TradeOffer.ETradeOfferState.Active)
            {
                Bot.ArchiLogger.LogGenericError(string.Format(Strings.ErrorIsInvalid, tradeOffer.State));
                return(null);
            }

            ParseTradeResult result = await ShouldAcceptTrade(tradeOffer).ConfigureAwait(false);

            if (result == null)
            {
                Bot.ArchiLogger.LogNullError(nameof(result));
                return(null);
            }

            switch (result.Result)
            {
            case ParseTradeResult.EResult.AcceptedWithItemLose:
            case ParseTradeResult.EResult.AcceptedWithoutItemLose:
                Bot.ArchiLogger.LogGenericInfo(string.Format(Strings.AcceptingTrade, tradeOffer.TradeOfferID));
                await Bot.ArchiWebHandler.AcceptTradeOffer(tradeOffer.TradeOfferID).ConfigureAwait(false);

                break;

            case ParseTradeResult.EResult.RejectedPermanently:
            case ParseTradeResult.EResult.RejectedTemporarily:
                if (result.Result == ParseTradeResult.EResult.RejectedPermanently)
                {
                    if (Bot.BotConfig.IsBotAccount)
                    {
                        Bot.ArchiLogger.LogGenericInfo(string.Format(Strings.RejectingTrade, tradeOffer.TradeOfferID));
                        await Bot.ArchiWebHandler.DeclineTradeOffer(tradeOffer.TradeOfferID).ConfigureAwait(false);

                        break;
                    }

                    IgnoredTrades.Add(tradeOffer.TradeOfferID);
                }

                Bot.ArchiLogger.LogGenericInfo(string.Format(Strings.IgnoringTrade, tradeOffer.TradeOfferID));
                break;
            }

            return(result);
        }
Пример #3
0
        private async Task <ParseTradeResult> ParseTrade(Steam.TradeOffer tradeOffer)
        {
            if (tradeOffer == null)
            {
                Logging.LogNullError(nameof(tradeOffer), Bot.BotName);
                return(null);
            }

            if (tradeOffer.State != Steam.TradeOffer.ETradeOfferState.Active)
            {
                Logging.LogGenericError("Ignoring trade in non-active state!", Bot.BotName);
                return(null);
            }

            ParseTradeResult result = await ShouldAcceptTrade(tradeOffer).ConfigureAwait(false);

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

            switch (result.Result)
            {
            case ParseTradeResult.EResult.AcceptedWithItemLose:
            case ParseTradeResult.EResult.AcceptedWithoutItemLose:
                Logging.LogGenericInfo("Accepting trade: " + tradeOffer.TradeOfferID, Bot.BotName);
                await Bot.ArchiWebHandler.AcceptTradeOffer(tradeOffer.TradeOfferID).ConfigureAwait(false);

                break;

            case ParseTradeResult.EResult.RejectedPermanently:
            case ParseTradeResult.EResult.RejectedTemporarily:
                if (result.Result == ParseTradeResult.EResult.RejectedPermanently)
                {
                    if (Bot.BotConfig.IsBotAccount)
                    {
                        Logging.LogGenericInfo("Rejecting trade: " + tradeOffer.TradeOfferID, Bot.BotName);
                        Bot.ArchiWebHandler.DeclineTradeOffer(tradeOffer.TradeOfferID);
                        break;
                    }

                    IgnoredTrades.Add(tradeOffer.TradeOfferID);
                }

                Logging.LogGenericInfo("Ignoring trade: " + tradeOffer.TradeOfferID, Bot.BotName);
                break;
            }

            return(result);
        }
Пример #4
0
        private async Task ParseTrade(Steam.TradeOffer tradeOffer)
        {
            if ((tradeOffer == null) || (tradeOffer.State != Steam.TradeOffer.ETradeOfferState.Active))
            {
                return;
            }

            if (await ShouldAcceptTrade(tradeOffer).ConfigureAwait(false))
            {
                Logging.LogGenericInfo("Accepting trade: " + tradeOffer.TradeOfferID, Bot.BotName);
                await Bot.ArchiWebHandler.AcceptTradeOffer(tradeOffer.TradeOfferID).ConfigureAwait(false);
            }
            else
            {
                Logging.LogGenericInfo("Ignoring trade: " + tradeOffer.TradeOfferID, Bot.BotName);
            }
        }
Пример #5
0
        private bool ShouldAcceptTrade(Steam.TradeOffer tradeOffer)
        {
            if (tradeOffer == null)
            {
                return(false);
            }

            // Always accept trades when we're not losing anything
            if (tradeOffer.items_to_give.Count == 0)
            {
                return(true);
            }

            // Always accept trades from SteamMasterID
            if (tradeOffer.OtherSteamID64 != 0 && tradeOffer.OtherSteamID64 == Bot.BotConfig.SteamMasterID)
            {
                return(true);
            }

            // TODO: Add optional SteamTradeMatcher integration here

            // If no rule above matched this trade, reject it
            return(false);
        }
Пример #6
0
        private async Task ParseTrade(Steam.TradeOffer tradeOffer)
        {
            if (tradeOffer == null || tradeOffer.trade_offer_state != Steam.TradeOffer.ETradeOfferState.Active)
            {
                return;
            }

            ulong tradeID;

            if (!ulong.TryParse(tradeOffer.tradeofferid, out tradeID))
            {
                return;
            }

            if (ShouldAcceptTrade(tradeOffer))
            {
                Logging.LogGenericInfo("Accepting trade: " + tradeID, Bot.BotName);
                await Bot.ArchiWebHandler.AcceptTradeOffer(tradeID).ConfigureAwait(false);
            }
            else
            {
                Logging.LogGenericInfo("Ignoring trade: " + tradeID, Bot.BotName);
            }
        }
Пример #7
0
        private async Task ParseTrade(Steam.TradeOffer tradeOffer)
        {
            if (tradeOffer == null)
            {
                return;
            }

            ulong tradeID;

            if (!ulong.TryParse(tradeOffer.tradeofferid, out tradeID))
            {
                return;
            }

            if (tradeOffer.items_to_give.Count == 0 || tradeOffer.OtherSteamID64 == Bot.BotConfig.SteamMasterID)
            {
                Logging.LogGenericInfo("Accepting trade: " + tradeID, Bot.BotName);
                await Bot.ArchiWebHandler.AcceptTradeOffer(tradeID).ConfigureAwait(false);
            }
            else
            {
                Logging.LogGenericInfo("Ignoring trade: " + tradeID, Bot.BotName);
            }
        }
Пример #8
0
        private async Task <bool> MatchActivelyRound()
        {
            // TODO: This function has a lot of debug leftovers for logic testing, once that period is over, get rid of them
            if (!Bot.IsConnectedAndLoggedOn || Bot.BotConfig.TradingPreferences.HasFlag(BotConfig.ETradingPreferences.MatchEverything) || !Bot.BotConfig.TradingPreferences.HasFlag(BotConfig.ETradingPreferences.MatchActively) || !await IsEligibleForMatching().ConfigureAwait(false))
            {
                Bot.ArchiLogger.LogGenericDebug("User not eligible for this function, returning");
                return(false);
            }

            HashSet <Steam.Asset.EType> acceptedMatchableTypes = Bot.BotConfig.MatchableTypes.Where(type => AcceptedMatchableTypes.Contains(type)).ToHashSet();

            if (acceptedMatchableTypes.Count == 0)
            {
                Bot.ArchiLogger.LogGenericDebug("No acceptable matchable types, returning");
                return(false);
            }

            HashSet <Steam.Asset> ourInventory = await Bot.ArchiWebHandler.GetInventory(Bot.SteamID, tradable : true, wantedTypes : acceptedMatchableTypes).ConfigureAwait(false);

            if ((ourInventory == null) || (ourInventory.Count == 0))
            {
                Bot.ArchiLogger.LogGenericDebug("Empty inventory, returning");
                return(false);
            }

            Dictionary <(uint AppID, Steam.Asset.EType Type), Dictionary <ulong, uint> > ourInventoryState = Trading.GetInventoryState(ourInventory);

            if (ourInventoryState.Values.All(set => set.Values.All(amount => amount <= 1)))
            {
                // User doesn't have any more dupes in the inventory
                Bot.ArchiLogger.LogGenericDebug("No dupes in inventory, returning");
                return(false);
            }

            HashSet <ListedUser> listedUsers = await GetListedUsers().ConfigureAwait(false);

            if ((listedUsers == null) || (listedUsers.Count == 0))
            {
                Bot.ArchiLogger.LogGenericDebug("No listed users, returning");
                return(false);
            }

            byte emptyMatches = 0;
            HashSet <(uint AppID, Steam.Asset.EType Type)> skippedSets = new HashSet <(uint AppID, Steam.Asset.EType Type)>();

            foreach (ListedUser listedUser in listedUsers.Where(listedUser => listedUser.MatchEverything && !Bot.IsBlacklistedFromTrades(listedUser.SteamID)).OrderByDescending(listedUser => listedUser.Score).Take(MaxMatchedBotsHard))
            {
                Bot.ArchiLogger.LogGenericDebug("Now matching " + listedUser.SteamID + "...");

                HashSet <Steam.Asset> theirInventory = await Bot.ArchiWebHandler.GetInventory(listedUser.SteamID, tradable : true, wantedTypes : acceptedMatchableTypes, skippedSets : skippedSets).ConfigureAwait(false);

                if ((theirInventory == null) || (theirInventory.Count == 0))
                {
                    Bot.ArchiLogger.LogGenericDebug("Inventory of " + listedUser.SteamID + " is empty, continuing...");
                    continue;
                }

                Dictionary <(uint AppID, Steam.Asset.EType Type), Dictionary <ulong, uint> > theirInventoryState = Trading.GetInventoryState(theirInventory);

                Dictionary <ulong, uint> classIDsToGive    = new Dictionary <ulong, uint>();
                Dictionary <ulong, uint> classIDsToReceive = new Dictionary <ulong, uint>();
                HashSet <(uint AppID, Steam.Asset.EType Type)> skippedSetsThisTrade = new HashSet <(uint AppID, Steam.Asset.EType Type)>();

                foreach (KeyValuePair <(uint AppID, Steam.Asset.EType Type), Dictionary <ulong, uint> > ourInventoryStateSet in ourInventoryState.Where(set => listedUser.MatchableTypes.Contains(set.Key.Type) && set.Value.Values.Any(count => count > 1)))
                {
                    if (!theirInventoryState.TryGetValue(ourInventoryStateSet.Key, out Dictionary <ulong, uint> theirItems))
                    {
                        continue;
                    }

                    bool match;

                    do
                    {
                        match = false;

                        foreach (KeyValuePair <ulong, uint> ourItem in ourInventoryStateSet.Value.Where(item => item.Value > 1).OrderByDescending(item => item.Value))
                        {
                            foreach (KeyValuePair <ulong, uint> theirItem in theirItems.OrderBy(item => ourInventoryStateSet.Value.TryGetValue(item.Key, out uint ourAmount) ? ourAmount : 0))
                            {
                                if (ourInventoryStateSet.Value.TryGetValue(theirItem.Key, out uint ourAmountOfTheirItem) && (ourItem.Value <= ourAmountOfTheirItem + 1))
                                {
                                    continue;
                                }

                                Bot.ArchiLogger.LogGenericDebug("Found a match: our " + ourItem.Key + " for theirs " + theirItem.Key);

                                // Skip this set from the remaining of this round
                                skippedSetsThisTrade.Add(ourInventoryStateSet.Key);

                                // Update our state based on given items
                                classIDsToGive[ourItem.Key]             = classIDsToGive.TryGetValue(ourItem.Key, out uint givenAmount) ? givenAmount + 1 : 1;
                                ourInventoryStateSet.Value[ourItem.Key] = ourItem.Value - 1;

                                // Update our state based on received items
                                classIDsToReceive[theirItem.Key]          = classIDsToReceive.TryGetValue(theirItem.Key, out uint receivedAmount) ? receivedAmount + 1 : 1;
                                ourInventoryStateSet.Value[theirItem.Key] = ourAmountOfTheirItem + 1;

                                // Update their state based on taken items
                                if (theirItems.TryGetValue(theirItem.Key, out uint theirAmount) && (theirAmount > 1))
                                {
                                    theirItems[theirItem.Key] = theirAmount - 1;
                                }
                                else
                                {
                                    theirItems.Remove(theirItem.Key);
                                }

                                match = true;
                                break;
                            }

                            if (match)
                            {
                                break;
                            }
                        }
                    } while (match);
                }

                if ((classIDsToGive.Count == 0) && (classIDsToReceive.Count == 0))
                {
                    Bot.ArchiLogger.LogGenericDebug("No matches found, continuing...");

                    if (++emptyMatches >= MaxMatchesBotsSoft)
                    {
                        break;
                    }

                    continue;
                }

                emptyMatches = 0;

                HashSet <Steam.Asset> itemsToGive    = Trading.GetItemsFromInventory(ourInventory, classIDsToGive);
                HashSet <Steam.Asset> itemsToReceive = Trading.GetItemsFromInventory(theirInventory, classIDsToReceive);

                // TODO: Debug only offer, should be removed after tests
                Steam.TradeOffer debugOffer = new Steam.TradeOffer(1, 46697991, Steam.TradeOffer.ETradeOfferState.Active);

                foreach (Steam.Asset itemToGive in itemsToGive)
                {
                    debugOffer.ItemsToGive.Add(itemToGive);
                }

                foreach (Steam.Asset itemToReceive in itemsToReceive)
                {
                    debugOffer.ItemsToReceive.Add(itemToReceive);
                }

                if (!debugOffer.IsFairTypesExchange())
                {
                    Bot.ArchiLogger.LogGenericDebug("CRITICAL: This offer is NOT fair!!!");
                    return(false);
                }

                Bot.ArchiLogger.LogGenericDebug("Sending trade: our " + string.Join(", ", itemsToGive.Select(item => item.RealAppID + "/" + item.Type + " " + item.ClassID + " of " + item.Amount)));

                (bool success, HashSet <ulong> mobileTradeOfferIDs) = await Bot.ArchiWebHandler.SendTradeOffer(listedUser.SteamID, itemsToGive, itemsToReceive, listedUser.TradeToken, true).ConfigureAwait(false);

                if ((mobileTradeOfferIDs != null) && (mobileTradeOfferIDs.Count > 0) && Bot.HasMobileAuthenticator)
                {
                    if (!await Bot.Actions.AcceptConfirmations(true, Steam.ConfirmationDetails.EType.Trade, listedUser.SteamID, mobileTradeOfferIDs, true).ConfigureAwait(false))
                    {
                        return(false);
                    }
                }

                if (!success)
                {
                    Bot.ArchiLogger.LogGenericDebug("Trade failed (?), continuing...");
                    continue;
                }

                Bot.ArchiLogger.LogGenericDebug("Trade succeeded!");

                foreach ((uint AppID, Steam.Asset.EType Type)skippedSetThisTrade in skippedSetsThisTrade)
                {
                    ourInventoryState.Remove(skippedSetThisTrade);
                }

                if (ourInventoryState.Values.All(set => set.Values.All(amount => amount <= 1)))
                {
                    // User doesn't have any more dupes in the inventory
                    Bot.ArchiLogger.LogGenericDebug("No dupes in inventory, returning");
                    break;
                }

                skippedSets.UnionWith(skippedSetsThisTrade);
            }

            Bot.ArchiLogger.LogGenericDebug("This round is over, we traded " + skippedSets.Count + " sets!");
            return(skippedSets.Count > 0);
        }
        internal HashSet <Steam.TradeOffer> GetActiveTradeOffers()
        {
            if (string.IsNullOrEmpty(Bot.BotConfig.SteamApiKey))
            {
                Logging.LogNullError(nameof(Bot.BotConfig.SteamApiKey), Bot.BotName);
                return(null);
            }

            KeyValue response = null;

            for (byte i = 0; (i < WebBrowser.MaxRetries) && (response == null); i++)
            {
                using (dynamic iEconService = WebAPI.GetInterface("IEconService", Bot.BotConfig.SteamApiKey)) {
                    iEconService.Timeout = Timeout;

                    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.LogGenericWarning("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);
                    return(null);
                }

                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 = description["appid"].AsUnsignedInteger();
                }

                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.ETradeOfferState state = trade["trade_offer_state"].AsEnum <Steam.TradeOffer.ETradeOfferState>();
                if (state == Steam.TradeOffer.ETradeOfferState.Unknown)
                {
                    Logging.LogNullError(nameof(state));
                    return(null);
                }

                if (state != Steam.TradeOffer.ETradeOfferState.Active)
                {
                    continue;
                }

                ulong tradeOfferID = trade["tradeofferid"].AsUnsignedLong();
                if (tradeOfferID == 0)
                {
                    Logging.LogNullError(nameof(tradeOfferID));
                    return(null);
                }

                uint otherSteamID3 = trade["accountid_other"].AsUnsignedInteger();
                if (otherSteamID3 == 0)
                {
                    Logging.LogNullError(nameof(otherSteamID3));
                    return(null);
                }

                Steam.TradeOffer tradeOffer = new Steam.TradeOffer(tradeOfferID, otherSteamID3, state);

                List <KeyValue> itemsToGive = trade["items_to_give"].Children;
                if (itemsToGive.Count > 0)
                {
                    if (!ParseItems(descriptions, itemsToGive, tradeOffer.ItemsToGive))
                    {
                        Logging.LogGenericError("Parsing " + nameof(itemsToGive) + " failed!", Bot.BotName);
                        return(null);
                    }
                }

                List <KeyValue> itemsToReceive = trade["items_to_receive"].Children;
                if (itemsToReceive.Count > 0)
                {
                    if (!ParseItems(descriptions, itemsToReceive, tradeOffer.ItemsToReceive))
                    {
                        Logging.LogGenericError("Parsing " + nameof(itemsToReceive) + " failed!", Bot.BotName);
                        return(null);
                    }
                }

                result.Add(tradeOffer);
            }

            return(result);
        }
Пример #10
0
        private async Task <ParseTradeResult> ShouldAcceptTrade(Steam.TradeOffer tradeOffer)
        {
            if (tradeOffer == null)
            {
                Logging.LogNullError(nameof(tradeOffer), Bot.BotName);
                return(ParseTradeResult.Error);
            }

            // Always accept trades when we're not losing anything
            if (tradeOffer.ItemsToGive.Count == 0)
            {
                // Unless it's steam fuckup and we're dealing with broken trade
                return(tradeOffer.ItemsToReceive.Count > 0 ? ParseTradeResult.AcceptedWithoutItemLose : ParseTradeResult.RejectedTemporarily);
            }

            // Always accept trades from SteamMasterID
            if ((tradeOffer.OtherSteamID64 != 0) && (tradeOffer.OtherSteamID64 == Bot.BotConfig.SteamMasterID))
            {
                return(ParseTradeResult.AcceptedWithItemLose);
            }

            // If we don't have SteamTradeMatcher enabled, this is the end for us
            if (!Bot.BotConfig.SteamTradeMatcher)
            {
                return(ParseTradeResult.RejectedPermanently);
            }

            // Decline trade if we're giving more count-wise
            if (tradeOffer.ItemsToGive.Count > tradeOffer.ItemsToReceive.Count)
            {
                return(ParseTradeResult.RejectedPermanently);
            }

            // Decline trade if we're losing anything but steam cards, or if it's non-dupes trade
            if (!tradeOffer.IsSteamCardsOnlyTradeForUs() || !tradeOffer.IsPotentiallyDupesTradeForUs())
            {
                return(ParseTradeResult.RejectedPermanently);
            }

            // At this point we're sure that STM trade is valid

            // Fetch trade hold duration
            byte?holdDuration = await Bot.ArchiWebHandler.GetTradeHoldDuration(tradeOffer.TradeOfferID).ConfigureAwait(false);

            if (!holdDuration.HasValue)
            {
                // If we can't get trade hold duration, reject trade temporarily
                return(ParseTradeResult.RejectedTemporarily);
            }

            // If user has a trade hold, we add extra logic
            if (holdDuration.Value > 0)
            {
                // If trade hold duration exceeds our max, or user asks for cards with short lifespan, reject the trade
                if ((holdDuration.Value > Program.GlobalConfig.MaxTradeHoldDuration) || tradeOffer.ItemsToGive.Any(item => GlobalConfig.GlobalBlacklist.Contains(item.RealAppID)))
                {
                    return(ParseTradeResult.RejectedPermanently);
                }
            }

            // Now check if it's worth for us to do the trade
            await LimitInventoryRequestsAsync().ConfigureAwait(false);

            HashSet <Steam.Item> inventory = await Bot.ArchiWebHandler.GetMyInventory(false).ConfigureAwait(false);

            if ((inventory == null) || (inventory.Count == 0))
            {
                return(ParseTradeResult.AcceptedWithItemLose);                // OK, assume that this trade is valid, we can't check our EQ
            }

            // Get appIDs we're interested in
            HashSet <uint> appIDs = new HashSet <uint>(tradeOffer.ItemsToGive.Select(item => item.RealAppID));

            // Now remove from our inventory all items we're NOT interested in
            inventory.RemoveWhere(item => !appIDs.Contains(item.RealAppID));
            inventory.TrimExcess();

            // If for some reason Valve is talking crap and we can't find mentioned items, assume OK
            if (inventory.Count == 0)
            {
                return(ParseTradeResult.AcceptedWithItemLose);
            }

            // Now let's create a map which maps items to their amount in our EQ
            Dictionary <ulong, uint> amountMap = new Dictionary <ulong, uint>();

            foreach (Steam.Item item in inventory)
            {
                uint amount;
                if (amountMap.TryGetValue(item.ClassID, out amount))
                {
                    amountMap[item.ClassID] = amount + item.Amount;
                }
                else
                {
                    amountMap[item.ClassID] = item.Amount;
                }
            }

            // Calculate our value of items to give
            List <uint> amountsToGive = new List <uint>(tradeOffer.ItemsToGive.Count);
            Dictionary <ulong, uint> amountMapToGive = new Dictionary <ulong, uint>(amountMap);

            foreach (ulong key in tradeOffer.ItemsToGive.Select(item => item.ClassID))
            {
                uint amount;
                if (!amountMapToGive.TryGetValue(key, out amount))
                {
                    amountsToGive.Add(0);
                    continue;
                }

                amountsToGive.Add(amount);
                amountMapToGive[key] = amount - 1;                 // We're giving one, so we have one less
            }

            // Sort it ascending
            amountsToGive.Sort();

            // Calculate our value of items to receive
            List <uint> amountsToReceive = new List <uint>(tradeOffer.ItemsToReceive.Count);
            Dictionary <ulong, uint> amountMapToReceive = new Dictionary <ulong, uint>(amountMap);

            foreach (ulong key in tradeOffer.ItemsToReceive.Select(item => item.ClassID))
            {
                uint amount;
                if (!amountMapToReceive.TryGetValue(key, out amount))
                {
                    amountsToReceive.Add(0);
                    continue;
                }

                amountsToReceive.Add(amount);
                amountMapToReceive[key] = amount + 1;                 // We're getting one, so we have one more
            }

            // Sort it ascending
            amountsToReceive.Sort();

            // Check actual difference
            // We sum only values at proper indexes of giving, because user might be overpaying
            int difference = amountsToGive.Select((t, i) => (int)(t - amountsToReceive[i])).Sum();

            // Trade is worth for us if the difference is greater than 0
            return(difference > 0 ? ParseTradeResult.AcceptedWithItemLose : ParseTradeResult.RejectedTemporarily);
        }
Пример #11
0
        private async Task <ParseTradeResult> ShouldAcceptTrade(Steam.TradeOffer tradeOffer)
        {
            if (tradeOffer == null)
            {
                Bot.ArchiLogger.LogNullError(nameof(tradeOffer));
                return(null);
            }

            // Always accept trades from SteamMasterID
            if ((tradeOffer.OtherSteamID64 != 0) && ((tradeOffer.OtherSteamID64 == Bot.BotConfig.SteamMasterID) || (tradeOffer.OtherSteamID64 == Program.GlobalConfig.SteamOwnerID)))
            {
                return(new ParseTradeResult(tradeOffer.TradeOfferID, tradeOffer.ItemsToGive.Count > 0 ? ParseTradeResult.EResult.AcceptedWithItemLose : ParseTradeResult.EResult.AcceptedWithoutItemLose));
            }

            // Check if it's donation trade
            if (tradeOffer.ItemsToGive.Count == 0)
            {
                ParseTradeResult.EResult donationResult;

                // If it's steam fuckup, temporarily ignore it, otherwise react accordingly, depending on our preference
                if (tradeOffer.ItemsToReceive.Count == 0)
                {
                    donationResult = ParseTradeResult.EResult.RejectedTemporarily;
                }
                else if (Bot.BotConfig.TradingPreferences.HasFlag(BotConfig.ETradingPreferences.AcceptDonations) || ((tradeOffer.OtherSteamID64 != 0) && Bot.Bots.Values.Any(bot => bot.SteamID == tradeOffer.OtherSteamID64)))
                {
                    donationResult = ParseTradeResult.EResult.AcceptedWithoutItemLose;
                }
                else
                {
                    donationResult = ParseTradeResult.EResult.RejectedPermanently;
                }

                return(new ParseTradeResult(tradeOffer.TradeOfferID, donationResult));
            }

            // If we don't have SteamTradeMatcher enabled, this is the end for us
            if (!Bot.BotConfig.TradingPreferences.HasFlag(BotConfig.ETradingPreferences.SteamTradeMatcher))
            {
                return(new ParseTradeResult(tradeOffer.TradeOfferID, ParseTradeResult.EResult.RejectedPermanently));
            }

            // Decline trade if we're giving more count-wise
            if (tradeOffer.ItemsToGive.Count > tradeOffer.ItemsToReceive.Count)
            {
                return(new ParseTradeResult(tradeOffer.TradeOfferID, ParseTradeResult.EResult.RejectedPermanently));
            }

            // Decline trade if we're losing anything but steam cards, or if it's non-dupes trade
            if (!tradeOffer.IsSteamCardsRequest() || !tradeOffer.IsFairTypesExchange())
            {
                return(new ParseTradeResult(tradeOffer.TradeOfferID, ParseTradeResult.EResult.RejectedPermanently));
            }

            // At this point we're sure that STM trade is valid

            // Fetch trade hold duration
            byte?holdDuration = await Bot.ArchiWebHandler.GetTradeHoldDuration(tradeOffer.TradeOfferID).ConfigureAwait(false);

            if (!holdDuration.HasValue)
            {
                // If we can't get trade hold duration, reject trade temporarily
                return(new ParseTradeResult(tradeOffer.TradeOfferID, ParseTradeResult.EResult.RejectedTemporarily));
            }

            // If user has a trade hold, we add extra logic
            if (holdDuration.Value > 0)
            {
                // If trade hold duration exceeds our max, or user asks for cards with short lifespan, reject the trade
                if ((holdDuration.Value > Program.GlobalConfig.MaxTradeHoldDuration) || tradeOffer.ItemsToGive.Any(item => GlobalConfig.GlobalBlacklist.Contains(item.RealAppID)))
                {
                    return(new ParseTradeResult(tradeOffer.TradeOfferID, ParseTradeResult.EResult.RejectedPermanently));
                }
            }

            // If we're matching everything, this is enough for us
            if (Bot.BotConfig.TradingPreferences.HasFlag(BotConfig.ETradingPreferences.MatchEverything))
            {
                return(new ParseTradeResult(tradeOffer.TradeOfferID, ParseTradeResult.EResult.AcceptedWithItemLose));
            }

            // Now check if it's worth for us to do the trade
            await LimitInventoryRequestsAsync().ConfigureAwait(false);

            HashSet <Steam.Item> inventory = await Bot.ArchiWebHandler.GetMySteamInventory(false, new HashSet <Steam.Item.EType> {
                Steam.Item.EType.TradingCard
            }).ConfigureAwait(false);

            if ((inventory == null) || (inventory.Count == 0))
            {
                return(new ParseTradeResult(tradeOffer.TradeOfferID, ParseTradeResult.EResult.AcceptedWithItemLose));                // OK, assume that this trade is valid, we can't check our EQ
            }

            // Get appIDs we're interested in
            HashSet <uint> appIDs = new HashSet <uint>(tradeOffer.ItemsToGive.Select(item => item.RealAppID));

            // Now remove from our inventory all items we're NOT interested in
            inventory.RemoveWhere(item => !appIDs.Contains(item.RealAppID));

            // If for some reason Valve is talking crap and we can't find mentioned items, assume OK
            if (inventory.Count == 0)
            {
                return(new ParseTradeResult(tradeOffer.TradeOfferID, ParseTradeResult.EResult.AcceptedWithItemLose));
            }

            // Now let's create a map which maps items to their amount in our EQ
            Dictionary <ulong, uint> amountMap = new Dictionary <ulong, uint>();

            foreach (Steam.Item item in inventory)
            {
                uint amount;
                if (amountMap.TryGetValue(item.ClassID, out amount))
                {
                    amountMap[item.ClassID] = amount + item.Amount;
                }
                else
                {
                    amountMap[item.ClassID] = item.Amount;
                }
            }

            // Calculate our value of items to give
            List <uint> amountsToGive = new List <uint>(tradeOffer.ItemsToGive.Count);
            Dictionary <ulong, uint> amountMapToGive = new Dictionary <ulong, uint>(amountMap);

            foreach (ulong key in tradeOffer.ItemsToGive.Select(item => item.ClassID))
            {
                uint amount;
                if (!amountMapToGive.TryGetValue(key, out amount))
                {
                    amountsToGive.Add(0);
                    continue;
                }

                amountsToGive.Add(amount);
                amountMapToGive[key] = amount - 1;                 // We're giving one, so we have one less
            }

            // Sort it ascending
            amountsToGive.Sort();

            // Calculate our value of items to receive
            List <uint> amountsToReceive = new List <uint>(tradeOffer.ItemsToReceive.Count);
            Dictionary <ulong, uint> amountMapToReceive = new Dictionary <ulong, uint>(amountMap);

            foreach (ulong key in tradeOffer.ItemsToReceive.Select(item => item.ClassID))
            {
                uint amount;
                if (!amountMapToReceive.TryGetValue(key, out amount))
                {
                    amountsToReceive.Add(0);
                    continue;
                }

                amountsToReceive.Add(amount);
                amountMapToReceive[key] = amount + 1;                 // We're getting one, so we have one more
            }

            // Sort it ascending
            amountsToReceive.Sort();

            // Check actual difference
            // We sum only values at proper indexes of giving, because user might be overpaying
            int difference = amountsToGive.Select((t, i) => (int)(t - amountsToReceive[i])).Sum();

            // Trade is worth for us if the difference is greater than 0
            // If not, we assume that the trade might be good for us in the future, unless we're bot account where we assume that inventory doesn't change
            return(new ParseTradeResult(tradeOffer.TradeOfferID, difference > 0 ? ParseTradeResult.EResult.AcceptedWithItemLose : (Bot.BotConfig.IsBotAccount ? ParseTradeResult.EResult.RejectedPermanently : ParseTradeResult.EResult.RejectedTemporarily)));
        }
Пример #12
0
        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);
        }
Пример #13
0
        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);
        }
Пример #14
0
        private async Task <bool> ShouldAcceptTrade(Steam.TradeOffer tradeOffer)
        {
            if (tradeOffer == null)
            {
                Logging.LogNullError(nameof(tradeOffer), Bot.BotName);
                return(false);
            }

            // Always accept trades when we're not losing anything
            if (tradeOffer.ItemsToGive.Count == 0)
            {
                // Unless it's steam fuckup and we're dealing with broken trade
                return(tradeOffer.ItemsToReceive.Count > 0);
            }

            // Always accept trades from SteamMasterID
            if ((tradeOffer.OtherSteamID64 != 0) && (tradeOffer.OtherSteamID64 == Bot.BotConfig.SteamMasterID))
            {
                return(true);
            }

            // If we don't have SteamTradeMatcher enabled, this is the end for us
            if (!Bot.BotConfig.SteamTradeMatcher)
            {
                return(false);
            }

            // Decline trade if we're giving more count-wise
            if (tradeOffer.ItemsToGive.Count > tradeOffer.ItemsToReceive.Count)
            {
                return(false);
            }

            // Decline trade if we're losing anything but steam cards, or if it's non-dupes trade
            if (!tradeOffer.IsSteamCardsOnlyTradeForUs() || !tradeOffer.IsPotentiallyDupesTradeForUs())
            {
                return(false);
            }

            // At this point we're sure that STM trade is valid
            // Now check if it's worth for us to do the trade
            HashSet <Steam.Item> inventory = await Bot.ArchiWebHandler.GetMyInventory(false).ConfigureAwait(false);

            if ((inventory == null) || (inventory.Count == 0))
            {
                return(true);                // OK, assume that this trade is valid, we can't check our EQ
            }

            // Get appIDs we're interested in
            HashSet <uint> appIDs = new HashSet <uint>(tradeOffer.ItemsToGive.Select(item => item.RealAppID));

            // Now remove from our inventory all items we're NOT interested in
            inventory.RemoveWhere(item => !appIDs.Contains(item.RealAppID));
            inventory.TrimExcess();

            // If for some reason Valve is talking crap and we can't find mentioned items, assume OK
            if (inventory.Count == 0)
            {
                return(true);
            }

            // Now let's create a map which maps items to their amount in our EQ
            Dictionary <ulong, uint> amountMap = new Dictionary <ulong, uint>();

            foreach (Steam.Item item in inventory)
            {
                uint amount;
                if (amountMap.TryGetValue(item.ClassID, out amount))
                {
                    amountMap[item.ClassID] = amount + item.Amount;
                }
                else
                {
                    amountMap[item.ClassID] = item.Amount;
                }
            }

            // Calculate our value of items to give
            List <uint> amountsToGive = new List <uint>(tradeOffer.ItemsToGive.Count);

            foreach (ulong key in tradeOffer.ItemsToGive.Select(item => item.ClassID))
            {
                uint amount;
                if (!amountMap.TryGetValue(key, out amount))
                {
                    amountsToGive.Add(0);
                    continue;
                }

                amountsToGive.Add(amount);
            }

            // Sort it ascending
            amountsToGive.Sort();

            // Calculate our value of items to receive
            List <uint> amountsToReceive = new List <uint>(tradeOffer.ItemsToReceive.Count);

            foreach (ulong key in tradeOffer.ItemsToReceive.Select(item => item.ClassID))
            {
                uint amount;
                if (!amountMap.TryGetValue(key, out amount))
                {
                    amountsToReceive.Add(0);
                    continue;
                }

                amountsToReceive.Add(amount);
            }

            // Sort it ascending
            amountsToReceive.Sort();

            // Check actual difference
            int difference = amountsToGive.Select((t, i) => (int)(t - amountsToReceive[i])).Sum();

            // Trade is worth for us if the difference is greater than 0
            return(difference > 0);
        }
Пример #15
0
		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;
		}