internal Bot(string botName) { if (Bots.ContainsKey(botName)) { return; } BotName = botName; ConfigFile = Path.Combine(Program.ConfigDirectoryPath, BotName + ".xml"); SentryFile = Path.Combine(Program.ConfigDirectoryPath, BotName + ".bin"); if (!ReadConfig()) { return; } if (!Enabled) { return; } lock (Bots) { Bots.Add(BotName, this); } // Initialize SteamClient = new SteamClient(); ArchiHandler = new ArchiHandler(); SteamClient.AddHandler(ArchiHandler); CallbackManager = new CallbackManager(SteamClient); CallbackManager.Subscribe <SteamClient.ConnectedCallback>(OnConnected); CallbackManager.Subscribe <SteamClient.DisconnectedCallback>(OnDisconnected); SteamFriends = SteamClient.GetHandler <SteamFriends>(); CallbackManager.Subscribe <SteamFriends.FriendsListCallback>(OnFriendsList); CallbackManager.Subscribe <SteamFriends.FriendMsgCallback>(OnFriendMsg); CallbackManager.Subscribe <SteamFriends.PersonaStateCallback>(OnPersonaState); SteamUser = SteamClient.GetHandler <SteamUser>(); CallbackManager.Subscribe <SteamUser.AccountInfoCallback>(OnAccountInfo); CallbackManager.Subscribe <SteamUser.LoggedOffCallback>(OnLoggedOff); CallbackManager.Subscribe <SteamUser.LoggedOnCallback>(OnLoggedOn); CallbackManager.Subscribe <SteamUser.UpdateMachineAuthCallback>(OnMachineAuth); CallbackManager.Subscribe <ArchiHandler.NotificationCallback>(OnNotification); CallbackManager.Subscribe <ArchiHandler.PurchaseResponseCallback>(OnPurchaseResponse); ArchiWebHandler = new ArchiWebHandler(this, SteamApiKey); CardsFarmer = new CardsFarmer(this); Trading = new Trading(this); // Start Start(); }
private void OnNotification(ArchiHandler.NotificationCallback callback) { if (callback == null) { return; } switch (callback.NotificationType) { case ArchiHandler.NotificationCallback.ENotificationType.Trading: Trading.CheckTrades(); break; } }
internal void Start() { if (SteamClient != null) { return; } SteamClient = new SteamClient(); ArchiHandler = new ArchiHandler(); SteamClient.AddHandler(ArchiHandler); CallbackManager = new CallbackManager(SteamClient); CallbackManager.Subscribe <SteamClient.ConnectedCallback>(OnConnected); CallbackManager.Subscribe <SteamClient.DisconnectedCallback>(OnDisconnected); SteamFriends = SteamClient.GetHandler <SteamFriends>(); CallbackManager.Subscribe <SteamFriends.FriendsListCallback>(OnFriendsList); CallbackManager.Subscribe <SteamFriends.FriendMsgCallback>(OnFriendMsg); CallbackManager.Subscribe <SteamFriends.PersonaStateCallback>(OnPersonaState); SteamUser = SteamClient.GetHandler <SteamUser>(); CallbackManager.Subscribe <SteamUser.AccountInfoCallback>(OnAccountInfo); CallbackManager.Subscribe <SteamUser.LoggedOffCallback>(OnLoggedOff); CallbackManager.Subscribe <SteamUser.LoggedOnCallback>(OnLoggedOn); CallbackManager.Subscribe <SteamUser.UpdateMachineAuthCallback>(OnMachineAuth); CallbackManager.Subscribe <ArchiHandler.NotificationCallback>(OnNotification); CallbackManager.Subscribe <ArchiHandler.PurchaseResponseCallback>(OnPurchaseResponse); ArchiWebHandler = new ArchiWebHandler(this, SteamApiKey); Trading = new Trading(this); SteamClient.Connect(); Task.Run(() => HandleCallbacks()); }
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 Bot(string botName) { if (string.IsNullOrEmpty(botName)) { throw new ArgumentNullException(nameof(botName)); } if (Bots.ContainsKey(botName)) { throw new ArgumentException("That bot is already defined!"); } string botPath = Path.Combine(SharedInfo.ConfigDirectory, botName); BotName = botName; SentryFile = botPath + ".bin"; string botConfigFile = botPath + ".json"; BotConfig = BotConfig.Load(botConfigFile); if (BotConfig == null) { Logging.LogGenericError("Your bot config is invalid, please verify content of " + botConfigFile + " and try again!", botName); return; } // Register bot as available for ASF if (!Bots.TryAdd(botName, this)) { throw new ArgumentException("That bot is already defined!"); } string botDatabaseFile = botPath + ".db"; BotDatabase = BotDatabase.Load(botDatabaseFile); if (BotDatabase == null) { Logging.LogGenericError("Bot database could not be loaded, refusing to create this bot instance! In order to recreate it, remove " + botDatabaseFile + " and try again!", botName); return; } if (BotDatabase.MobileAuthenticator != null) { BotDatabase.MobileAuthenticator.Init(this); } else { // Support and convert SDA files string maFilePath = botPath + ".maFile"; if (File.Exists(maFilePath)) { ImportAuthenticator(maFilePath); } } // Initialize SteamClient = new SteamClient(Program.GlobalConfig.SteamProtocol); if (Program.GlobalConfig.Debug && Directory.Exists(SharedInfo.DebugDirectory)) { string debugListenerPath = Path.Combine(SharedInfo.DebugDirectory, botName); try { Directory.CreateDirectory(debugListenerPath); SteamClient.DebugNetworkListener = new NetHookNetworkListener(debugListenerPath); } catch (Exception e) { Logging.LogGenericException(e, botName); } } ArchiHandler = new ArchiHandler(this); SteamClient.AddHandler(ArchiHandler); CallbackManager = new CallbackManager(SteamClient); CallbackManager.Subscribe<SteamClient.ConnectedCallback>(OnConnected); CallbackManager.Subscribe<SteamClient.DisconnectedCallback>(OnDisconnected); SteamApps = SteamClient.GetHandler<SteamApps>(); CallbackManager.Subscribe<SteamApps.FreeLicenseCallback>(OnFreeLicense); CallbackManager.Subscribe<SteamApps.GuestPassListCallback>(OnGuestPassList); CallbackManager.Subscribe<SteamApps.LicenseListCallback>(OnLicenseList); SteamFriends = SteamClient.GetHandler<SteamFriends>(); CallbackManager.Subscribe<SteamFriends.ChatInviteCallback>(OnChatInvite); CallbackManager.Subscribe<SteamFriends.ChatMsgCallback>(OnChatMsg); CallbackManager.Subscribe<SteamFriends.FriendsListCallback>(OnFriendsList); CallbackManager.Subscribe<SteamFriends.FriendMsgCallback>(OnFriendMsg); CallbackManager.Subscribe<SteamFriends.FriendMsgHistoryCallback>(OnFriendMsgHistory); CallbackManager.Subscribe<SteamFriends.PersonaStateCallback>(OnPersonaState); SteamUser = SteamClient.GetHandler<SteamUser>(); CallbackManager.Subscribe<SteamUser.AccountInfoCallback>(OnAccountInfo); CallbackManager.Subscribe<SteamUser.LoggedOffCallback>(OnLoggedOff); CallbackManager.Subscribe<SteamUser.LoggedOnCallback>(OnLoggedOn); CallbackManager.Subscribe<SteamUser.LoginKeyCallback>(OnLoginKey); CallbackManager.Subscribe<SteamUser.UpdateMachineAuthCallback>(OnMachineAuth); CallbackManager.Subscribe<SteamUser.WebAPIUserNonceCallback>(OnWebAPIUserNonce); CallbackManager.Subscribe<ArchiHandler.NotificationsCallback>(OnNotifications); CallbackManager.Subscribe<ArchiHandler.OfflineMessageCallback>(OnOfflineMessage); CallbackManager.Subscribe<ArchiHandler.PlayingSessionStateCallback>(OnPlayingSessionState); CallbackManager.Subscribe<ArchiHandler.PurchaseResponseCallback>(OnPurchaseResponse); CallbackManager.Subscribe<ArchiHandler.SharedLibraryLockStatusCallback>(OnSharedLibraryLockStatus); ArchiWebHandler = new ArchiWebHandler(this); CardsFarmer = new CardsFarmer(this) { Paused = BotConfig.Paused }; Trading = new Trading(this); HeartBeatTimer = new Timer( async e => await HeartBeat().ConfigureAwait(false), null, TimeSpan.FromMinutes(1) + TimeSpan.FromMinutes(0.2 * Bots.Count), // Delay TimeSpan.FromMinutes(1) // Period ); Initialize().Forget(); }
internal async Task OnPersonaState(SteamFriends.PersonaStateCallback callback) { if (callback == null) { ASF.ArchiLogger.LogNullError(nameof(callback)); return; } if (DateTime.UtcNow < LastAnnouncementCheck.AddHours(MinAnnouncementCheckTTL)) { return; } // Don't announce if we don't meet conditions if (!Bot.HasMobileAuthenticator || !Bot.BotConfig.TradingPreferences.HasFlag(BotConfig.ETradingPreferences.SteamTradeMatcher) || !await Bot.ArchiWebHandler.HasValidApiKey().ConfigureAwait(false) || !await Bot.ArchiWebHandler.HasPublicInventory().ConfigureAwait(false)) { LastAnnouncementCheck = DateTime.UtcNow; ShouldSendHeartBeats = false; return; } string nickname = callback.Name ?? ""; string avatarHash = ""; if ((callback.AvatarHash != null) && (callback.AvatarHash.Length > 0) && callback.AvatarHash.Any(singleByte => singleByte != 0)) { avatarHash = BitConverter.ToString(callback.AvatarHash).Replace("-", "").ToLowerInvariant(); if (avatarHash.All(singleChar => singleChar == '0')) { avatarHash = ""; } } bool matchEverything = Bot.BotConfig.TradingPreferences.HasFlag(BotConfig.ETradingPreferences.MatchEverything); await Semaphore.WaitAsync().ConfigureAwait(false); try { if (DateTime.UtcNow < LastAnnouncementCheck.AddHours(MinAnnouncementCheckTTL)) { return; } await Trading.LimitInventoryRequestsAsync().ConfigureAwait(false); HashSet <Steam.Item> inventory = await Bot.ArchiWebHandler.GetMySteamInventory(true, new HashSet <Steam.Item.EType> { Steam.Item.EType.TradingCard }).ConfigureAwait(false); // This is actually inventory failure, so we'll stop sending heartbeats but not record it as valid check if (inventory == null) { ShouldSendHeartBeats = false; return; } // This is actual inventory if (inventory.Count < MinCardsCount) { LastAnnouncementCheck = DateTime.UtcNow; ShouldSendHeartBeats = false; return; } string request = await GetURL().ConfigureAwait(false) + "/api/Announce"; Dictionary <string, string> data = new Dictionary <string, string>(6) { { "SteamID", Bot.SteamID.ToString() }, { "Guid", Program.GlobalDatabase.Guid.ToString("N") }, { "Nickname", nickname }, { "AvatarHash", avatarHash }, { "MatchEverything", matchEverything ? "1" : "0" }, { "CardsCount", inventory.Count.ToString() } }; // We don't need retry logic here if (await Program.WebBrowser.UrlPost(request, data).ConfigureAwait(false)) { LastAnnouncementCheck = DateTime.UtcNow; ShouldSendHeartBeats = true; } } finally { Semaphore.Release(); } }
internal Bot(string botName) { if (Bots.ContainsKey(botName)) { return; } BotName = botName; ConfigFile = Path.Combine(Program.ConfigDirectoryPath, BotName + ".xml"); SentryFile = Path.Combine(Program.ConfigDirectoryPath, BotName + ".bin"); if (!ReadConfig()) { return; } if (!Enabled) { return; } lock (Bots) { Bots.Add(BotName, this); } // Initialize SteamClient = new SteamClient(); ArchiHandler = new ArchiHandler(); SteamClient.AddHandler(ArchiHandler); CallbackManager = new CallbackManager(SteamClient); CallbackManager.Subscribe<SteamClient.ConnectedCallback>(OnConnected); CallbackManager.Subscribe<SteamClient.DisconnectedCallback>(OnDisconnected); SteamFriends = SteamClient.GetHandler<SteamFriends>(); CallbackManager.Subscribe<SteamFriends.FriendsListCallback>(OnFriendsList); CallbackManager.Subscribe<SteamFriends.FriendMsgCallback>(OnFriendMsg); CallbackManager.Subscribe<SteamFriends.PersonaStateCallback>(OnPersonaState); SteamUser = SteamClient.GetHandler<SteamUser>(); CallbackManager.Subscribe<SteamUser.AccountInfoCallback>(OnAccountInfo); CallbackManager.Subscribe<SteamUser.LoggedOffCallback>(OnLoggedOff); CallbackManager.Subscribe<SteamUser.LoggedOnCallback>(OnLoggedOn); CallbackManager.Subscribe<SteamUser.UpdateMachineAuthCallback>(OnMachineAuth); CallbackManager.Subscribe<ArchiHandler.NotificationCallback>(OnNotification); CallbackManager.Subscribe<ArchiHandler.PurchaseResponseCallback>(OnPurchaseResponse); ArchiWebHandler = new ArchiWebHandler(this, SteamApiKey); CardsFarmer = new CardsFarmer(this); Trading = new Trading(this); // Start Start(); }
internal async Task OnPersonaState(SteamFriends.PersonaStateCallback callback) { if (callback == null) { ASF.ArchiLogger.LogNullError(nameof(callback)); return; } // Don't announce if we don't meet conditions if (!Bot.HasMobileAuthenticator || !Bot.BotConfig.TradingPreferences.HasFlag(BotConfig.ETradingPreferences.SteamTradeMatcher) || !await Bot.ArchiWebHandler.HasValidApiKey().ConfigureAwait(false) || !await Bot.ArchiWebHandler.HasPublicInventory().ConfigureAwait(false)) { ShouldSendHeartBeats = false; return; } string nickname = callback.Name ?? ""; string avatarHash = ""; if ((callback.AvatarHash != null) && (callback.AvatarHash.Length > 0) && callback.AvatarHash.Any(singleByte => singleByte != 0)) { avatarHash = BitConverter.ToString(callback.AvatarHash).Replace("-", "").ToLowerInvariant(); if (avatarHash.All(singleChar => singleChar == '0')) { avatarHash = ""; } } bool matchEverything = Bot.BotConfig.TradingPreferences.HasFlag(BotConfig.ETradingPreferences.MatchEverything); // Skip announcing if we already announced this bot with the same data if (ShouldSendHeartBeats && (LastNickname != null) && nickname.Equals(LastNickname) && (LastAvatarHash != null) && avatarHash.Equals(LastAvatarHash) && LastMatchEverything.HasValue && (matchEverything == LastMatchEverything.Value)) { return; } await Semaphore.WaitAsync().ConfigureAwait(false); try { // Skip announcing if we already announced this bot with the same data if (ShouldSendHeartBeats && (LastNickname != null) && nickname.Equals(LastNickname) && (LastAvatarHash != null) && avatarHash.Equals(LastAvatarHash) && LastMatchEverything.HasValue && (matchEverything == LastMatchEverything.Value)) { return; } await Trading.LimitInventoryRequestsAsync().ConfigureAwait(false); HashSet <Steam.Item> inventory = await Bot.ArchiWebHandler.GetMySteamInventory(true, new HashSet <Steam.Item.EType> { Steam.Item.EType.TradingCard }).ConfigureAwait(false); if ((inventory == null) || (inventory.Count == 0)) { // Don't announce, we have empty inventory ShouldSendHeartBeats = false; return; } // Even if following request fails, we want to send HeartBeats regardless ShouldSendHeartBeats = true; string request = await GetURL().ConfigureAwait(false) + "/api/Announce"; Dictionary <string, string> data = new Dictionary <string, string>(6) { { "SteamID", Bot.SteamID.ToString() }, { "Guid", Program.GlobalDatabase.Guid.ToString("N") }, { "Nickname", nickname }, { "AvatarHash", avatarHash }, { "MatchEverything", matchEverything ? "1" : "0" }, { "CardsCount", inventory.Count.ToString() } }; // We don't need retry logic here if (await Program.WebBrowser.UrlPost(request, data).ConfigureAwait(false)) { LastNickname = nickname; LastAvatarHash = avatarHash; LastMatchEverything = matchEverything; } } finally { Semaphore.Release(); } }
internal Bot(string botName, bool initialLaunch = false) { if (Bots.ContainsKey(botName)) { return; } BotName = botName; ConfigFile = Path.Combine(Program.ConfigDirectory, BotName + ".xml"); LoginKeyFile = Path.Combine(Program.ConfigDirectory, BotName + ".key"); MobileAuthenticatorFile = Path.Combine(Program.ConfigDirectory, BotName + ".auth"); SentryFile = Path.Combine(Program.ConfigDirectory, BotName + ".bin"); if (!ReadConfig()) { return; } if (!Enabled) { return; } Bots.AddOrUpdate(BotName, this, (key, value) => this); // Initialize SteamClient = new SteamClient(); ArchiHandler = new ArchiHandler(); SteamClient.AddHandler(ArchiHandler); CallbackManager = new CallbackManager(SteamClient); CallbackManager.Subscribe<SteamClient.ConnectedCallback>(OnConnected); CallbackManager.Subscribe<SteamClient.DisconnectedCallback>(OnDisconnected); SteamApps = SteamClient.GetHandler<SteamApps>(); CallbackManager.Subscribe<SteamApps.FreeLicenseCallback>(OnFreeLicense); SteamFriends = SteamClient.GetHandler<SteamFriends>(); CallbackManager.Subscribe<SteamFriends.ChatInviteCallback>(OnChatInvite); CallbackManager.Subscribe<SteamFriends.ChatMsgCallback>(OnChatMsg); CallbackManager.Subscribe<SteamFriends.FriendsListCallback>(OnFriendsList); CallbackManager.Subscribe<SteamFriends.FriendMsgCallback>(OnFriendMsg); CallbackManager.Subscribe<SteamFriends.FriendMsgHistoryCallback>(OnFriendMsgHistory); if (UseAsfAsMobileAuthenticator && File.Exists(MobileAuthenticatorFile)) { SteamGuardAccount = JsonConvert.DeserializeObject<SteamGuardAccount>(File.ReadAllText(MobileAuthenticatorFile)); } SteamUser = SteamClient.GetHandler<SteamUser>(); CallbackManager.Subscribe<SteamUser.AccountInfoCallback>(OnAccountInfo); CallbackManager.Subscribe<SteamUser.LoggedOffCallback>(OnLoggedOff); CallbackManager.Subscribe<SteamUser.LoggedOnCallback>(OnLoggedOn); CallbackManager.Subscribe<SteamUser.LoginKeyCallback>(OnLoginKey); CallbackManager.Subscribe<SteamUser.UpdateMachineAuthCallback>(OnMachineAuth); CallbackManager.Subscribe<ArchiHandler.NotificationsCallback>(OnNotifications); CallbackManager.Subscribe<ArchiHandler.OfflineMessageCallback>(OnOfflineMessage); CallbackManager.Subscribe<ArchiHandler.PurchaseResponseCallback>(OnPurchaseResponse); ArchiWebHandler = new ArchiWebHandler(this, SteamApiKey); CardsFarmer = new CardsFarmer(this); Trading = new Trading(this); if (SendTradePeriod > 0 && SendItemsTimer == null) { SendItemsTimer = new Timer( async e => await ResponseSendTrade(BotName).ConfigureAwait(false), null, TimeSpan.FromHours(SendTradePeriod), // Delay TimeSpan.FromHours(SendTradePeriod) // Period ); } if (initialLaunch && !StartOnLaunch) { return; } // Start var start = Task.Run(async () => await Start().ConfigureAwait(false)); }
internal void Start() { if (SteamClient != null) { return; } SteamClient = new SteamClient(); ArchiHandler = new ArchiHandler(); SteamClient.AddHandler(ArchiHandler); CallbackManager = new CallbackManager(SteamClient); CallbackManager.Subscribe<SteamClient.ConnectedCallback>(OnConnected); CallbackManager.Subscribe<SteamClient.DisconnectedCallback>(OnDisconnected); SteamFriends = SteamClient.GetHandler<SteamFriends>(); CallbackManager.Subscribe<SteamFriends.FriendsListCallback>(OnFriendsList); CallbackManager.Subscribe<SteamFriends.FriendMsgCallback>(OnFriendMsg); CallbackManager.Subscribe<SteamFriends.PersonaStateCallback>(OnPersonaState); SteamUser = SteamClient.GetHandler<SteamUser>(); CallbackManager.Subscribe<SteamUser.AccountInfoCallback>(OnAccountInfo); CallbackManager.Subscribe<SteamUser.LoggedOffCallback>(OnLoggedOff); CallbackManager.Subscribe<SteamUser.LoggedOnCallback>(OnLoggedOn); CallbackManager.Subscribe<SteamUser.UpdateMachineAuthCallback>(OnMachineAuth); CallbackManager.Subscribe<ArchiHandler.NotificationCallback>(OnNotification); CallbackManager.Subscribe<ArchiHandler.PurchaseResponseCallback>(OnPurchaseResponse); ArchiWebHandler = new ArchiWebHandler(this, SteamApiKey); Trading = new Trading(this); SteamClient.Connect(); Task.Run(() => HandleCallbacks()); }
internal Bot(string botName) { if (string.IsNullOrEmpty(botName)) { throw new ArgumentNullException(nameof(botName)); } if (Bots.ContainsKey(botName)) { throw new Exception("That bot is already defined!"); } string botPath = Path.Combine(Program.ConfigDirectory, botName); BotName = botName; SentryFile = botPath + ".bin"; BotConfig = BotConfig.Load(botPath + ".json"); if (BotConfig == null) { Logging.LogGenericError("Your bot config is invalid, refusing to start this bot instance!", botName); return; } if (!BotConfig.Enabled) { Logging.LogGenericInfo("Not initializing this instance because it's disabled in config file", botName); return; } BotDatabase = BotDatabase.Load(botPath + ".db"); if (BotDatabase == null) { Logging.LogGenericError("Bot database could not be loaded, refusing to start this bot instance!", botName); return; } // TODO: Converter code will be removed soon if (BotDatabase.SteamGuardAccount != null) { Logging.LogGenericWarning("Converting old ASF 2FA V2.0 format into new ASF 2FA V2.1 format...", botName); BotDatabase.MobileAuthenticator = MobileAuthenticator.LoadFromSteamGuardAccount(BotDatabase.SteamGuardAccount); Logging.LogGenericInfo("Done! If you didn't make a copy of your revocation code yet, then it's a good moment to do so: " + BotDatabase.SteamGuardAccount.RevocationCode, botName); Logging.LogGenericWarning("ASF will not keep this code anymore!", botName); BotDatabase.SteamGuardAccount = null; } if (BotDatabase.MobileAuthenticator != null) { BotDatabase.MobileAuthenticator.Init(this); } else { // Support and convert SDA files string maFilePath = botPath + ".maFile"; if (File.Exists(maFilePath)) { ImportAuthenticator(maFilePath); } } // Initialize SteamClient = new SteamClient(Program.GlobalConfig.SteamProtocol); if (Program.GlobalConfig.Debug && !Debugging.NetHookAlreadyInitialized && Directory.Exists(Program.DebugDirectory)) { try { Debugging.NetHookAlreadyInitialized = true; SteamClient.DebugNetworkListener = new NetHookNetworkListener(Program.DebugDirectory); } catch (Exception e) { Logging.LogGenericException(e, botName); } } ArchiHandler = new ArchiHandler(this); SteamClient.AddHandler(ArchiHandler); CallbackManager = new CallbackManager(SteamClient); CallbackManager.Subscribe<SteamClient.ConnectedCallback>(OnConnected); CallbackManager.Subscribe<SteamClient.DisconnectedCallback>(OnDisconnected); SteamApps = SteamClient.GetHandler<SteamApps>(); CallbackManager.Subscribe<SteamApps.FreeLicenseCallback>(OnFreeLicense); CallbackManager.Subscribe<SteamApps.GuestPassListCallback>(OnGuestPassList); SteamFriends = SteamClient.GetHandler<SteamFriends>(); CallbackManager.Subscribe<SteamFriends.ChatInviteCallback>(OnChatInvite); CallbackManager.Subscribe<SteamFriends.ChatMsgCallback>(OnChatMsg); CallbackManager.Subscribe<SteamFriends.FriendsListCallback>(OnFriendsList); CallbackManager.Subscribe<SteamFriends.FriendMsgCallback>(OnFriendMsg); CallbackManager.Subscribe<SteamFriends.FriendMsgHistoryCallback>(OnFriendMsgHistory); SteamUser = SteamClient.GetHandler<SteamUser>(); CallbackManager.Subscribe<SteamUser.AccountInfoCallback>(OnAccountInfo); CallbackManager.Subscribe<SteamUser.LoggedOffCallback>(OnLoggedOff); CallbackManager.Subscribe<SteamUser.LoggedOnCallback>(OnLoggedOn); CallbackManager.Subscribe<SteamUser.LoginKeyCallback>(OnLoginKey); CallbackManager.Subscribe<SteamUser.UpdateMachineAuthCallback>(OnMachineAuth); CallbackManager.Subscribe<SteamUser.WebAPIUserNonceCallback>(OnWebAPIUserNonce); CallbackManager.Subscribe<ArchiHandler.NotificationsCallback>(OnNotifications); CallbackManager.Subscribe<ArchiHandler.OfflineMessageCallback>(OnOfflineMessage); CallbackManager.Subscribe<ArchiHandler.PlayingSessionStateCallback>(OnPlayingSessionState); CallbackManager.Subscribe<ArchiHandler.PurchaseResponseCallback>(OnPurchaseResponse); ArchiWebHandler = new ArchiWebHandler(this); CardsFarmer = new CardsFarmer(this); Trading = new Trading(this); if ((AcceptConfirmationsTimer == null) && (BotConfig.AcceptConfirmationsPeriod > 0)) { AcceptConfirmationsTimer = new Timer( async e => await AcceptConfirmations(true).ConfigureAwait(false), null, TimeSpan.FromMinutes(BotConfig.AcceptConfirmationsPeriod), // Delay TimeSpan.FromMinutes(BotConfig.AcceptConfirmationsPeriod) // Period ); } if ((SendItemsTimer == null) && (BotConfig.SendTradePeriod > 0)) { SendItemsTimer = new Timer( async e => await ResponseLoot(BotConfig.SteamMasterID).ConfigureAwait(false), null, TimeSpan.FromHours(BotConfig.SendTradePeriod), // Delay TimeSpan.FromHours(BotConfig.SendTradePeriod) // Period ); } // Register bot as available for ASF Bots[botName] = this; if (!BotConfig.StartOnLaunch) { return; } // Start Start().Forget(); }
private async Task <bool> MatchActivelyRound(IReadOnlyCollection <Steam.Asset.EType> acceptedMatchableTypes) { if ((acceptedMatchableTypes == null) || (acceptedMatchableTypes.Count == 0)) { Bot.ArchiLogger.LogNullError(nameof(acceptedMatchableTypes)); 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.LogGenericTrace(string.Format(Strings.ErrorIsEmpty, nameof(ourInventory))); 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.LogGenericTrace(string.Format(Strings.ErrorIsEmpty, nameof(ourInventoryState))); return(false); } HashSet <ListedUser> listedUsers = await GetListedUsers().ConfigureAwait(false); if ((listedUsers == null) || (listedUsers.Count == 0)) { Bot.ArchiLogger.LogGenericTrace(string.Format(Strings.ErrorIsEmpty, nameof(listedUsers))); return(false); } byte emptyMatches = 0; HashSet <(uint AppID, Steam.Asset.EType Type)> skippedSetsThisRound = new HashSet <(uint AppID, Steam.Asset.EType Type)>(); foreach (ListedUser listedUser in listedUsers.Where(listedUser => listedUser.MatchEverything && acceptedMatchableTypes.Any(listedUser.MatchableTypes.Contains) && !Bot.IsBlacklistedFromTrades(listedUser.SteamID)).OrderByDescending(listedUser => listedUser.Score).Take(MaxMatchedBotsHard)) { Bot.ArchiLogger.LogGenericTrace(listedUser.SteamID + "..."); HashSet <Steam.Asset> theirInventory = await Bot.ArchiWebHandler.GetInventory(listedUser.SteamID, tradable : true, wantedSets : ourInventoryState.Keys, skippedSets : skippedSetsThisRound).ConfigureAwait(false); if ((theirInventory == null) || (theirInventory.Count == 0)) { Bot.ArchiLogger.LogGenericTrace(string.Format(Strings.ErrorIsEmpty, nameof(theirInventory))); continue; } HashSet <(uint AppID, Steam.Asset.EType Type)> skippedSetsThisUser = new HashSet <(uint AppID, Steam.Asset.EType Type)>(); Dictionary <(uint AppID, Steam.Asset.EType Type), Dictionary <ulong, uint> > theirInventoryState = Trading.GetInventoryState(theirInventory); for (byte i = 0; i < Trading.MaxTradesPerAccount; i++) { byte itemsInTrade = 0; Dictionary <ulong, uint> classIDsToGive = new Dictionary <ulong, uint>(); Dictionary <ulong, uint> classIDsToReceive = new Dictionary <ulong, uint>(); 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; } // Skip this set from the remaining of this round skippedSetsThisUser.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); } itemsInTrade += 2; match = true; break; } if (match) { break; } } } while (match && (itemsInTrade < Trading.MaxItemsPerTrade - 1)); if (itemsInTrade >= Trading.MaxItemsPerTrade - 1) { break; } } if ((classIDsToGive.Count == 0) && (classIDsToReceive.Count == 0)) { Bot.ArchiLogger.LogGenericTrace(string.Format(Strings.ErrorIsEmpty, nameof(classIDsToGive))); if (++emptyMatches >= MaxMatchesBotsSoft) { Bot.ArchiLogger.LogGenericInfo(string.Format(Strings.ActivelyMatchingItemsRound, skippedSetsThisRound.Count)); return(skippedSetsThisRound.Count > 0); } break; } emptyMatches = 0; HashSet <Steam.Asset> itemsToGive = Trading.GetItemsFromInventory(ourInventory, classIDsToGive); HashSet <Steam.Asset> itemsToReceive = Trading.GetItemsFromInventory(theirInventory, classIDsToReceive); Bot.ArchiLogger.LogGenericTrace(Bot.SteamID + " <- " + string.Join(", ", itemsToReceive.Select(item => item.RealAppID + "/" + item.Type + "-" + item.ClassID + " #" + item.Amount)) + " | " + string.Join(", ", itemsToGive.Select(item => item.RealAppID + "/" + item.Type + "-" + item.ClassID + " #" + item.Amount)) + " -> " + listedUser.SteamID); (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)) { Bot.ArchiLogger.LogGenericTrace(Strings.WarningFailed); return(false); } } if (!success) { Bot.ArchiLogger.LogGenericTrace(Strings.WarningFailed); continue; } Bot.ArchiLogger.LogGenericTrace(Strings.Success); } if (skippedSetsThisUser.Count == 0) { continue; } skippedSetsThisRound.UnionWith(skippedSetsThisUser); foreach ((uint AppID, Steam.Asset.EType Type)skippedSet in skippedSetsThisUser) { ourInventoryState.Remove(skippedSet); } if (ourInventoryState.Values.All(set => set.Values.All(amount => amount <= 1))) { // User doesn't have any more dupes in the inventory Bot.ArchiLogger.LogGenericTrace(string.Format(Strings.ErrorIsEmpty, nameof(ourInventoryState))); break; } } Bot.ArchiLogger.LogGenericInfo(string.Format(Strings.ActivelyMatchingItemsRound, skippedSetsThisRound.Count)); return(skippedSetsThisRound.Count > 0); }
internal Bot(string botName) { if (string.IsNullOrEmpty(botName)) { throw new ArgumentNullException("botName"); } BotName = botName; string botPath = Path.Combine(Program.ConfigDirectory, botName); BotConfig = BotConfig.Load(botPath + ".json"); if (BotConfig == null) { Logging.LogGenericError("Your bot config is invalid, refusing to start this bot instance!", botName); return; } if (!BotConfig.Enabled) { return; } BotDatabase = BotDatabase.Load(botPath + ".db"); if (BotDatabase == null) { Logging.LogGenericError("Bot database could not be loaded, refusing to start this bot instance!", botName); return; } bool alreadyExists; lock (Bots) { alreadyExists = Bots.ContainsKey(botName); if (!alreadyExists) { Bots[botName] = this; } } if (alreadyExists) { return; } SentryFile = botPath + ".bin"; if (BotDatabase.SteamGuardAccount == null) { // Support and convert SDA files string maFilePath = botPath + ".maFile"; if (File.Exists(maFilePath)) { ImportAuthenticator(maFilePath); } } // Initialize SteamClient = new SteamClient(Program.GlobalConfig.SteamProtocol); if (Program.GlobalConfig.Debug && !Debugging.NetHookAlreadyInitialized && Directory.Exists(Program.DebugDirectory)) { try { SteamClient.DebugNetworkListener = new NetHookNetworkListener(Program.DebugDirectory); Debugging.NetHookAlreadyInitialized = true; } catch (Exception e) { Logging.LogGenericException(e, botName); } } ArchiHandler = new ArchiHandler(this); SteamClient.AddHandler(ArchiHandler); CallbackManager = new CallbackManager(SteamClient); CallbackManager.Subscribe<SteamClient.ConnectedCallback>(OnConnected); CallbackManager.Subscribe<SteamClient.DisconnectedCallback>(OnDisconnected); SteamApps = SteamClient.GetHandler<SteamApps>(); CallbackManager.Subscribe<SteamApps.FreeLicenseCallback>(OnFreeLicense); CallbackManager.Subscribe<SteamApps.GuestPassListCallback>(OnGuestPassList); SteamFriends = SteamClient.GetHandler<SteamFriends>(); CallbackManager.Subscribe<SteamFriends.ChatInviteCallback>(OnChatInvite); CallbackManager.Subscribe<SteamFriends.ChatMsgCallback>(OnChatMsg); CallbackManager.Subscribe<SteamFriends.FriendsListCallback>(OnFriendsList); CallbackManager.Subscribe<SteamFriends.FriendMsgCallback>(OnFriendMsg); CallbackManager.Subscribe<SteamFriends.FriendMsgHistoryCallback>(OnFriendMsgHistory); SteamUser = SteamClient.GetHandler<SteamUser>(); CallbackManager.Subscribe<SteamUser.AccountInfoCallback>(OnAccountInfo); CallbackManager.Subscribe<SteamUser.LoggedOffCallback>(OnLoggedOff); CallbackManager.Subscribe<SteamUser.LoggedOnCallback>(OnLoggedOn); CallbackManager.Subscribe<SteamUser.LoginKeyCallback>(OnLoginKey); CallbackManager.Subscribe<SteamUser.UpdateMachineAuthCallback>(OnMachineAuth); CallbackManager.Subscribe<SteamUser.WebAPIUserNonceCallback>(OnWebAPIUserNonce); CallbackManager.Subscribe<ArchiHandler.NotificationsCallback>(OnNotifications); CallbackManager.Subscribe<ArchiHandler.OfflineMessageCallback>(OnOfflineMessage); CallbackManager.Subscribe<ArchiHandler.PurchaseResponseCallback>(OnPurchaseResponse); ArchiWebHandler = new ArchiWebHandler(this); CardsFarmer = new CardsFarmer(this); Trading = new Trading(this); if (AcceptConfirmationsTimer == null && BotConfig.AcceptConfirmationsPeriod > 0) { AcceptConfirmationsTimer = new Timer( async e => await AcceptConfirmations(true).ConfigureAwait(false), null, TimeSpan.FromMinutes(BotConfig.AcceptConfirmationsPeriod), // Delay TimeSpan.FromMinutes(BotConfig.AcceptConfirmationsPeriod) // Period ); } if (SendItemsTimer == null && BotConfig.SendTradePeriod > 0) { SendItemsTimer = new Timer( async e => await ResponseSendTrade(BotConfig.SteamMasterID).ConfigureAwait(false), null, TimeSpan.FromHours(BotConfig.SendTradePeriod), // Delay TimeSpan.FromHours(BotConfig.SendTradePeriod) // Period ); } if (!BotConfig.StartOnLaunch) { return; } // Start Start().Forget(); }