public void Process(SteamApps.PICSProductInfoCallback.PICSProductInfo productInfo) { ChangeNumber = productInfo.ChangeNumber; if (Settings.IsFullRun) { Log.WriteDebug("App Processor", "AppID: {0}", AppID); DbConnection.Execute("INSERT INTO `Changelists` (`ChangeID`) VALUES (@ChangeNumber) ON DUPLICATE KEY UPDATE `Date` = `Date`", new { productInfo.ChangeNumber }); DbConnection.Execute("INSERT INTO `ChangelistsApps` (`ChangeID`, `AppID`) VALUES (@ChangeNumber, @AppID) ON DUPLICATE KEY UPDATE `AppID` = `AppID`", new { AppID, productInfo.ChangeNumber }); } var app = DbConnection.Query <App>("SELECT `Name`, `AppType` FROM `Apps` WHERE `AppID` = @AppID LIMIT 1", new { AppID }).SingleOrDefault(); var newAppName = productInfo.KeyValues["common"]["name"].AsString(); if (newAppName != null) { int newAppType = -1; string currentType = productInfo.KeyValues["common"]["type"].AsString().ToLower(); using (var reader = DbConnection.ExecuteReader("SELECT `AppType` FROM `AppsTypes` WHERE `Name` = @Type LIMIT 1", new { Type = currentType })) { if (reader.Read()) { newAppType = reader.GetInt32(reader.GetOrdinal("AppType")); } } if (newAppType == -1) { DbConnection.Execute("INSERT INTO `AppsTypes` (`Name`, `DisplayName`) VALUES(@Name, @DisplayName)", new { Name = currentType, DisplayName = productInfo.KeyValues["common"]["type"].AsString() }); // We don't need to lower display name Log.WriteInfo("App Processor", "Creating new apptype \"{0}\" (AppID {1})", currentType, AppID); IRC.Instance.SendOps("New app type: {0}{1}{2} - {3}", Colors.BLUE, currentType, Colors.NORMAL, SteamDB.GetAppURL(AppID, "history")); newAppType = DbConnection.ExecuteScalar <int>("SELECT `AppType` FROM `AppsTypes` WHERE `Name` = @Type LIMIT 1", new { Type = currentType }); } if (string.IsNullOrEmpty(app.Name) || app.Name.StartsWith(SteamDB.UNKNOWN_APP, StringComparison.Ordinal)) { DbConnection.Execute("INSERT INTO `Apps` (`AppID`, `AppType`, `Name`, `LastKnownName`) VALUES (@AppID, @Type, @AppName, @AppName) ON DUPLICATE KEY UPDATE `Name` = VALUES(`Name`), `LastKnownName` = VALUES(`LastKnownName`), `AppType` = VALUES(`AppType`)", new { AppID, Type = newAppType, AppName = newAppName } ); MakeHistory("created_app"); MakeHistory("created_info", SteamDB.DATABASE_NAME_TYPE, string.Empty, newAppName); // TODO: Testy testy if (!Settings.IsFullRun && Settings.Current.ChatRooms.Count > 0) { Steam.Instance.Friends.SendChatRoomMessage(Settings.Current.ChatRooms[0], EChatEntryType.ChatMsg, string.Format( "New {0} was published: {1}\nSteamDB: {2}\nSteam: https://store.steampowered.com/app/{3}/", currentType, newAppName, SteamDB.GetAppURL(AppID), AppID ) ); } if ((newAppType > 9 && newAppType != 13) || Triggers.Any(newAppName.Contains)) { IRC.Instance.SendOps("New {0}: {1}{2}{3} -{4} {5}", currentType, Colors.BLUE, newAppName, Colors.NORMAL, Colors.DARKBLUE, SteamDB.GetAppURL(AppID, "history")); } } else if (!app.Name.Equals(newAppName)) { DbConnection.Execute("UPDATE `Apps` SET `Name` = @AppName, `LastKnownName` = @AppName WHERE `AppID` = @AppID", new { AppID, AppName = newAppName }); MakeHistory("modified_info", SteamDB.DATABASE_NAME_TYPE, app.Name, newAppName); } if (app.AppType == 0 || app.AppType != newAppType) { DbConnection.Execute("UPDATE `Apps` SET `AppType` = @Type WHERE `AppID` = @AppID", new { AppID, Type = newAppType }); if (app.AppType == 0) { MakeHistory("created_info", SteamDB.DATABASE_APPTYPE, string.Empty, newAppType.ToString()); } else { MakeHistory("modified_info", SteamDB.DATABASE_APPTYPE, app.AppType.ToString(), newAppType.ToString()); } } } foreach (var section in productInfo.KeyValues.Children) { string sectionName = section.Name.ToLower(); if (sectionName == "appid" || sectionName == "public_only") { continue; } if (sectionName == "change_number") // Carefully handle change_number { sectionName = "root_change_number"; // TODO: Remove this key, move it to Apps table itself ProcessKey(sectionName, "change_number", productInfo.ChangeNumber.ToString()); //section.AsString()); } else if (sectionName == "common" || sectionName == "extended") { string keyName; foreach (KeyValue keyvalue in section.Children) { keyName = string.Format("{0}_{1}", sectionName, keyvalue.Name); if (keyName.Equals("common_type") || keyName.Equals("common_gameid") || keyName.Equals("common_name") || keyName.Equals("extended_order")) { // Ignore common keys that are either duplicated or serve no real purpose continue; } if (keyvalue.Children.Count > 0) { ProcessKey(keyName, keyvalue.Name, Utils.JsonifyKeyValue(keyvalue), true); } else if (!string.IsNullOrEmpty(keyvalue.Value)) { ProcessKey(keyName, keyvalue.Name, keyvalue.Value); } } } else { sectionName = string.Format("root_{0}", sectionName); if (ProcessKey(sectionName, sectionName, Utils.JsonifyKeyValue(section), true) && sectionName.Equals("root_depots")) { DbConnection.Execute("UPDATE `Apps` SET `LastDepotUpdate` = CURRENT_TIMESTAMP() WHERE `AppID` = @AppID", new { AppID }); } } } foreach (var data in CurrentData.Values) { if (!data.Processed && !data.KeyName.StartsWith("website", StringComparison.Ordinal)) { DbConnection.Execute("DELETE FROM `AppsInfo` WHERE `AppID` = @AppID AND `Key` = @Key", new { AppID, data.Key }); MakeHistory("removed_key", data.Key, data.Value); if (newAppName != null && data.Key.Equals("common_section_type") && data.Value.Equals("ownersonly")) { IRC.Instance.SendMain("Removed ownersonly from: {0}{1}{2} -{3} {4}", Colors.BLUE, app.Name, Colors.NORMAL, Colors.DARKBLUE, SteamDB.GetAppURL(AppID, "history")); } } } if (newAppName == null) { if (string.IsNullOrEmpty(app.Name)) // We don't have the app in our database yet { DbConnection.Execute("INSERT INTO `Apps` (`AppID`, `Name`) VALUES (@AppID, @AppName) ON DUPLICATE KEY UPDATE `AppType` = `AppType`", new { AppID, AppName = string.Format("{0} {1}", SteamDB.UNKNOWN_APP, AppID) }); } else if (!app.Name.StartsWith(SteamDB.UNKNOWN_APP, StringComparison.Ordinal)) // We do have the app, replace it with default name { IRC.Instance.SendMain("App deleted: {0}{1}{2} -{3} {4}", Colors.BLUE, app.Name, Colors.NORMAL, Colors.DARKBLUE, SteamDB.GetAppURL(AppID, "history")); DbConnection.Execute("UPDATE `Apps` SET `Name` = @AppName, `AppType` = 0 WHERE `AppID` = @AppID", new { AppID, AppName = string.Format("{0} {1}", SteamDB.UNKNOWN_APP, AppID) }); MakeHistory("deleted_app", 0, app.Name); } } if (productInfo.KeyValues["depots"] != null) { Steam.Instance.DepotProcessor.Process(AppID, ChangeNumber, productInfo.KeyValues["depots"]); } }
protected override async Task ProcessData() { await LoadData(); ChangeNumber = ProductInfo.ChangeNumber; if (Settings.IsFullRun) { await DbConnection.ExecuteAsync("INSERT INTO `Changelists` (`ChangeID`) VALUES (@ChangeNumber) ON DUPLICATE KEY UPDATE `Date` = `Date`", new { ProductInfo.ChangeNumber }); await DbConnection.ExecuteAsync("INSERT INTO `ChangelistsSubs` (`ChangeID`, `SubID`) VALUES (@ChangeNumber, @SubID) ON DUPLICATE KEY UPDATE `SubID` = `SubID`", new { SubID, ProductInfo.ChangeNumber }); } await ProcessKey("root_changenumber", "changenumber", ChangeNumber.ToString()); var appAddedToThisPackage = false; var hasPackageInfo = ProductInfo.KeyValues.Children.Count > 0; var packageOwned = LicenseList.OwnedSubs.ContainsKey(SubID); var newPackageName = ProductInfo.KeyValues["name"].AsString() ?? string.Concat("Steam Sub ", SubID); var apps = (await DbConnection.QueryAsync <PackageApp>("SELECT `AppID`, `Type` FROM `SubsApps` WHERE `SubID` = @SubID", new { SubID })).ToDictionary(x => x.AppID, x => x.Type); var alreadySeenAppIds = new HashSet <uint>(); if (!hasPackageInfo) { ProductInfo.KeyValues.Children.Add(new KeyValue("steamdb_requires_token", "1")); } if (string.IsNullOrEmpty(PackageName)) { await DbConnection.ExecuteAsync("INSERT INTO `Subs` (`SubID`, `Name`, `LastKnownName`) VALUES (@SubID, @Name, @Name)", new { SubID, Name = newPackageName }); await MakeHistory("created_sub"); await MakeHistory("created_info", SteamDB.DatabaseNameType, string.Empty, newPackageName); } else if (PackageName != newPackageName) { if (newPackageName.StartsWith("Steam Sub ", StringComparison.Ordinal)) { await DbConnection.ExecuteAsync("UPDATE `Subs` SET `Name` = @Name WHERE `SubID` = @SubID", new { SubID, Name = newPackageName }); } else { await DbConnection.ExecuteAsync("UPDATE `Subs` SET `Name` = @Name, `LastKnownName` = @Name WHERE `SubID` = @SubID", new { SubID, Name = newPackageName }); } await MakeHistory("modified_info", SteamDB.DatabaseNameType, PackageName, newPackageName); } foreach (var section in ProductInfo.KeyValues.Children) { var sectionName = section.Name.ToLowerInvariant(); if (string.IsNullOrEmpty(sectionName) || sectionName == "packageid" || sectionName == "changenumber" || sectionName == "name") { // Ignore common keys continue; } if (sectionName == "appids" || sectionName == "depotids") { // Remove "ids", so we get "app" from appids and "depot" from depotids var type = sectionName.Replace("ids", string.Empty); var isAppSection = type == "app"; var typeID = (uint)(isAppSection ? 0 : 1); // 0 = app, 1 = depot; can't store as string because it's in the `key` field foreach (var childrenApp in section.Children) { if (!uint.TryParse(childrenApp.Value, out var appID)) { Log.WriteWarn(nameof(SubProcessor), $"Package {SubID} has an invalid uint: {childrenApp.Value}"); continue; } if (alreadySeenAppIds.Contains(appID)) { Log.WriteWarn(nameof(SubProcessor), $"Package {SubID} has a duplicate app: {appID}"); continue; } alreadySeenAppIds.Add(appID); // Is this appid already in this package? if (apps.ContainsKey(appID)) { // Is this appid's type the same? if (apps[appID] != type) { await DbConnection.ExecuteAsync("UPDATE `SubsApps` SET `Type` = @Type WHERE `SubID` = @SubID AND `AppID` = @AppID", new { SubID, AppID = appID, Type = type }); await MakeHistory("added_to_sub", typeID, apps[appID] == "app"? "0" : "1", childrenApp.Value); appAddedToThisPackage = true; // Log relevant add/remove history events for depot and app var appHistory = new PICSHistory { ID = appID, ChangeID = ChangeNumber, }; if (isAppSection) { appHistory.NewValue = SubID.ToString(); appHistory.Action = "added_to_sub"; } else { appHistory.OldValue = SubID.ToString(); appHistory.Action = "removed_from_sub"; } await DbConnection.ExecuteAsync(AppProcessor.HistoryQuery, appHistory); var depotHistory = new DepotHistory { DepotID = appID, ManifestID = 0, ChangeID = ChangeNumber, OldValue = SubID, Action = isAppSection ? "removed_from_sub" : "added_to_sub" }; if (isAppSection) { depotHistory.OldValue = SubID; depotHistory.Action = "removed_from_sub"; } else { depotHistory.NewValue = SubID; depotHistory.Action = "added_to_sub"; } await DbConnection.ExecuteAsync(DepotProcessor.HistoryQuery, depotHistory); } apps.Remove(appID); } else { await DbConnection.ExecuteAsync("INSERT INTO `SubsApps` (`SubID`, `AppID`, `Type`) VALUES(@SubID, @AppID, @Type)", new { SubID, AppID = appID, Type = type }); await MakeHistory("added_to_sub", typeID, string.Empty, childrenApp.Value); if (isAppSection) { await DbConnection.ExecuteAsync(AppProcessor.HistoryQuery, new PICSHistory { ID = appID, ChangeID = ChangeNumber, NewValue = SubID.ToString(), Action = "added_to_sub" } ); } else { await DbConnection.ExecuteAsync(DepotProcessor.HistoryQuery, new DepotHistory { DepotID = appID, ManifestID = 0, ChangeID = ChangeNumber, NewValue = SubID, Action = "added_to_sub" } ); } appAddedToThisPackage = true; if (packageOwned && !LicenseList.OwnedApps.ContainsKey(appID)) { LicenseList.OwnedApps.Add(appID, 1); } } } } else if (sectionName == "extended") { foreach (var children in section.Children) { var keyName = $"{sectionName}_{children.Name}"; if (children.Children.Count > 0) { await ProcessKey(keyName, children.Name, Utils.JsonifyKeyValue(children), true); } else { await ProcessKey(keyName, children.Name, children.Value); } } } else if (sectionName == "appitems" && section.Children.Count > 1) { sectionName = $"root_{sectionName}"; var fixedAppItems = new KeyValue(section.Name); // Valve for some reason creates a new children for each item, // instead of actually making it an array. // This causes json_decode in php override the key, thus lose data. foreach (var item in section.Children) { var appItem = fixedAppItems.Children.Find(s => s.Name == item.Name); if (appItem == default) { appItem = new KeyValue(item.Name); fixedAppItems.Children.Add(appItem); } foreach (var itemId in item.Children) { appItem.Children.Add(new KeyValue(itemId.Name, itemId.Value)); } } await ProcessKey(sectionName, sectionName, Utils.JsonifyKeyValue(fixedAppItems), true); } else if (section.Children.Count > 0) { sectionName = $"root_{sectionName}"; await ProcessKey(sectionName, sectionName, Utils.JsonifyKeyValue(section), true); } else if (!string.IsNullOrEmpty(section.Value)) { var keyName = $"root_{sectionName}"; await ProcessKey(keyName, sectionName, section.Value); } } // If this package no longer returns any package info, keep the existing info we have if (hasPackageInfo) { foreach (var data in CurrentData.Values.Where(data => !data.Processed && !data.KeyName.StartsWith("website", StringComparison.Ordinal))) { await DbConnection.ExecuteAsync("DELETE FROM `SubsInfo` WHERE `SubID` = @SubID AND `Key` = @Key", new { SubID, data.Key }); await MakeHistory("removed_key", data.Key, data.Value); } var appsRemoved = apps.Count > 0; foreach (var(appid, type) in apps) { await DbConnection.ExecuteAsync("DELETE FROM `SubsApps` WHERE `SubID` = @SubID AND `AppID` = @AppID AND `Type` = @Type", new { SubID, AppID = appid, Type = type }); var isAppSection = type == "app"; var typeID = (uint)(isAppSection ? 0 : 1); // 0 = app, 1 = depot; can't store as string because it's in the `key` field await MakeHistory("removed_from_sub", typeID, appid.ToString()); if (isAppSection) { await DbConnection.ExecuteAsync(AppProcessor.HistoryQuery, new PICSHistory { ID = appid, ChangeID = ChangeNumber, OldValue = SubID.ToString(), Action = "removed_from_sub" } ); } else { await DbConnection.ExecuteAsync(DepotProcessor.HistoryQuery, new DepotHistory { DepotID = appid, ManifestID = 0, ChangeID = ChangeNumber, OldValue = SubID, Action = "removed_from_sub" } ); } } if (appsRemoved) { LicenseList.RefreshApps(); } if (!packageOwned && SubID != 17906 && Settings.IsMillhaven) { Steam.Instance.FreeLicense.RequestFromPackage(SubID, ProductInfo.KeyValues); } } // Re-queue apps in this package so we can update depots and whatnot if (appAddedToThisPackage && !Settings.IsFullRun && !string.IsNullOrEmpty(PackageName)) { var appsToRequest = ProductInfo.KeyValues["appids"].Children.Select(x => (uint)x.AsInteger()).ToList(); JobManager.AddJob( () => Steam.Instance.Apps.PICSGetAccessTokens(appsToRequest, Enumerable.Empty <uint>()), new PICSTokens.RequestedTokens { Apps = appsToRequest }); } if (ProductInfo.MissingToken && PICSTokens.HasPackageToken(SubID)) { Log.WriteError(nameof(PICSTokens), $"Overridden token for subid {SubID} is invalid?"); IRC.Instance.SendOps($"[Tokens] Looks like the overridden token for subid {SubID} ({newPackageName}) is invalid"); } }
protected override async Task ProcessData() { await LoadData(); ChangeNumber = ProductInfo.ChangeNumber; if (Settings.IsFullRun) { await DbConnection.ExecuteAsync("INSERT INTO `Changelists` (`ChangeID`) VALUES (@ChangeNumber) ON DUPLICATE KEY UPDATE `Date` = `Date`", new { ProductInfo.ChangeNumber }); await DbConnection.ExecuteAsync("INSERT INTO `ChangelistsSubs` (`ChangeID`, `SubID`) VALUES (@ChangeNumber, @SubID) ON DUPLICATE KEY UPDATE `SubID` = `SubID`", new { SubID, ProductInfo.ChangeNumber }); } await ProcessKey("root_changenumber", "changenumber", ChangeNumber.ToString()); var appAddedToThisPackage = false; var packageOwned = LicenseList.OwnedSubs.ContainsKey(SubID); var newPackageName = ProductInfo.KeyValues["name"].AsString(); var apps = (await DbConnection.QueryAsync <PackageApp>("SELECT `AppID`, `Type` FROM `SubsApps` WHERE `SubID` = @SubID", new { SubID })).ToDictionary(x => x.AppID, x => x.Type); // TODO: Ideally this should be SteamDB Unknown Package and proper checks like app processor does if (newPackageName == null) { newPackageName = string.Concat("Steam Sub ", SubID); } if (newPackageName != null) { if (string.IsNullOrEmpty(PackageName)) { await DbConnection.ExecuteAsync("INSERT INTO `Subs` (`SubID`, `Name`, `LastKnownName`) VALUES (@SubID, @Name, @Name)", new { SubID, Name = newPackageName }); await MakeHistory("created_sub"); await MakeHistory("created_info", SteamDB.DATABASE_NAME_TYPE, string.Empty, newPackageName); } else if (!PackageName.Equals(newPackageName)) { if (newPackageName.StartsWith("Steam Sub ", StringComparison.Ordinal)) { await DbConnection.ExecuteAsync("UPDATE `Subs` SET `Name` = @Name WHERE `SubID` = @SubID", new { SubID, Name = newPackageName }); } else { await DbConnection.ExecuteAsync("UPDATE `Subs` SET `Name` = @Name, `LastKnownName` = @Name WHERE `SubID` = @SubID", new { SubID, Name = newPackageName }); } await MakeHistory("modified_info", SteamDB.DATABASE_NAME_TYPE, PackageName, newPackageName); } } foreach (var section in ProductInfo.KeyValues.Children) { var sectionName = section.Name.ToLower(); if (string.IsNullOrEmpty(sectionName) || sectionName.Equals("packageid") || sectionName.Equals("changenumber") || sectionName.Equals("name")) { // Ignore common keys continue; } if (sectionName.Equals("appids") || sectionName.Equals("depotids")) { // Remove "ids", so we get "app" from appids and "depot" from depotids var type = sectionName.Replace("ids", string.Empty); var isAppSection = type.Equals("app"); var typeID = (uint)(isAppSection ? 0 : 1); // 0 = app, 1 = depot; can't store as string because it's in the `key` field foreach (var childrenApp in section.Children) { var appID = uint.Parse(childrenApp.Value); // Is this appid already in this package? if (apps.ContainsKey(appID)) { // Is this appid's type the same? if (apps[appID] != type) { await DbConnection.ExecuteAsync("UPDATE `SubsApps` SET `Type` = @Type WHERE `SubID` = @SubID AND `AppID` = @AppID", new { SubID, AppID = appID, Type = type }); await MakeHistory("added_to_sub", typeID, apps[appID].Equals("app")? "0" : "1", childrenApp.Value); appAddedToThisPackage = true; // TODO: Log relevant add/remove history for depot/app? } apps.Remove(appID); } else { await DbConnection.ExecuteAsync("INSERT INTO `SubsApps` (`SubID`, `AppID`, `Type`) VALUES(@SubID, @AppID, @Type)", new { SubID, AppID = appID, Type = type }); await MakeHistory("added_to_sub", typeID, string.Empty, childrenApp.Value); if (isAppSection) { await DbConnection.ExecuteAsync(AppProcessor.HistoryQuery, new PICSHistory { ID = appID, ChangeID = ChangeNumber, NewValue = SubID.ToString(), Action = "added_to_sub" } ); } else { await DbConnection.ExecuteAsync(DepotProcessor.HistoryQuery, new DepotHistory { DepotID = appID, ChangeID = ChangeNumber, NewValue = SubID, Action = "added_to_sub" } ); } appAddedToThisPackage = true; if (packageOwned && !LicenseList.OwnedApps.ContainsKey(appID)) { LicenseList.OwnedApps.Add(appID, 1); } } } } else if (sectionName.Equals("extended")) { foreach (var children in section.Children) { var keyName = string.Format("{0}_{1}", sectionName, children.Name); if (children.Children.Count > 0) { await ProcessKey(keyName, children.Name, Utils.JsonifyKeyValue(children), true); } else { await ProcessKey(keyName, children.Name, children.Value); } } } else if (section.Children.Any()) { sectionName = string.Format("root_{0}", sectionName); await ProcessKey(sectionName, sectionName, Utils.JsonifyKeyValue(section), true); } else if (!string.IsNullOrEmpty(section.Value)) { var keyName = string.Format("root_{0}", sectionName); await ProcessKey(keyName, sectionName, section.Value); } } foreach (var data in CurrentData.Values.Where(data => !data.Processed && !data.KeyName.StartsWith("website", StringComparison.Ordinal))) { await DbConnection.ExecuteAsync("DELETE FROM `SubsInfo` WHERE `SubID` = @SubID AND `Key` = @Key", new { SubID, data.Key }); await MakeHistory("removed_key", data.Key, data.Value); } var appsRemoved = apps.Any(); foreach (var app in apps) { await DbConnection.ExecuteAsync("DELETE FROM `SubsApps` WHERE `SubID` = @SubID AND `AppID` = @AppID AND `Type` = @Type", new { SubID, AppID = app.Key, Type = app.Value }); var isAppSection = app.Value.Equals("app"); var typeID = (uint)(isAppSection ? 0 : 1); // 0 = app, 1 = depot; can't store as string because it's in the `key` field await MakeHistory("removed_from_sub", typeID, app.Key.ToString()); if (isAppSection) { await DbConnection.ExecuteAsync(AppProcessor.HistoryQuery, new PICSHistory { ID = app.Key, ChangeID = ChangeNumber, OldValue = SubID.ToString(), Action = "removed_from_sub" } ); } else { await DbConnection.ExecuteAsync(DepotProcessor.HistoryQuery, new DepotHistory { DepotID = app.Key, ChangeID = ChangeNumber, OldValue = SubID, Action = "removed_from_sub" } ); } } if (appsRemoved) { LicenseList.RefreshApps(); } if (!packageOwned && SubID != 17906) { FreeLicense.RequestFromPackage(SubID, ProductInfo.KeyValues); } // Re-queue apps in this package so we can update depots and whatnot if (appAddedToThisPackage && !Settings.IsFullRun && !string.IsNullOrEmpty(PackageName)) { JobManager.AddJob(() => Steam.Instance.Apps.PICSGetAccessTokens(ProductInfo.KeyValues["appids"].Children.Select(x => (uint)x.AsInteger()), Enumerable.Empty <uint>())); } // Maintain a list of anonymous content if (appAddedToThisPackage && SubID == 17906) { LicenseList.RefreshAnonymous(); } }
protected override async Task ProcessData() { ChangeNumber = ProductInfo.ChangeNumber; if (Settings.IsFullRun) { await DbConnection.ExecuteAsync("INSERT INTO `Changelists` (`ChangeID`) VALUES (@ChangeNumber) ON DUPLICATE KEY UPDATE `Date` = `Date`", new { ProductInfo.ChangeNumber }); await DbConnection.ExecuteAsync("INSERT INTO `ChangelistsApps` (`ChangeID`, `AppID`) VALUES (@ChangeNumber, @AppID) ON DUPLICATE KEY UPDATE `AppID` = `AppID`", new { AppID, ProductInfo.ChangeNumber }); } await ProcessKey("root_changenumber", "changenumber", ChangeNumber.ToString()); var app = (await DbConnection.QueryAsync <App>("SELECT `Name`, `AppType` FROM `Apps` WHERE `AppID` = @AppID LIMIT 1", new { AppID })).SingleOrDefault(); var newAppName = ProductInfo.KeyValues["common"]["name"].AsString(); if (newAppName != null) { var currentType = ProductInfo.KeyValues["common"]["type"].AsString().ToLowerInvariant(); var newAppType = await DbConnection.ExecuteScalarAsync <int?>("SELECT `AppType` FROM `AppsTypes` WHERE `Name` = @Type LIMIT 1", new { Type = currentType }) ?? -1; var modifiedNameOrType = false; if (newAppType == -1) { await DbConnection.ExecuteAsync("INSERT INTO `AppsTypes` (`Name`, `DisplayName`) VALUES(@Name, @DisplayName)", new { Name = currentType, DisplayName = ProductInfo.KeyValues["common"]["type"].AsString() }); // We don't need to lower display name Log.WriteInfo("App Processor", "Creating new apptype \"{0}\" (AppID {1})", currentType, AppID); IRC.Instance.SendOps("New app type: {0}{1}{2} - {3}", Colors.BLUE, currentType, Colors.NORMAL, SteamDB.GetAppURL(AppID, "history")); newAppType = await DbConnection.ExecuteScalarAsync <int>("SELECT `AppType` FROM `AppsTypes` WHERE `Name` = @Type LIMIT 1", new { Type = currentType }); } if (string.IsNullOrEmpty(app.Name) || app.Name.StartsWith(SteamDB.UNKNOWN_APP, StringComparison.Ordinal)) { await DbConnection.ExecuteAsync("INSERT INTO `Apps` (`AppID`, `AppType`, `Name`, `LastKnownName`) VALUES (@AppID, @Type, @AppName, @AppName) ON DUPLICATE KEY UPDATE `Name` = VALUES(`Name`), `LastKnownName` = VALUES(`LastKnownName`), `AppType` = VALUES(`AppType`)", new { AppID, Type = newAppType, AppName = newAppName } ); await MakeHistory("created_app"); await MakeHistory("created_info", SteamDB.DATABASE_NAME_TYPE, string.Empty, newAppName); modifiedNameOrType = true; } else if (app.Name != newAppName) { await DbConnection.ExecuteAsync("UPDATE `Apps` SET `Name` = @AppName, `LastKnownName` = @AppName WHERE `AppID` = @AppID", new { AppID, AppName = newAppName }); await MakeHistory("modified_info", SteamDB.DATABASE_NAME_TYPE, app.Name, newAppName); modifiedNameOrType = true; } if (app.AppType == 0 || app.AppType != newAppType) { await DbConnection.ExecuteAsync("UPDATE `Apps` SET `AppType` = @Type WHERE `AppID` = @AppID", new { AppID, Type = newAppType }); if (app.AppType == 0) { await MakeHistory("created_info", SteamDB.DATABASE_APPTYPE, string.Empty, newAppType.ToString()); } else { await MakeHistory("modified_info", SteamDB.DATABASE_APPTYPE, app.AppType.ToString(), newAppType.ToString()); } modifiedNameOrType = true; } if (modifiedNameOrType) { if ((newAppType > 9 && newAppType != 13 && newAppType != 15 && newAppType != 17) || Triggers.Any(newAppName.Contains)) { IRC.Instance.SendOps("New {0}: {1}{2}{3} -{4} {5}", currentType, Colors.BLUE, Utils.LimitStringLength(newAppName), Colors.NORMAL, Colors.DARKBLUE, SteamDB.GetAppURL(AppID, "history")); } } } foreach (var section in ProductInfo.KeyValues.Children) { var sectionName = section.Name.ToLowerInvariant(); if (sectionName == "appid" || sectionName == "public_only" || sectionName == "change_number") { continue; } if (sectionName == "common" || sectionName == "extended") { foreach (var keyvalue in section.Children) { var keyName = string.Format("{0}_{1}", sectionName, keyvalue.Name); if (keyName == "common_type" || keyName == "common_gameid" || keyName == "common_name" || keyName == "extended_order") { // Ignore common keys that are either duplicated or serve no real purpose continue; } if (keyvalue.Children.Count > 0) { await ProcessKey(keyName, keyvalue.Name, Utils.JsonifyKeyValue(keyvalue), keyvalue); } else if (!string.IsNullOrEmpty(keyvalue.Value)) { await ProcessKey(keyName, keyvalue.Name, keyvalue.Value); } } } else { sectionName = string.Format("root_{0}", sectionName); if (await ProcessKey(sectionName, sectionName, Utils.JsonifyKeyValue(section), section) && sectionName == "root_depots") { await DbConnection.ExecuteAsync("UPDATE `Apps` SET `LastDepotUpdate` = CURRENT_TIMESTAMP() WHERE `AppID` = @AppID", new { AppID }); } } } // If app gets hidden but we already have data, do not delete the already existing app info if (newAppName != null) { foreach (var data in CurrentData.Values.Where(data => !data.Processed && !data.KeyName.StartsWith("website", StringComparison.Ordinal))) { await DbConnection.ExecuteAsync("DELETE FROM `AppsInfo` WHERE `AppID` = @AppID AND `Key` = @Key", new { AppID, data.Key }); await MakeHistory("removed_key", data.Key, data.Value); } } else { if (string.IsNullOrEmpty(app.Name)) // We don't have the app in our database yet { await DbConnection.ExecuteAsync("INSERT INTO `Apps` (`AppID`, `Name`) VALUES (@AppID, @AppName) ON DUPLICATE KEY UPDATE `AppType` = `AppType`", new { AppID, AppName = string.Format("{0} {1}", SteamDB.UNKNOWN_APP, AppID) }); } else if (!app.Name.StartsWith(SteamDB.UNKNOWN_APP, StringComparison.Ordinal)) // We do have the app, replace it with default name { await DbConnection.ExecuteAsync("UPDATE `Apps` SET `Name` = @AppName, `AppType` = 0 WHERE `AppID` = @AppID", new { AppID, AppName = string.Format("{0} {1}", SteamDB.UNKNOWN_APP, AppID) }); await MakeHistory("deleted_app", 0, app.Name); } } // Close the connection as it's no longer needed going into depot processor DbConnection.Close(); if (ProductInfo.KeyValues["depots"] != null) { await Steam.Instance.DepotProcessor.Process(AppID, ChangeNumber, ProductInfo.KeyValues["depots"]); } if (ProductInfo.MissingToken && PICSTokens.HasAppToken(AppID)) { Log.WriteError(nameof(PICSTokens), $"Overriden token for appid {AppID} is invalid?"); IRC.Instance.SendOps($"[Tokens] Looks like the overriden token for appid {AppID} ({newAppName}) is invalid"); } }
public void Process(SteamApps.PICSProductInfoCallback.PICSProductInfo productInfo) { ChangeNumber = productInfo.ChangeNumber; #if !DEBUG if (Settings.IsFullRun) #endif { Log.WriteDebug("Sub Processor", "SubID: {0}", SubID); } var appAddedToThisPackage = false; var packageOwned = LicenseList.OwnedSubs.ContainsKey(SubID); var kv = productInfo.KeyValues.Children.FirstOrDefault(); var newPackageName = kv["name"].AsString(); var apps = DbConnection.Query <PackageApp>("SELECT `AppID`, `Type` FROM `SubsApps` WHERE `SubID` = @SubID", new { SubID }).ToDictionary(x => x.AppID, x => x.Type); // TODO: Ideally this should be SteamDB Unknown Package and proper checks like app processor does if (newPackageName == null) { newPackageName = string.Concat("Steam Sub ", SubID); } if (newPackageName != null) { if (string.IsNullOrEmpty(PackageName)) { DbConnection.Execute("INSERT INTO `Subs` (`SubID`, `Name`, `LastKnownName`) VALUES (@SubID, @Name, @Name) ON DUPLICATE KEY UPDATE `Name` = @Name", new { SubID, Name = newPackageName }); MakeHistory("created_sub"); MakeHistory("created_info", SteamDB.DATABASE_NAME_TYPE, string.Empty, newPackageName); } else if (!PackageName.Equals(newPackageName)) { if (newPackageName.StartsWith("Steam Sub ", StringComparison.Ordinal)) { DbConnection.Execute("UPDATE `Subs` SET `Name` = @Name WHERE `SubID` = @SubID", new { SubID, Name = newPackageName }); } else { DbConnection.Execute("UPDATE `Subs` SET `Name` = @Name, `LastKnownName` = @Name WHERE `SubID` = @SubID", new { SubID, Name = newPackageName }); } MakeHistory("modified_info", SteamDB.DATABASE_NAME_TYPE, PackageName, newPackageName); } } foreach (var section in kv.Children) { string sectionName = section.Name.ToLower(); if (string.IsNullOrEmpty(sectionName) || sectionName.Equals("packageid") || sectionName.Equals("name")) { // Ignore common keys continue; } if (sectionName.Equals("appids") || sectionName.Equals("depotids")) { // Remove "ids", so we get "app" from appids and "depot" from depotids string type = sectionName.Replace("ids", string.Empty); var isAppSection = type.Equals("app"); var typeID = (uint)(isAppSection ? 0 : 1); // 0 = app, 1 = depot; can't store as string because it's in the `key` field foreach (var childrenApp in section.Children) { uint appID = uint.Parse(childrenApp.Value); // Is this appid already in this package? if (apps.ContainsKey(appID)) { // Is this appid's type the same? if (apps[appID] != type) { DbConnection.Execute("UPDATE `SubsApps` SET `Type` = @Type WHERE `SubID` = @SubID AND `AppID` = @AppID", new { SubID, AppID = appID, Type = type }); MakeHistory("added_to_sub", typeID, apps[appID].Equals("app") ? "0" : "1", childrenApp.Value); appAddedToThisPackage = true; // TODO: Log relevant add/remove history for depot/app? } apps.Remove(appID); } else { DbConnection.Execute("INSERT INTO `SubsApps` (`SubID`, `AppID`, `Type`) VALUES(@SubID, @AppID, @Type) ON DUPLICATE KEY UPDATE `Type` = @Type", new { SubID, AppID = appID, Type = type }); MakeHistory("added_to_sub", typeID, string.Empty, childrenApp.Value); if (isAppSection) { DbConnection.Execute(AppProcessor.GetHistoryQuery(), new PICSHistory { ID = appID, ChangeID = ChangeNumber, NewValue = SubID.ToString(), Action = "added_to_sub" } ); } else { DbConnection.Execute(DepotProcessor.GetHistoryQuery(), new DepotHistory { DepotID = appID, ChangeID = ChangeNumber, NewValue = SubID, Action = "added_to_sub" } ); } appAddedToThisPackage = true; if (packageOwned && !LicenseList.OwnedApps.ContainsKey(appID)) { LicenseList.OwnedApps.Add(appID, (byte)1); } } } } else if (sectionName.Equals("extended")) { string keyName; foreach (var children in section.Children) { keyName = string.Format("{0}_{1}", sectionName, children.Name); if (children.Children.Count > 0) { ProcessKey(keyName, children.Name, Utils.JsonifyKeyValue(children), true); } else { ProcessKey(keyName, children.Name, children.Value); } } } else if (section.Children.Any()) { sectionName = string.Format("root_{0}", sectionName); ProcessKey(sectionName, sectionName, Utils.JsonifyKeyValue(section), true); } else if (!string.IsNullOrEmpty(section.Value)) { string keyName = string.Format("root_{0}", sectionName); ProcessKey(keyName, sectionName, section.Value); } } foreach (var data in CurrentData.Values) { if (!data.Processed && !data.KeyName.StartsWith("website", StringComparison.Ordinal)) { DbConnection.Execute("DELETE FROM `SubsInfo` WHERE `SubID` = @SubID AND `Key` = @Key", new { SubID, data.Key }); MakeHistory("removed_key", data.Key, data.Value); } } var appsRemoved = apps.Any(); foreach (var app in apps) { DbConnection.Execute("DELETE FROM `SubsApps` WHERE `SubID` = @SubID AND `AppID` = @AppID AND `Type` = @Type", new { SubID, AppID = app.Key, Type = app.Value }); var isAppSection = app.Value.Equals("app"); var typeID = (uint)(isAppSection ? 0 : 1); // 0 = app, 1 = depot; can't store as string because it's in the `key` field MakeHistory("removed_from_sub", typeID, app.Key.ToString()); if (isAppSection) { DbConnection.Execute(AppProcessor.GetHistoryQuery(), new PICSHistory { ID = app.Key, ChangeID = ChangeNumber, OldValue = SubID.ToString(), Action = "removed_from_sub" } ); } else { DbConnection.Execute(DepotProcessor.GetHistoryQuery(), new DepotHistory { DepotID = app.Key, ChangeID = ChangeNumber, OldValue = SubID, Action = "removed_from_sub" } ); } } if (appsRemoved) { LicenseList.RefreshApps(); } if (kv["billingtype"].AsInteger() == 12 && !packageOwned) // 12 == free on demand { Log.WriteDebug("Sub Processor", "Requesting apps in SubID {0} as a free license", SubID); JobManager.AddJob(() => SteamDB.RequestFreeLicense(kv["appids"].Children.Select(appid => (uint)appid.AsInteger()).ToList())); } // Re-queue apps in this package so we can update depots and whatnot if (appAddedToThisPackage && !Settings.IsFullRun && !string.IsNullOrEmpty(PackageName)) { JobManager.AddJob(() => Steam.Instance.Apps.PICSGetAccessTokens(kv["appids"].Children.Select(x => (uint)x.AsInteger()), Enumerable.Empty <uint>())); } }
protected override async Task ProcessData() { ChangeNumber = ProductInfo.ChangeNumber; if (Settings.IsFullRun) { await DbConnection.ExecuteAsync("INSERT INTO `Changelists` (`ChangeID`) VALUES (@ChangeNumber) ON DUPLICATE KEY UPDATE `Date` = `Date`", new { ProductInfo.ChangeNumber }); await DbConnection.ExecuteAsync("INSERT INTO `ChangelistsApps` (`ChangeID`, `AppID`) VALUES (@ChangeNumber, @AppID) ON DUPLICATE KEY UPDATE `AppID` = `AppID`", new { AppID, ProductInfo.ChangeNumber }); } await ProcessKey("root_changenumber", "changenumber", ChangeNumber.ToString()); var app = (await DbConnection.QueryAsync <App>("SELECT `Name`, `AppType` FROM `Apps` WHERE `AppID` = @AppID LIMIT 1", new { AppID })).SingleOrDefault(); var newAppName = ProductInfo.KeyValues["common"]["name"].AsString(); var newAppType = -1; if (newAppName != null) { var currentType = ProductInfo.KeyValues["common"]["type"].AsString().ToLowerInvariant(); newAppType = await DbConnection.ExecuteScalarAsync <int?>("SELECT `AppType` FROM `AppsTypes` WHERE `Name` = @Type LIMIT 1", new { Type = currentType }) ?? -1; var modifiedNameOrType = false; if (newAppType == -1) { await DbConnection.ExecuteAsync("INSERT INTO `AppsTypes` (`Name`, `DisplayName`) VALUES(@Name, @DisplayName)", new { Name = currentType, DisplayName = ProductInfo.KeyValues["common"]["type"].AsString() }); // We don't need to lower display name Log.WriteInfo(nameof(AppProcessor), $"Creating new apptype \"{currentType}\" (AppID {AppID})"); IRC.Instance.SendOps($"New app type: {Colors.BLUE}{currentType}{Colors.NORMAL} - {SteamDB.GetAppUrl(AppID, "history")}"); newAppType = await DbConnection.ExecuteScalarAsync <int>("SELECT `AppType` FROM `AppsTypes` WHERE `Name` = @Type LIMIT 1", new { Type = currentType }); } if (string.IsNullOrEmpty(app.Name) || app.Name.StartsWith(SteamDB.UnknownAppName, StringComparison.Ordinal)) { await DbConnection.ExecuteAsync("INSERT INTO `Apps` (`AppID`, `AppType`, `Name`, `LastKnownName`) VALUES (@AppID, @Type, @AppName, @AppName) ON DUPLICATE KEY UPDATE `Name` = VALUES(`Name`), `LastKnownName` = VALUES(`LastKnownName`), `AppType` = VALUES(`AppType`)", new { AppID, Type = newAppType, AppName = newAppName } ); await MakeHistory("created_app"); await MakeHistory("created_info", SteamDB.DatabaseNameType, string.Empty, newAppName); modifiedNameOrType = true; } else if (app.Name != newAppName) { await DbConnection.ExecuteAsync("UPDATE `Apps` SET `Name` = @AppName, `LastKnownName` = @AppName WHERE `AppID` = @AppID", new { AppID, AppName = newAppName }); await MakeHistory("modified_info", SteamDB.DatabaseNameType, app.Name, newAppName); modifiedNameOrType = true; } if (app.AppType == 0 || app.AppType != newAppType) { await DbConnection.ExecuteAsync("UPDATE `Apps` SET `AppType` = @Type WHERE `AppID` = @AppID", new { AppID, Type = newAppType }); if (app.AppType == 0) { await MakeHistory("created_info", SteamDB.DatabaseAppType, string.Empty, newAppType.ToString()); } else { await MakeHistory("modified_info", SteamDB.DatabaseAppType, app.AppType.ToString(), newAppType.ToString()); } modifiedNameOrType = true; } if (modifiedNameOrType) { if ((newAppType > 9 && newAppType != 13 && newAppType != 15 && newAppType != 17 && newAppType != 18) || Triggers.Any(newAppName.Contains)) { IRC.Instance.SendOps($"New {currentType}: {Colors.BLUE}{Utils.LimitStringLength(newAppName)}{Colors.NORMAL} -{Colors.DARKBLUE} {SteamDB.GetAppUrl(AppID, "history")}"); } } } foreach (var section in ProductInfo.KeyValues.Children) { var sectionName = section.Name.ToLowerInvariant(); if (sectionName == "appid" || sectionName == "change_number") { continue; } if (sectionName == "common" || sectionName == "extended") { foreach (var keyvalue in section.Children) { var keyName = $"{sectionName}_{keyvalue.Name}"; if (keyName == "common_type" || keyName == "common_gameid" || keyName == "common_name" || keyName == "extended_order") { // Ignore common keys that are either duplicated or serve no real purpose continue; } if (keyvalue.Children.Count > 0) { await ProcessKey(keyName, keyvalue.Name, Utils.JsonifyKeyValue(keyvalue), keyvalue); } else if (!string.IsNullOrEmpty(keyvalue.Value)) { await ProcessKey(keyName, keyvalue.Name, keyvalue.Value); } } } else if (sectionName == "public_only") { await ProcessKey($"root_{sectionName}", section.Name, section.Value); } else { sectionName = $"root_{sectionName}"; if (await ProcessKey(sectionName, sectionName, Utils.JsonifyKeyValue(section), section) && sectionName == "root_depots") { await DbConnection.ExecuteAsync("UPDATE `Apps` SET `LastDepotUpdate` = CURRENT_TIMESTAMP() WHERE `AppID` = @AppID", new { AppID }); } } } // If app gets hidden but we already have data, do not delete the already existing app info if (newAppName != null) { foreach (var data in CurrentData.Values.Where(data => !data.Processed && !data.KeyName.StartsWith("website", StringComparison.Ordinal))) { await DbConnection.ExecuteAsync("DELETE FROM `AppsInfo` WHERE `AppID` = @AppID AND `Key` = @Key", new { AppID, data.Key }); await MakeHistory("removed_key", data.Key, data.Value); } } else { if (string.IsNullOrEmpty(app.Name)) // We don't have the app in our database yet { await DbConnection.ExecuteAsync("INSERT INTO `Apps` (`AppID`, `Name`) VALUES (@AppID, @AppName) ON DUPLICATE KEY UPDATE `AppType` = `AppType`", new { AppID, AppName = $"{SteamDB.UnknownAppName} {AppID}" }); } else if (!app.Name.StartsWith(SteamDB.UnknownAppName, StringComparison.Ordinal)) // We do have the app, replace it with default name { await DbConnection.ExecuteAsync("UPDATE `Apps` SET `Name` = @AppName, `AppType` = 0 WHERE `AppID` = @AppID", new { AppID, AppName = $"{SteamDB.UnknownAppName} {AppID}" }); await MakeHistory("deleted_app", 0, app.Name); } } // Close the connection as it's no longer needed going into depot processor DbConnection.Close(); if (ProductInfo.KeyValues["depots"].Children.Any()) { await Steam.Instance.DepotProcessor.Process(AppID, ChangeNumber, ProductInfo.KeyValues["depots"]); } if (ProductInfo.MissingToken && PICSTokens.HasAppToken(AppID)) { Log.WriteError(nameof(PICSTokens), $"Overridden token for appid {AppID} is invalid?"); IRC.Instance.SendOps($"[Tokens] Looks like the overridden token for appid {AppID} ({newAppName}) is invalid"); } if (Settings.IsMillhaven && app.AppType == 0 && newAppType == 18) { var betaAppId = ProductInfo.KeyValues["extended"]["betaforappid"].AsUnsignedInteger(); if (betaAppId == 0) { betaAppId = ProductInfo.KeyValues["common"]["parent"].AsUnsignedInteger(); } Log.WriteDebug(nameof(AppProcessor), $"Requesting beta access for {AppID} ({betaAppId})"); var response = await WebAuth.PerformRequest( HttpMethod.Post, new Uri($"https://store.steampowered.com/ajaxrequestplaytestaccess/{betaAppId}"), new List <KeyValuePair <string, string> > { new KeyValuePair <string, string>("sessionid", nameof(SteamDatabaseBackend)) } ); var data = await response.Content.ReadAsStringAsync(); Log.WriteDebug(nameof(AppProcessor), $"Beta {AppID}: {data}"); } }
protected override async Task ProcessData() { ChangeNumber = ProductInfo.ChangeNumber; if (Settings.IsFullRun) { await DbConnection.ExecuteAsync("INSERT INTO `Changelists` (`ChangeID`) VALUES (@ChangeNumber) ON DUPLICATE KEY UPDATE `Date` = `Date`", new { ProductInfo.ChangeNumber }); await DbConnection.ExecuteAsync("INSERT INTO `ChangelistsApps` (`ChangeID`, `AppID`) VALUES (@ChangeNumber, @AppID) ON DUPLICATE KEY UPDATE `AppID` = `AppID`", new { AppID, ProductInfo.ChangeNumber }); } await ProcessKey("root_changenumber", "changenumber", ChangeNumber.ToString()); var app = (await DbConnection.QueryAsync <App>("SELECT `Name`, `AppType` FROM `Apps` WHERE `AppID` = @AppID LIMIT 1", new { AppID })).SingleOrDefault(); var isPublicOnly = false; var newAppName = ProductInfo.KeyValues["common"]["name"].AsString(); var newAppType = EAppType.Invalid; if (newAppName != null) { var currentType = ProductInfo.KeyValues["common"]["type"].AsString().ToLowerInvariant(); newAppType = Utils.GetAppType(currentType); var modifiedNameOrType = false; if (string.IsNullOrEmpty(app.Name) || app.Name.StartsWith(SteamDB.UnknownAppName, StringComparison.Ordinal)) { await DbConnection.ExecuteAsync("INSERT INTO `Apps` (`AppID`, `AppType`, `Name`, `LastKnownName`) VALUES (@AppID, @Type, @AppName, @AppName) ON DUPLICATE KEY UPDATE `Name` = VALUES(`Name`), `LastKnownName` = VALUES(`LastKnownName`), `AppType` = VALUES(`AppType`)", new { AppID, Type = (int)newAppType, AppName = newAppName } ); await MakeHistory("created_app"); await MakeHistory("created_info", SteamDB.DatabaseNameType, string.Empty, newAppName); modifiedNameOrType = true; } else if (app.Name != newAppName) { await DbConnection.ExecuteAsync("UPDATE `Apps` SET `Name` = @AppName, `LastKnownName` = @AppName WHERE `AppID` = @AppID", new { AppID, AppName = newAppName }); await MakeHistory("modified_info", SteamDB.DatabaseNameType, app.Name, newAppName); modifiedNameOrType = true; } if (app.AppType != newAppType) { await DbConnection.ExecuteAsync("UPDATE `Apps` SET `AppType` = @Type WHERE `AppID` = @AppID", new { AppID, Type = (int)newAppType }); if (app.AppType == EAppType.Invalid) { await MakeHistory("created_info", SteamDB.DatabaseAppType, string.Empty, newAppType.ToString("d")); } else { await MakeHistory("modified_info", SteamDB.DatabaseAppType, app.AppType.ToString(), newAppType.ToString("d")); } modifiedNameOrType = true; } if (modifiedNameOrType && Triggers.Any(newAppName.Contains)) { IRC.Instance.SendOps($"New {newAppType}: {Colors.BLUE}{Utils.LimitStringLength(newAppName)}{Colors.NORMAL} -{Colors.DARKBLUE} {SteamDB.GetAppUrl(AppID, "history")}"); } } foreach (var section in ProductInfo.KeyValues.Children) { var sectionName = section.Name.ToLowerInvariant(); if (sectionName == "appid" || sectionName == "change_number") { continue; } if (sectionName == "common" || sectionName == "extended") { foreach (var keyvalue in section.Children) { var keyName = $"{sectionName}_{keyvalue.Name}"; if (keyName == "common_type" || keyName == "common_gameid" || keyName == "common_name" || keyName == "extended_order") { // Ignore common keys that are either duplicated or serve no real purpose continue; } if (keyvalue.Value != null) { await ProcessKey(keyName, keyvalue.Name, keyvalue.Value); } else { await ProcessKey(keyName, keyvalue.Name, Utils.JsonifyKeyValue(keyvalue), keyvalue); } } } else if (sectionName == "public_only") { isPublicOnly = section.Value == "1"; await ProcessKey($"root_{sectionName}", section.Name, section.Value); } else { sectionName = $"root_{sectionName}"; if (await ProcessKey(sectionName, sectionName, Utils.JsonifyKeyValue(section), section) && sectionName == "root_depots") { await DbConnection.ExecuteAsync("UPDATE `Apps` SET `LastDepotUpdate` = CURRENT_TIMESTAMP() WHERE `AppID` = @AppID", new { AppID }); } } } // If app gets hidden but we already have data, do not delete the already existing app info if (newAppName != null) { foreach (var data in CurrentData.Values) { // This key still exists in appinfo and was correctly processed above if (data.Processed) { continue; } // This is a key that is created and handled by steamdb.info; not in appinfo if (data.KeyName.StartsWith("website", StringComparison.Ordinal)) { continue; } // If this app requires a token, but previously was public and we had stored data, keep it around if (isPublicOnly && !data.KeyName.StartsWith("common", StringComparison.Ordinal)) { continue; } await DbConnection.ExecuteAsync("DELETE FROM `AppsInfo` WHERE `AppID` = @AppID AND `Key` = @Key", new { AppID, data.Key }); await MakeHistory("removed_key", data.Key, data.Value); } } else { if (string.IsNullOrEmpty(app.Name)) // We don't have the app in our database yet { await DbConnection.ExecuteAsync("INSERT INTO `Apps` (`AppID`, `Name`) VALUES (@AppID, @AppName) ON DUPLICATE KEY UPDATE `AppType` = `AppType`", new { AppID, AppName = $"{SteamDB.UnknownAppName} {AppID}" }); } else if (!app.Name.StartsWith(SteamDB.UnknownAppName, StringComparison.Ordinal)) // We do have the app, replace it with default name { await DbConnection.ExecuteAsync("UPDATE `Apps` SET `Name` = @AppName, `AppType` = @AppType WHERE `AppID` = @AppID", new { AppID, AppType = (int)EAppType.Invalid, AppName = $"{SteamDB.UnknownAppName} {AppID}" }); await MakeHistory("deleted_app", 0, app.Name); } } // Close the connection as it's no longer needed going into depot processor DbConnection.Close(); if (ProductInfo.KeyValues["depots"].Children.Any()) { await Steam.Instance.DepotProcessor.Process(AppID, ChangeNumber, ProductInfo.KeyValues["depots"]); } if (ProductInfo.MissingToken && PICSTokens.HasAppToken(AppID)) { Log.WriteError(nameof(PICSTokens), $"Overridden token for appid {AppID} is invalid?"); IRC.Instance.SendOps($"[Tokens] Looks like the overridden token for appid {AppID} ({newAppName}) is invalid"); } if (Settings.IsMillhaven && newAppType == EAppType.Beta && !LicenseList.OwnedApps.ContainsKey(AppID)) { var betaAppId = ProductInfo.KeyValues["extended"]["betaforappid"].AsUnsignedInteger(); if (betaAppId == 0) { betaAppId = ProductInfo.KeyValues["common"]["parent"].AsUnsignedInteger(); } Log.WriteDebug(nameof(AppProcessor), $"Adding beta access request for app {AppID} ({betaAppId})"); Steam.Instance.FreeLicense.AddBeta(betaAppId); } }