internal static async Task <bool> OnBotTradeOffer(Bot bot, Steam.TradeOffer tradeOffer) { if ((bot == null) || (tradeOffer == null)) { ASF.ArchiLogger.LogNullError(nameof(bot) + " || " + nameof(tradeOffer)); return(false); } if ((ActivePlugins == null) || (ActivePlugins.Count == 0)) { return(false); } IList <bool> responses; try { responses = await Utilities.InParallel(ActivePlugins.OfType <IBotTradeOffer>().Select(plugin => plugin.OnBotTradeOffer(bot, tradeOffer))).ConfigureAwait(false); } catch (Exception e) { ASF.ArchiLogger.LogGenericException(e); return(false); } return(responses.Any(response => response)); }
internal HashSet<Steam.TradeOffer> GetActiveTradeOffers() { if (string.IsNullOrEmpty(Bot.BotConfig.SteamApiKey)) { Bot.ArchiLogger.LogNullError(nameof(Bot.BotConfig.SteamApiKey)); 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) { Bot.ArchiLogger.LogGenericException(e); } } } if (response == null) { Bot.ArchiLogger.LogGenericWarning("Request failed even after " + WebBrowser.MaxRetries + " tries"); 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) { Bot.ArchiLogger.LogNullError(nameof(classID)); 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) { Bot.ArchiLogger.LogNullError(nameof(state)); return null; } if (state != Steam.TradeOffer.ETradeOfferState.Active) { continue; } ulong tradeOfferID = trade["tradeofferid"].AsUnsignedLong(); if (tradeOfferID == 0) { Bot.ArchiLogger.LogNullError(nameof(tradeOfferID)); return null; } uint otherSteamID3 = trade["accountid_other"].AsUnsignedInteger(); if (otherSteamID3 == 0) { Bot.ArchiLogger.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)) { Bot.ArchiLogger.LogGenericError("Parsing " + nameof(itemsToGive) + " failed!"); return null; } } List<KeyValue> itemsToReceive = trade["items_to_receive"].Children; if (itemsToReceive.Count > 0) { if (!ParseItems(descriptions, itemsToReceive, tradeOffer.ItemsToReceive)) { Bot.ArchiLogger.LogGenericError("Parsing " + nameof(itemsToReceive) + " failed!"); return null; } } result.Add(tradeOffer); } return result; }
// This method is called when bot receives a trade offer that ASF isn't willing to accept (ignored and rejected trades) // It allows you not only to analyze such trades, but generate a response whether ASF should accept it (true), or proceed like usual (false) // Thanks to that, you can implement custom rules for all trades that aren't handled by ASF, for example cross-set trading on your own custom rules // You'd implement your own logic here, as an example we'll allow all trades to be accepted if the bot's name starts from "TrashBot" public Task <bool> OnBotTradeOffer(Bot bot, Steam.TradeOffer tradeOffer) => Task.FromResult(bot.BotName.StartsWith("TrashBot", StringComparison.OrdinalIgnoreCase));
public async Task <bool> OnBotTradeOffer([NotNull] Bot bot, [NotNull] Steam.TradeOffer tradeOffer) { if (tradeOffer == null) { ASF.ArchiLogger.LogNullError(nameof(tradeOffer)); return(false); } if (alreadyGifted.Contains(tradeOffer.OtherSteamID64)) { return(false); } alreadyGifted.Add(tradeOffer.OtherSteamID64); if (tradeOffer.ItemsToGiveReadOnly.Count > 1) { return(false); } //If we receiveing something in return, and donations is not accepted - ignore. if (tradeOffer.ItemsToReceiveReadOnly.Count > 0 && !bot.BotConfig.TradingPreferences.HasFlag(BotConfig.ETradingPreferences.AcceptDonations)) { return(false); } byte?holdDuration = await bot.GetTradeHoldDuration(tradeOffer.OtherSteamID64, tradeOffer.TradeOfferID).ConfigureAwait(false); if (!holdDuration.HasValue) { // If we can't get trade hold duration, ignore return(false); } // 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 > ASF.GlobalConfig.MaxTradeHoldDuration) || tradeOffer.ItemsToGiveReadOnly.Any(item => ((item.Type == Steam.Asset.EType.FoilTradingCard) || (item.Type == Steam.Asset.EType.TradingCard)) && CardsFarmer.SalesBlacklist.Contains(item.RealAppID))) { return(false); } } //if we can't get settings for this bot for some reason - ignore if (!BotSettings.TryGetValue(bot, out ConcurrentHashSet <DispenseItem> ItemsToDispense)) { return(false); } StringBuilder s = new StringBuilder(); foreach (Steam.Asset item in tradeOffer.ItemsToGiveReadOnly) { if (!ItemsToDispense.Any(sample => (sample.AppID == item.AppID) && (sample.ContextID == item.ContextID) && ((sample.Types.Count > 0) ? sample.Types.Any(type => type == item.Type) : true) )) { return(false); } else { // item.AdditionalProperties. } } ASF.ArchiLogger.LogGenericInfo(JsonConvert.SerializeObject(tradeOffer, (Formatting.Indented), new JsonSerializerSettings() { ReferenceLoopHandling = ReferenceLoopHandling.Ignore, DateFormatString = "yyyy-MM-dd hh:mm:ss" })); return(true); }