public static async Task Init() { ImportantApps = new Dictionary <uint, List <string> >(); ImportantSubs = new Dictionary <uint, byte>(); await ReloadImportant(); await KeyNameCache.Init(); if (Settings.IsFullRun) { return; } var commandHandler = new CommandHandler(); Steam.Instance.RegisterCommandHandlers(commandHandler); if (Settings.Current.IRC.Enabled) { RssReader = new RSS(); IrcThread = new Thread(IRC.Instance.Connect) { Name = "IRC" }; IrcThread.Start(); IRC.Instance.RegisterCommandHandlers(commandHandler); } }
private void OnTimer(object sender, ElapsedEventArgs e) { if (!Steam.Instance.IsLoggedOn) { lock (FreeLicenseTimer) { FreeLicenseTimer.Start(); } return; } var list = FreeLicensesToRequest.Take(REQUEST_RATE_LIMIT).ToList(); var now = DateUtils.DateTimeToUnixTime(DateTime.UtcNow) - 60; Dictionary <uint, ulong> startTimes; using (var db = Database.Get()) { startTimes = db.Query( "SELECT `SubID`, `Value` FROM `SubsInfo` WHERE `Key` = @Key AND `SubID` IN @Ids", new { Key = KeyNameCache.GetSubKeyID("extended_starttime"), Ids = list.Select(x => x.Key) } ).ToDictionary(x => (uint)x.SubID, x => Convert.ToUInt64((string)x.Value)); } foreach (var(subId, _) in list) { if (startTimes.TryGetValue(subId, out var startTime) && startTime > now) { // If start time has not been reached yet, don't remove this app from the list and keep trying to activate it continue; } FreeLicensesToRequest.TryRemove(subId, out _); } TaskManager.Run(Save); var appids = list.Select(x => x.Value).Distinct(); AppsRequestedInHour = appids.Count(); Log.WriteDebug(nameof(FreeLicense), $"Requesting {AppsRequestedInHour} free apps as the rate limit timer ran: {string.Join(", ", appids)}"); JobManager.AddJob(() => Steam.Instance.Apps.RequestFreeLicense(appids)); if (FreeLicensesToRequest.Count > 0) { lock (FreeLicenseTimer) { FreeLicenseTimer.Start(); } } }
public static void Init() { ImportantApps = new Dictionary <uint, List <string> >(); ImportantSubs = new Dictionary <uint, byte>(); ReloadImportant(); TaskManager.RunAsync(async() => await KeyNameCache.Init()); ChangelistTimer = new Timer(); ChangelistTimer.Elapsed += Tick; ChangelistTimer.Interval = TimeSpan.FromSeconds(1).TotalMilliseconds; var thread = new Thread(Steam.Instance.Tick) { Name = "Steam" }; thread.Start(); var anonThread = new Thread(Steam.Anonymous.Tick) { Name = "SteamAnonymous" }; anonThread.Start(); Threads = new List <Thread> { thread, anonThread, }; if (Settings.IsFullRun) { return; } var commandHandler = new CommandHandler(); Steam.Instance.RegisterCommandHandlers(commandHandler); if (Settings.Current.IRC.Enabled) { RssReader = new RSS(); thread = new Thread(IRC.Instance.Connect) { Name = "IRC" }; thread.Start(); Threads.Add(thread); IRC.Instance.RegisterCommandHandlers(commandHandler); } }
public static void Init() { ImportantApps = new Dictionary <uint, List <string> >(); ImportantSubs = new Dictionary <uint, byte>(); ReloadImportant(); TaskManager.RunAsync(async() => await KeyNameCache.Init()); var thread = new Thread(Steam.Instance.Tick) { Name = "Steam" }; thread.Start(); Threads = new List <Thread> { thread, }; if (Settings.Current.BuiltInHttpServerPort > 0) { HttpServer = new HttpServer(Settings.Current.BuiltInHttpServerPort); } if (Settings.IsFullRun) { return; } var commandHandler = new CommandHandler(); Steam.Instance.RegisterCommandHandlers(commandHandler); if (Settings.Current.IRC.Enabled) { RssReader = new RSS(); thread = new Thread(IRC.Instance.Connect) { Name = "IRC" }; thread.Start(); Threads.Add(thread); IRC.Instance.RegisterCommandHandlers(commandHandler); } }
public PICSChanges(CallbackManager manager) { if (Settings.IsFullRun) { var timer = new Timer { Interval = TimeSpan.FromSeconds(10).TotalMilliseconds }; timer.Elapsed += (sender, args) => FullUpdateProcessor.IsBusy(); timer.Start(); return; } manager.Subscribe <SteamApps.PICSChangesCallback>(OnPICSChanges); using (var db = Database.Get()) { BillingTypeKey = KeyNameCache.GetSubKeyID("root_billingtype"); if (LocalConfig.Current.ChangeNumber == 0) { PreviousChangeNumber = db.ExecuteScalar <uint>("SELECT `ChangeID` FROM `Changelists` ORDER BY `ChangeID` DESC LIMIT 1"); LocalConfig.Current.ChangeNumber = PreviousChangeNumber; } else { PreviousChangeNumber = LocalConfig.Current.ChangeNumber; } Log.WriteInfo(nameof(PICSChanges), $"Previous changelist was {PreviousChangeNumber}"); } if (PreviousChangeNumber == 0) { Log.WriteWarn(nameof(PICSChanges), "Looks like there are no changelists in the database."); Log.WriteWarn(nameof(PICSChanges), $"If you want to fill up your database first, restart with \"FullRun\" setting set to {(int)FullRunState.Enumerate}."); } }
public static async Task Init() { ImportantApps = new HashSet <uint>(); ImportantSubs = new HashSet <uint>(); await ReloadImportant(); await PICSTokens.Reload(); await KeyNameCache.Init(); if (Settings.Current.BuiltInHttpServerPort > 0) { HttpServer = new HttpServer(Settings.Current.BuiltInHttpServerPort); } if (Settings.IsFullRun) { return; } var commandHandler = new CommandHandler(); Steam.Instance.RegisterCommandHandlers(commandHandler); RssReader = new RSS(); if (Settings.Current.IRC.Enabled) { IrcThread = new Thread(IRC.Instance.Connect) { Name = nameof(IRC) }; IrcThread.Start(); IRC.Instance.RegisterCommandHandlers(commandHandler); } }
public static void RequestFromPackage(uint subId, KeyValue kv) { if ((EBillingType)kv["billingtype"].AsInteger() != EBillingType.FreeOnDemand) { return; } if (kv["appids"].Children.Count == 0) { Log.WriteDebug(nameof(FreeLicense), $"Package {subId} has no apps"); return; } // TODO: Put LicenseList.OwnedApps.ContainsKey() in First() search var appId = kv["appids"].Children[0].AsUnsignedInteger(); if (LicenseList.OwnedApps.ContainsKey(appId)) { return; } if (kv["status"].AsInteger() != 0) // EPackageStatus.Available { Log.WriteDebug(nameof(FreeLicense), $"Package {subId} is not available"); return; } if ((ELicenseType)kv["licensetype"].AsInteger() != ELicenseType.SinglePurchase) { Log.WriteDebug(nameof(FreeLicense), $"Package {subId} is not single purchase"); return; } var dontGrantIfAppIdOwned = kv["extended"]["dontgrantifappidowned"].AsUnsignedInteger(); if (dontGrantIfAppIdOwned > 0 && LicenseList.OwnedApps.ContainsKey(dontGrantIfAppIdOwned)) { Log.WriteDebug(nameof(FreeLicense), $"Package {subId} already owns app {dontGrantIfAppIdOwned}"); return; } if (kv["extended"]["curatorconnect"].AsInteger() == 1) { Log.WriteDebug(nameof(FreeLicense), $"Package {subId} is a curator package"); return; } var allowPurchaseFromRestrictedCountries = kv["extended"]["allowpurchasefromrestrictedcountries"].AsBoolean(); var purchaseRestrictedCountries = kv["extended"]["purchaserestrictedcountries"].AsString(); if (purchaseRestrictedCountries != null && purchaseRestrictedCountries.Contains(AccountInfo.Country) != allowPurchaseFromRestrictedCountries) { Log.WriteDebug(nameof(FreeLicense), $"Package {subId} is not available in {AccountInfo.Country}"); return; } var startTime = kv["extended"]["starttime"].AsUnsignedLong(); var expiryTime = kv["extended"]["expirytime"].AsUnsignedLong(); var now = DateUtils.DateTimeToUnixTime(DateTime.UtcNow); if (expiryTime > 0 && expiryTime < now) { Log.WriteDebug(nameof(FreeLicense), $"Package {subId} has already expired"); return; } if (startTime > 0) { if (startTime < now) { QueueRequest(subId, appId); } else { AddToQueue(subId, appId); } Log.WriteDebug(nameof(FreeLicense), $"Package {subId} has not reached starttime yet, added to queue"); return; } uint parentAppId; bool available; using (var db = Database.Get()) { available = db.ExecuteScalar <bool>("SELECT IFNULL(`Value`, \"\") = \"released\" FROM `Apps` LEFT JOIN `AppsInfo` ON `Apps`.`AppID` = `AppsInfo`.`AppID` AND `Key` = @Key WHERE `Apps`.`AppID` = @AppID", new { Key = KeyNameCache.GetAppKeyID("common_releasestate"), AppID = appId }); parentAppId = db.ExecuteScalar <uint>("SELECT `Value` FROM `Apps` JOIN `AppsInfo` ON `Apps`.`AppID` = `AppsInfo`.`AppID` WHERE `Key` = @Key AND `Apps`.`AppID` = @AppID AND `AppType` NOT IN (3, 15)", new { Key = KeyNameCache.GetAppKeyID("common_parent"), AppID = appId }); } if (!available) { Log.WriteDebug(nameof(FreeLicense), $"Package {subId} (app {appId}) did not pass release check"); return; } if (parentAppId > 0 && !LicenseList.OwnedApps.ContainsKey(parentAppId)) { Log.WriteDebug(nameof(FreeLicense), $"Parent app {parentAppId} is not owned to get {appId}"); return; } Log.WriteDebug(nameof(FreeLicense), $"Requesting apps in package {subId}"); QueueRequest(subId, appId); }
private async Task ProcessKey(string keyName, string displayName, string value, bool isJSON = false) { if (keyName.Length > 90) { Log.WriteError(nameof(SubProcessor), $"Key {keyName} for SubID {SubID} is too long, not inserting info."); return; } // All keys in PICS are supposed to be lower case. // But currently some keys in packages are not lowercased, // this lowercases everything to make sure nothing breaks in future keyName = keyName.ToLowerInvariant().Trim(); if (!CurrentData.ContainsKey(keyName)) { CurrentData[keyName] = new PICSInfo { Processed = true, }; var key = KeyNameCache.GetSubKeyID(keyName); if (key == 0) { var type = isJSON ? 86 : 0; // 86 is a hardcoded const for the website key = await KeyNameCache.CreateSubKey(keyName, displayName, type); if (key == 0) { // We can't insert anything because key wasn't created Log.WriteError(nameof(SubProcessor), $"Failed to create key {keyName} for SubID {SubID}, not inserting info."); return; } IRC.Instance.SendOps($"New package keyname: {Colors.BLUE}{keyName} {Colors.LIGHTGRAY}(ID: {key}) ({displayName}) - {SteamDB.GetPackageUrl(SubID, "history")}"); } await DbConnection.ExecuteAsync("INSERT INTO `SubsInfo` (`SubID`, `Key`, `Value`) VALUES (@SubID, @Key, @Value)", new { SubID, Key = key, Value = value }); await MakeHistory("created_key", key, string.Empty, value); return; } var data = CurrentData[keyName]; if (data.Processed) { Log.WriteWarn(nameof(SubProcessor), $"Duplicate key {keyName} in SubID {SubID}"); return; } data.Processed = true; CurrentData[keyName] = data; if (data.Value == value) { return; } await DbConnection.ExecuteAsync("UPDATE `SubsInfo` SET `Value` = @Value WHERE `SubID` = @SubID AND `Key` = @Key", new { SubID, data.Key, Value = value }); await MakeHistory("modified_key", data.Key, data.Value, value); }
public static async Task HandleMetadataInfo(SteamApps.PICSProductInfoCallback callback) { var apps = new List <uint>(); var subs = new List <uint>(); await using var db = await Database.GetConnectionAsync(); if (callback.Apps.Any()) { Log.WriteDebug(nameof(FullUpdateProcessor), $"Received metadata only product info for {callback.Apps.Count} apps ({callback.Apps.First().Key}...{callback.Apps.Last().Key}), job: {callback.JobID}"); var currentChangeNumbers = (await db.QueryAsync <(uint, uint)>( "SELECT `AppID`, `Value` FROM `AppsInfo` WHERE `Key` = @ChangeNumberKey AND `AppID` IN @Apps", new { ChangeNumberKey = KeyNameCache.GetAppKeyID("root_changenumber"), Apps = callback.Apps.Keys } )).ToDictionary(x => x.Item1, x => x.Item2); foreach (var app in callback.Apps.Values) { currentChangeNumbers.TryGetValue(app.ID, out var currentChangeNumber); if (currentChangeNumber == app.ChangeNumber) { continue; } Log.WriteInfo(nameof(FullUpdateProcessor), $"App {app.ID} - Change: {currentChangeNumber} -> {app.ChangeNumber}"); apps.Add(app.ID); if (!Settings.IsFullRun) { await db.ExecuteAsync("INSERT INTO `Changelists` (`ChangeID`) VALUES (@ChangeNumber) ON DUPLICATE KEY UPDATE `Date` = `Date`", new { app.ChangeNumber }); await db.ExecuteAsync("INSERT INTO `ChangelistsApps` (`ChangeID`, `AppID`) VALUES (@ChangeNumber, @AppID) ON DUPLICATE KEY UPDATE `AppID` = `AppID`", new { AppID = app.ID, app.ChangeNumber }); } } } if (callback.Packages.Any()) { Log.WriteDebug(nameof(FullUpdateProcessor), $"Received metadata only product info for {callback.Packages.Count} packages ({callback.Packages.First().Key}...{callback.Packages.Last().Key}), job: {callback.JobID}"); var currentChangeNumbers = (await db.QueryAsync <(uint, uint)>( "SELECT `SubID`, `Value` FROM `SubsInfo` WHERE `Key` = @ChangeNumberKey AND `SubID` IN @Subs", new { ChangeNumberKey = KeyNameCache.GetSubKeyID("root_changenumber"), Subs = callback.Packages.Keys } )).ToDictionary(x => x.Item1, x => x.Item2); foreach (var sub in callback.Packages.Values) { currentChangeNumbers.TryGetValue(sub.ID, out var currentChangeNumber); if (currentChangeNumber == sub.ChangeNumber) { continue; } Log.WriteInfo(nameof(FullUpdateProcessor), $"Package {sub.ID} - Change: {currentChangeNumber} -> {sub.ChangeNumber}"); subs.Add(sub.ID); if (!Settings.IsFullRun) { await db.ExecuteAsync("INSERT INTO `Changelists` (`ChangeID`) VALUES (@ChangeNumber) ON DUPLICATE KEY UPDATE `Date` = `Date`", new { sub.ChangeNumber }); await db.ExecuteAsync("INSERT INTO `ChangelistsSubs` (`ChangeID`, `SubID`) VALUES (@ChangeNumber, @SubID) ON DUPLICATE KEY UPDATE `SubID` = `SubID`", new { SubID = sub.ID, sub.ChangeNumber }); } } } if (apps.Any() || subs.Any()) { JobManager.AddJob( () => Steam.Instance.Apps.PICSGetAccessTokens(apps, subs), new PICSTokens.RequestedTokens { Apps = apps, Packages = subs, }); } }
private async Task <bool> ProcessKey(string keyName, string displayName, string value, KeyValue newKv = null) { if (keyName.Length > 90) { Log.WriteError("App Processor", "Key {0} for AppID {1} is too long, not inserting info.", keyName, AppID); return(false); } // All keys in PICS are supposed to be lower case keyName = keyName.ToLowerInvariant().Trim(); if (!CurrentData.ContainsKey(keyName)) { var key = KeyNameCache.GetAppKeyID(keyName); if (key == 0) { var type = newKv != null ? 86 : 0; // 86 is a hardcoded const for the website key = await KeyNameCache.CreateAppKey(keyName, displayName, type); if (key == 0) { // We can't insert anything because key wasn't created Log.WriteError("App Processor", "Failed to create key {0} for AppID {1}, not inserting info.", keyName, AppID); return(false); } IRC.Instance.SendOps("New app keyname: {0}{1} {2}(ID: {3}) ({4}) - {5}", Colors.BLUE, keyName, Colors.LIGHTGRAY, key, displayName, SteamDB.GetAppURL(AppID, "history")); } await DbConnection.ExecuteAsync("INSERT INTO `AppsInfo` (`AppID`, `Key`, `Value`) VALUES (@AppID, @Key, @Value)", new { AppID, Key = key, Value = value }); await MakeHistory("created_key", key, string.Empty, value); if ((keyName == "extended_developer" || keyName == "extended_publisher") && value == "Valve") { IRC.Instance.SendOps("New {0}=Valve app: {1}{2}{3} -{4} {5}", displayName, Colors.BLUE, Steam.GetAppName(AppID), Colors.NORMAL, Colors.DARKBLUE, SteamDB.GetAppURL(AppID, "history")); } if (keyName == "common_oslist" && value.Contains("linux")) { PrintLinux(); } return(true); } var data = CurrentData[keyName]; if (data.Processed) { Log.WriteWarn("App Processor", "Duplicate key {0} in AppID {1}", keyName, AppID); return(false); } data.Processed = true; CurrentData[keyName] = data; if (data.Value == value) { return(false); } await DbConnection.ExecuteAsync("UPDATE `AppsInfo` SET `Value` = @Value WHERE `AppID` = @AppID AND `Key` = @Key", new { AppID, data.Key, Value = value }); if (newKv != null) { await MakeHistoryForJson(data.Key, data.Value, newKv); } else { await MakeHistory("modified_key", data.Key, data.Value, value); } if (keyName == "common_oslist" && value.Contains("linux") && !data.Value.Contains("linux")) { PrintLinux(); } return(true); }
private async Task ProcessDlcNames(uint parentAppId, uint changeNumber, Dictionary <uint, string> dlcNames) { // TODO: Track name changes? await using var db = await Database.GetConnectionAsync(); var dlcApps = await db.QueryAsync <App>("SELECT `AppID` FROM `Apps` WHERE `AppID` IN @Ids AND `LastKnownName` = \"\"", new { Ids = dlcNames.Keys }); var parentKey = KeyNameCache.GetAppKeyID("common_parent"); foreach (var dlcApp in dlcApps) { Log.WriteInfo(nameof(DepotProcessor), $"Got a name for app {dlcApp.AppID} from parent app {parentAppId}"); var name = dlcNames[dlcApp.AppID]; name = DepotNameStart.Replace(name, string.Empty); name = DepotNameEnd.Replace(name, string.Empty); var dlcAppIdEnding = $" ({dlcApp.AppID})"; if (name.EndsWith(dlcAppIdEnding)) { name = name.Substring(0, name.Length - dlcAppIdEnding.Length); } name = name.Trim(); await db.ExecuteAsync("UPDATE `Apps` SET `LastKnownName` = @AppName WHERE `AppID` = @AppID", new { dlcApp.AppID, AppName = name, }); await db.ExecuteAsync("INSERT INTO `AppsInfo` (`AppID`, `Key`, `Value`) VALUES (@AppID, @Key, @Value) ON DUPLICATE KEY UPDATE `AppID` = `AppID`", new { dlcApp.AppID, Key = parentKey, Value = parentAppId.ToString(), }); await db.ExecuteAsync(AppProcessor.HistoryQuery, new PICSHistory { ID = dlcApp.AppID, ChangeID = changeNumber, Key = SteamDB.DatabaseNameType, NewValue = name, OldValue = "Depot name", Action = "created_info", } ); await db.ExecuteAsync(AppProcessor.HistoryQuery, new PICSHistory { ID = dlcApp.AppID, ChangeID = changeNumber, Key = parentKey, NewValue = parentAppId.ToString(), Action = "created_key", } ); } }