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 <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); }
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 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", } ); } }