private static void OnPICSProductInfo(SteamApps.PICSProductInfoCallback callback) { var apps = callback.Apps.Concat(callback.UnknownApps.ToDictionary(x => x, x => (SteamApps.PICSProductInfoCallback.PICSProductInfo)null)); var packages = callback.Packages.Concat(callback.UnknownPackages.ToDictionary(x => x, x => (SteamApps.PICSProductInfoCallback.PICSProductInfo)null)); foreach (var workaround in apps) { var app = workaround; Log.WriteInfo("PICSProductInfo", "{0}AppID: {1}", app.Value == null ? "Unknown " : "", app.Key); Task mostRecentItem; lock (ProcessedApps) { ProcessedApps.TryGetValue(app.Key, out mostRecentItem); } var workerItem = TaskManager.Run(async delegate { if (mostRecentItem != null && !mostRecentItem.IsCompleted) { Log.WriteDebug("PICSProductInfo", "Waiting for app {0} to finish processing", app.Key); await mostRecentItem; } using (var processor = new AppProcessor(app.Key)) { if (app.Value == null) { processor.ProcessUnknown(); } else { processor.Process(app.Value); } } }); if (Settings.IsFullRun) { continue; } lock (ProcessedApps) { ProcessedApps[app.Key] = workerItem; } workerItem.ContinueWith(task => { lock (ProcessedApps) { if (ProcessedApps.TryGetValue(app.Key, out mostRecentItem) && mostRecentItem.IsCompleted) { ProcessedApps.Remove(app.Key); } } }); } foreach (var workaround in packages) { var package = workaround; Log.WriteInfo("PICSProductInfo", "{0}SubID: {1}", package.Value == null ? "Unknown " : "", package.Key); Task mostRecentItem; lock (ProcessedSubs) { ProcessedSubs.TryGetValue(package.Key, out mostRecentItem); } var workerItem = TaskManager.Run(async delegate { if (mostRecentItem != null && !mostRecentItem.IsCompleted) { Log.WriteDebug("PICSProductInfo", "Waiting for package {0} to finish processing", package.Key); await mostRecentItem; } using (var processor = new SubProcessor(package.Key)) { if (package.Value == null) { processor.ProcessUnknown(); } else { processor.Process(package.Value); } } }); if (Settings.IsFullRun) { continue; } lock (ProcessedSubs) { ProcessedSubs[package.Key] = workerItem; } workerItem.ContinueWith(task => { lock (ProcessedSubs) { if (ProcessedSubs.TryGetValue(package.Key, out mostRecentItem) && mostRecentItem.IsCompleted) { ProcessedSubs.Remove(package.Key); } } }); } }
private static void OnFreeLicenseCallback(SteamApps.FreeLicenseCallback callback) { JobManager.TryRemoveJob(callback.JobID); var packageIDs = callback.GrantedPackages; var appIDs = callback.GrantedApps; Log.WriteDebug("FreeLicense", "Received free license: {0} ({1} apps: {2}, {3} packages: {4})", callback.Result, appIDs.Count, string.Join(", ", appIDs), packageIDs.Count, string.Join(", ", packageIDs)); if (appIDs.Count > 0) { JobManager.AddJob(() => Steam.Instance.Apps.PICSGetAccessTokens(appIDs, Enumerable.Empty <uint>())); } if (packageIDs.Count > 0) { JobManager.AddJob(() => Steam.Instance.Apps.PICSGetProductInfo(Enumerable.Empty <uint>(), packageIDs)); // We don't want to block our main thread with web requests TaskManager.Run(() => { string data = null; try { var response = WebAuth.PerformRequest("GET", "https://store.steampowered.com/account/licenses/"); using (var responseStream = response.GetResponseStream()) { using (var reader = new StreamReader(responseStream)) { data = reader.ReadToEnd(); } } } catch (WebException e) { Log.WriteError("FreeLicense", "Failed to fetch account details page: {0}", e.Message); } using (var db = Database.GetConnection()) { foreach (var package in packageIDs) { var packageData = db.Query <Package>("SELECT `SubID`, `Name`, `LastKnownName` FROM `Subs` WHERE `SubID` = @SubID", new { SubID = package }).FirstOrDefault(); if (!string.IsNullOrEmpty(data)) { // Tell me all about using regex var match = Regex.Match(data, string.Format("RemoveFreeLicense\\( ?{0}, ?'(.+)' ?\\)", package)); if (match.Success) { var grantedName = Encoding.UTF8.GetString(Convert.FromBase64String(match.Groups[1].Value)); // Update last known name if we can if (packageData.SubID > 0 && (string.IsNullOrEmpty(packageData.LastKnownName) || packageData.LastKnownName.StartsWith("Steam Sub ", StringComparison.Ordinal))) { db.Execute("UPDATE `Subs` SET `LastKnownName` = @Name WHERE `SubID` = @SubID", new { SubID = package, Name = grantedName }); db.Execute(SubProcessor.GetHistoryQuery(), new PICSHistory { ID = package, Key = SteamDB.DATABASE_NAME_TYPE, OldValue = "free on demand; account page", NewValue = grantedName, Action = "created_info" } ); // Add a app comment on each app in this package var comment = string.Format("This app is in a free on demand package called <b>{0}</b>", SecurityElement.Escape(grantedName)); var apps = db.Query <PackageApp>("SELECT `AppID` FROM `SubsApps` WHERE `SubID` = @SubID", new { SubID = package }).ToList(); var types = db.Query <App>("SELECT `AppID` FROM `Apps` WHERE `AppType` > 0 AND `AppID` IN @Ids", new { Ids = apps.Select(x => x.AppID) }).ToDictionary(x => x.AppID, x => true); var key = db.ExecuteScalar <uint>("SELECT `ID` FROM `KeyNames` WHERE `Name` = 'website_comment'"); foreach (var app in apps) { if (types.ContainsKey(app.AppID)) { continue; } db.Execute("INSERT INTO `AppsInfo` VALUES (@AppID, @Key, @Value) ON DUPLICATE KEY UPDATE `Key` = `Key`", new { app.AppID, Key = key, value = comment }); } } packageData.LastKnownName = grantedName; } } IRC.Instance.SendMain("New free license granted: {0}{1}{2} -{3} {4}", Colors.BLUE, Steam.FormatPackageName(package, packageData), Colors.NORMAL, Colors.DARKBLUE, SteamDB.GetPackageURL(package) ); } } }); } }
private static void OnPICSProductInfo(SteamApps.PICSProductInfoCallback callback) { JobManager.TryRemoveJob(callback.JobID); var apps = callback.Apps.Concat(callback.UnknownApps.ToDictionary(x => x, x => (SteamApps.PICSProductInfoCallback.PICSProductInfo)null)); var packages = callback.Packages.Concat(callback.UnknownPackages.ToDictionary(x => x, x => (SteamApps.PICSProductInfoCallback.PICSProductInfo)null)); foreach (var workaround in apps) { var app = workaround; Log.WriteInfo("PICSProductInfo", "{0}AppID: {1}", app.Value == null ? "Unknown " : "", app.Key); IWorkItemResult mostRecentItem; lock (ProcessedApps) { ProcessedApps.TryGetValue(app.Key, out mostRecentItem); } var workerItem = ProcessorThreadPool.QueueWorkItem(delegate { try { if (mostRecentItem != null && !mostRecentItem.IsCompleted) { Log.WriteDebug("PICSProductInfo", "Waiting for app {0} to finish processing", app.Key); SmartThreadPool.WaitAll(new IWaitableResult[] { mostRecentItem }); } using (var processor = new AppProcessor(app.Key)) { if (app.Value == null) { processor.ProcessUnknown(); } else { processor.Process(app.Value); } } } catch (MySqlException e) { Log.WriteError("PICSProductInfo", "App {0} faulted: {1}", app.Key, e); JobManager.AddJob(() => Steam.Instance.Apps.PICSGetAccessTokens(app.Key, null)); } catch (Exception e) { Log.WriteError("PICSProductInfo", "App {0} faulted: {1}", app.Key, e); } finally { lock (ProcessedApps) { if (ProcessedApps.TryGetValue(app.Key, out mostRecentItem) && mostRecentItem.IsCompleted) { ProcessedApps.Remove(app.Key); } } } }); if (Settings.IsFullRun) { continue; } lock (ProcessedApps) { ProcessedApps[app.Key] = workerItem; } } foreach (var workaround in packages) { var package = workaround; Log.WriteInfo("PICSProductInfo", "{0}SubID: {1}", package.Value == null ? "Unknown " : "", package.Key); IWorkItemResult mostRecentItem; lock (ProcessedSubs) { ProcessedSubs.TryGetValue(package.Key, out mostRecentItem); } var workerItem = ProcessorThreadPool.QueueWorkItem(delegate { try { if (mostRecentItem != null && !mostRecentItem.IsCompleted) { Log.WriteDebug("PICSProductInfo", "Waiting for package {0} to finish processing", package.Key); SmartThreadPool.WaitAll(new IWaitableResult[] { mostRecentItem }); } using (var processor = new SubProcessor(package.Key)) { if (package.Value == null) { processor.ProcessUnknown(); } else { processor.Process(package.Value); } } } catch (MySqlException e) { Log.WriteError("PICSProductInfo", "Package {0} faulted: {1}", package.Key, e); JobManager.AddJob(() => Steam.Instance.Apps.PICSGetProductInfo(null, package.Key, false, false)); } catch (Exception e) { Log.WriteError("PICSProductInfo", "Package {0} faulted: {1}", package.Key, e); } finally { lock (ProcessedSubs) { if (ProcessedSubs.TryGetValue(package.Key, out mostRecentItem) && mostRecentItem.IsCompleted) { ProcessedSubs.Remove(package.Key); } } } }); if (Settings.IsFullRun) { continue; } lock (ProcessedSubs) { ProcessedSubs[package.Key] = workerItem; } } }
private void RefreshPackageNames() { if (CurrentlyUpdatingNames) { return; } string data; try { CurrentlyUpdatingNames = true; var response = WebAuth.PerformRequest("GET", "https://store.steampowered.com/account/licenses/"); using (var responseStream = response.GetResponseStream()) { using (var reader = new StreamReader(responseStream)) { data = reader.ReadToEnd(); } } } catch (WebException e) { Log.WriteError("FreeLicense", "Failed to fetch account details page: {0}", e.Message); return; } finally { CurrentlyUpdatingNames = false; } var matches = PackageRegex.Matches(data); var names = new Dictionary <uint, string>(); foreach (Match match in matches) { var subID = uint.Parse(match.Groups["subid"].Value); var name = Encoding.UTF8.GetString(Convert.FromBase64String(match.Groups["name"].Value)); names.Add(subID, name); } using (var db = Database.GetConnection()) { // Skip packages that have a store name to avoid messing up history var packageData = db.Query <Package>("SELECT `SubID`, `LastKnownName` FROM `Subs` WHERE `SubID` IN @Ids AND `StoreName` = ''", new { Ids = names.Keys }); foreach (var package in packageData) { var newName = names[package.SubID]; if (package.LastKnownName != newName) { Log.WriteInfo("FreeLicense", "Changed package name for {0} from \"{1}\" to \"{2}\"", package.SubID, package.LastKnownName, newName); db.Execute("UPDATE `Subs` SET `LastKnownName` = @Name WHERE `SubID` = @SubID", new { SubID = package.SubID, Name = newName }); db.Execute( SubProcessor.GetHistoryQuery(), new PICSHistory { ID = package.SubID, Key = SteamDB.DATABASE_NAME_TYPE, OldValue = "free on demand; account page", NewValue = newName, Action = "created_info" } ); } } } }
private static void OnPICSProductInfo(SteamApps.PICSProductInfoCallback callback) { JobManager.TryRemoveJob(callback.JobID); var apps = callback.Apps.Concat(callback.UnknownApps.ToDictionary(x => x, x => (SteamApps.PICSProductInfoCallback.PICSProductInfo)null)); var packages = callback.Packages.Concat(callback.UnknownPackages.ToDictionary(x => x, x => (SteamApps.PICSProductInfoCallback.PICSProductInfo)null)); foreach (var workaround in apps) { var app = workaround; Log.WriteInfo("PICSProductInfo", "{0}AppID: {1}", app.Value == null ? "Unknown " : "", app.Key); Task mostRecentItem; lock (ProcessedApps) { ProcessedApps.TryGetValue(app.Key, out mostRecentItem); } var workerItem = TaskManager.Run(async() => { try { await ProcessorSemaphore.WaitAsync().ConfigureAwait(false); if (mostRecentItem != null && !mostRecentItem.IsCompleted) { Log.WriteDebug("PICSProductInfo", "Waiting for app {0} to finish processing", app.Key); await mostRecentItem.ConfigureAwait(false); } using (var processor = new AppProcessor(app.Key)) { if (app.Value == null) { processor.ProcessUnknown(); } else { processor.Process(app.Value); } } } catch (MySqlException e) { ErrorReporter.Notify($"App {app.Key}", e); JobManager.AddJob(() => Steam.Instance.Apps.PICSGetAccessTokens(app.Key, null)); } catch (Exception e) { ErrorReporter.Notify($"App {app.Key}", e); } finally { lock (ProcessedApps) { if (ProcessedApps.TryGetValue(app.Key, out mostRecentItem) && mostRecentItem.IsCompleted) { ProcessedApps.Remove(app.Key); } } ProcessorSemaphore.Release(); } }); if (Settings.IsFullRun) { continue; } lock (ProcessedApps) { ProcessedApps[app.Key] = workerItem; } } foreach (var workaround in packages) { var package = workaround; Log.WriteInfo("PICSProductInfo", "{0}SubID: {1}", package.Value == null ? "Unknown " : "", package.Key); Task mostRecentItem; lock (ProcessedSubs) { ProcessedSubs.TryGetValue(package.Key, out mostRecentItem); } var workerItem = TaskManager.Run(async() => { try { await ProcessorSemaphore.WaitAsync().ConfigureAwait(false); if (mostRecentItem != null && !mostRecentItem.IsCompleted) { Log.WriteDebug("PICSProductInfo", "Waiting for package {0} to finish processing", package.Key); await mostRecentItem.ConfigureAwait(false); } using (var processor = new SubProcessor(package.Key)) { if (package.Value == null) { processor.ProcessUnknown(); } else { processor.Process(package.Value); } } } catch (MySqlException e) { ErrorReporter.Notify($"Package {package.Key}", e); JobManager.AddJob(() => Steam.Instance.Apps.PICSGetProductInfo(null, package.Key, false, false)); } catch (Exception e) { ErrorReporter.Notify($"Package {package.Key}", e); } finally { lock (ProcessedSubs) { if (ProcessedSubs.TryGetValue(package.Key, out mostRecentItem) && mostRecentItem.IsCompleted) { ProcessedSubs.Remove(package.Key); } } ProcessorSemaphore.Release(); } }); if (Settings.IsFullRun) { continue; } lock (ProcessedSubs) { ProcessedSubs[package.Key] = workerItem; } } }