public static string FormatPackageName(uint subID, Package data) { if (data.SubID == 0) { // much hackery if (subID == 0) { return("Steam"); } return($"SubID {subID}"); } var name = Utils.RemoveControlCharacters(data.Name); var nameLast = Utils.RemoveControlCharacters(data.LastKnownName); if (string.IsNullOrEmpty(nameLast)) { return(Utils.LimitStringLength(name)); } if (!name.Equals(nameLast, StringComparison.CurrentCultureIgnoreCase) && !name.StartsWith("Steam Sub ", StringComparison.Ordinal)) { return($"{Utils.LimitStringLength(nameLast)} {Colors.DARKGRAY}({Utils.LimitStringLength(name)}){Colors.NORMAL}"); } return(Utils.LimitStringLength(nameLast)); }
public override async Task OnCommand(CommandArguments command) { if (string.IsNullOrWhiteSpace(command.Message)) { command.Reply($"Usage:{Colors.OLIVE} app <appid or partial game name>"); return; } if (!uint.TryParse(command.Message, out var appID)) { appID = await TrySearchAppId(command); if (appID == 0) { return; } } var info = await GetAppData(appID); if (info == null) { command.Reply($"Unknown AppID: {Colors.BLUE}{appID}{(LicenseList.OwnedApps.ContainsKey(appID) ? SteamDB.StringCheckmark : string.Empty)}"); return; } string name; if (info.KeyValues["common"]["name"].Value != null) { name = Utils.LimitStringLength(Utils.RemoveControlCharacters(info.KeyValues["common"]["name"].AsString())); } else { name = Steam.GetAppName(info.ID); } var filename = $"{Utils.ByteArrayToString(info.SHAHash)}.vdf"; info.KeyValues.SaveToFile(Path.Combine(Application.Path, "app", filename), false); command.Reply($"{Colors.BLUE}{name}{Colors.NORMAL} -{Colors.DARKBLUE} <{SteamDB.GetAppUrl(info.ID)}>{Colors.NORMAL} - Dump:{Colors.DARKBLUE} <{SteamDB.GetRawAppUrl(filename)}>{Colors.NORMAL}{(info.MissingToken ? SteamDB.StringNeedToken : string.Empty)}{(LicenseList.OwnedApps.ContainsKey(info.ID) ? SteamDB.StringCheckmark : string.Empty)}"); if (command.Recipient == Settings.Current.IRC.Channel.Ops && !LicenseList.OwnedApps.ContainsKey(info.ID)) { JobManager.AddJob(() => Steam.Instance.Apps.RequestFreeLicense(info.ID)); } }
public static string FormatAppName(uint appID, App data) { if (data.AppID == 0) { return($"AppID {appID}"); } var name = Utils.RemoveControlCharacters(data.Name); var nameLast = Utils.RemoveControlCharacters(data.LastKnownName); if (!string.IsNullOrEmpty(nameLast) && !name.Equals(nameLast, StringComparison.CurrentCultureIgnoreCase)) { return($"{Utils.LimitStringLength(name)} {Colors.DARKGRAY}({Utils.LimitStringLength(nameLast)}){Colors.NORMAL}"); } return(Utils.LimitStringLength(name)); }
public override async Task OnCommand(CommandArguments command) { if (string.IsNullOrWhiteSpace(command.Message)) { command.Reply($"Usage:{Colors.OLIVE} app <appid or partial game name>"); return; } string name; if (!uint.TryParse(command.Message, out var appID)) { appID = await TrySearchAppId(command); if (appID == 0) { return; } } var tokenTask = Steam.Instance.Apps.PICSGetAccessTokens(appID, null); tokenTask.Timeout = TimeSpan.FromSeconds(10); var tokenCallback = await tokenTask; SteamApps.PICSRequest request; if (tokenCallback.AppTokens.ContainsKey(appID)) { request = PICSTokens.NewAppRequest(appID, tokenCallback.AppTokens[appID]); } else { request = PICSTokens.NewAppRequest(appID); } var infoTask = Steam.Instance.Apps.PICSGetProductInfo(new List <SteamApps.PICSRequest> { request }, Enumerable.Empty <SteamApps.PICSRequest>()); infoTask.Timeout = TimeSpan.FromSeconds(10); var job = await infoTask; var callback = job.Results?.FirstOrDefault(x => x.Apps.ContainsKey(appID)); if (callback == null) { command.Reply($"Unknown AppID: {Colors.BLUE}{appID}{(LicenseList.OwnedApps.ContainsKey(appID) ? SteamDB.StringCheckmark : string.Empty)}"); return; } var info = callback.Apps[appID]; if (info.KeyValues["common"]["name"].Value != null) { name = Utils.LimitStringLength(Utils.RemoveControlCharacters(info.KeyValues["common"]["name"].AsString())); } else { name = Steam.GetAppName(info.ID); } info.KeyValues.SaveToFile(Path.Combine(Application.Path, "app", $"{info.ID}.vdf"), false); command.Reply($"{Colors.BLUE}{name}{Colors.NORMAL} -{Colors.DARKBLUE} {SteamDB.GetAppUrl(info.ID)}{Colors.NORMAL} - Dump:{Colors.DARKBLUE} {SteamDB.GetRawAppUrl(info.ID)}{Colors.NORMAL}{(info.MissingToken ? SteamDB.StringNeedToken : string.Empty)}{(LicenseList.OwnedApps.ContainsKey(info.ID) ? SteamDB.StringCheckmark : string.Empty)}"); if (command.IsUserAdmin && !LicenseList.OwnedApps.ContainsKey(info.ID)) { JobManager.AddJob(() => Steam.Instance.Apps.RequestFreeLicense(info.ID)); } }
public async void OnMessage(CommandArguments command) { var matches = SharedFileMatch.Matches(command.Message); foreach (Match match in matches) { if (!ulong.TryParse(match.Groups["pubfileid"].Value, out var pubFileId)) { continue; } Log.WriteInfo("Link Expander", "Will look up pubfile {0} for {1}", pubFileId, command); var pubFileRequest = new CPublishedFile_GetDetails_Request { includevotes = true, }; pubFileRequest.publishedfileids.Add(pubFileId); PublishedFileDetails details; try { var task = PublishedFiles.SendMessage(api => api.GetDetails(pubFileRequest)); task.Timeout = TimeSpan.FromSeconds(10); var callback = await task; var response = callback.GetDeserializedResponse <CPublishedFile_GetDetails_Response>(); details = response.publishedfiledetails.FirstOrDefault(); } catch (Exception e) { Log.WriteError("Link Expander", "Failed to get pubfile details: {0}", e.Message); continue; } if (details == null || (EResult)details.result != EResult.OK) { continue; } string title; if (!string.IsNullOrWhiteSpace(details.title)) { title = details.title; } else if (!string.IsNullOrEmpty(details.file_description)) { title = details.file_description; } else { title = details.filename; } if (title.Length > 49) { title = title.Substring(0, 49) + "…"; } var votesUp = details.vote_data?.votes_up ?? 0; var votesDown = details.vote_data?.votes_down ?? 0; IRC.Instance.SendReply(command.Recipient, string.Format("{0}» {1}{2} {3}{4}{5} for {6}{7}{8} ({9:N0} views, {10:N0} \ud83d\udc4d, {11:N0} \ud83d\udc4e)", Colors.OLIVE, Colors.NORMAL, (EWorkshopFileType)details.file_type, Colors.BLUE, Utils.RemoveControlCharacters(title), Colors.NORMAL, Colors.BLUE, Utils.LimitStringLength(Utils.RemoveControlCharacters(details.app_name)), Colors.LIGHTGRAY, details.views, votesUp, votesDown ), false ); } }
public void OnMessage(CommandArguments command) { var matches = SteamLinkMatch.Matches(command.Message); foreach (Match match in matches) { var page = match.Groups["page"].Value; // Ignore sub pages, easier to do it here rather than in regex if (!string.IsNullOrEmpty(page)) { continue; } var appType = string.Empty; var id = uint.Parse(match.Groups["id"].Value); var isPackage = match.Groups["type"].Value == "sub"; string name; if (isPackage) { name = Steam.GetPackageName(id); } else { App data; using (var db = Database.Get()) { data = db.Query <App>("SELECT `AppID`, `Apps`.`Name`, `LastKnownName`, `AppsTypes`.`DisplayName` as `AppTypeString` FROM `Apps` JOIN `AppsTypes` ON `Apps`.`AppType` = `AppsTypes`.`AppType` WHERE `AppID` = @AppID", new { AppID = id }).SingleOrDefault(); } if (data.AppID == 0) { continue; } name = string.IsNullOrEmpty(data.LastKnownName) ? data.Name : data.LastKnownName; name = Utils.LimitStringLength(Utils.RemoveControlCharacters(name)); appType = data.AppTypeString; } if (command.Message.IndexOf(name, StringComparison.CurrentCultureIgnoreCase) >= 0) { continue; } var priceInfo = isPackage ? string.Empty : GetFormattedPrices(id); IRC.Instance.SendReply(command.Recipient, string.Format("{0}» {1}{2} {3} —{4} {5}{6}{7}", Colors.OLIVE, Colors.NORMAL, isPackage ? "Package" : appType, id, Colors.BLUE, name, Colors.LIGHTGRAY, priceInfo ), false ); } }
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"); } }
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); } }