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 void SendChangelistsToIRC(SteamApps.PICSChangesCallback callback) { // Group apps and package changes by changelist, this will seperate into individual changelists var appGrouping = callback.AppChanges.Values.GroupBy(a => a.ChangeNumber); var packageGrouping = callback.PackageChanges.Values.GroupBy(p => p.ChangeNumber); // Join apps and packages back together based on changelist number var changeLists = Utils.FullOuterJoin(appGrouping, packageGrouping, a => a.Key, p => p.Key, (a, p, key) => new { ChangeNumber = key, Apps = a.ToList(), Packages = p.ToList(), }, new EmptyGrouping <uint, SteamApps.PICSChangesCallback.PICSChangeData>(), new EmptyGrouping <uint, SteamApps.PICSChangesCallback.PICSChangeData>()) .OrderBy(c => c.ChangeNumber); foreach (var changeList in changeLists) { var appCount = changeList.Apps.Count; var packageCount = changeList.Packages.Count; string Message = string.Format("Changelist {0}{1}{2} {3}({4:N0} apps and {5:N0} packages){6} -{7} {8}", Colors.BLUE, changeList.ChangeNumber, Colors.NORMAL, Colors.DARKGRAY, appCount, packageCount, Colors.NORMAL, Colors.DARKBLUE, SteamDB.GetChangelistURL(changeList.ChangeNumber) ); var changesCount = appCount + packageCount; if (changesCount >= 50) { IRC.Instance.SendMain(Message); } IRC.Instance.SendAnnounce("{0}»{1} {2}", Colors.RED, Colors.NORMAL, Message); // If this changelist is very big, freenode will hate us forever if we decide to print all that stuff if (changesCount > 300) { IRC.Instance.SendAnnounce("{0} This changelist is too big to be printed in IRC, please view it online", Colors.RED); continue; } if (appCount > 0) { Dictionary <uint, App> apps; App data; using (var db = Database.GetConnection()) { apps = db.Query <App>("SELECT `AppID`, `Name`, `LastKnownName` FROM `Apps` WHERE `AppID` IN @Ids", new { Ids = changeList.Apps.Select(x => x.ID) }).ToDictionary(x => x.AppID, x => x); } foreach (var app in changeList.Apps) { apps.TryGetValue(app.ID, out data); IRC.Instance.SendAnnounce(" App: {0}{1}{2} - {3}{4}", Colors.BLUE, app.ID, Colors.NORMAL, Steam.FormatAppName(app.ID, data), app.NeedsToken ? SteamDB.StringNeedToken : string.Empty ); } } if (packageCount > 0) { Dictionary <uint, Package> packages; Package data; using (var db = Database.GetConnection()) { packages = db.Query <Package>("SELECT `SubID`, `Name`, `LastKnownName` FROM `Subs` WHERE `SubID` IN @Ids", new { Ids = changeList.Packages.Select(x => x.ID) }).ToDictionary(x => x.SubID, x => x); } foreach (var package in changeList.Packages) { packages.TryGetValue(package.ID, out data); IRC.Instance.SendAnnounce(" Package: {0}{1}{2} - {3}{4}", Colors.BLUE, package.ID, Colors.NORMAL, Steam.FormatPackageName(package.ID, data), package.NeedsToken ? SteamDB.StringNeedToken : string.Empty ); } } } }
private async Task SendChangelistsToIRC(SteamApps.PICSChangesCallback callback) { if (DateTime.Now > ChangelistBurstTime) { ChangelistBurstTime = DateTime.Now.AddMinutes(5); ChangelistBurstCount = 0; } // Group apps and package changes by changelist number var changelists = new Dictionary <uint, IrcChangelistGroup>(); foreach (var app in callback.AppChanges.Values) { if (!changelists.ContainsKey(app.ChangeNumber)) { changelists[app.ChangeNumber] = new IrcChangelistGroup(); } changelists[app.ChangeNumber].Apps.Add(app.ID); } foreach (var package in callback.PackageChanges.Values) { if (!changelists.ContainsKey(package.ChangeNumber)) { changelists[package.ChangeNumber] = new IrcChangelistGroup(); } changelists[package.ChangeNumber].Packages.Add(package.ID); } foreach (var(changeNumber, changeList) in changelists.OrderBy(x => x.Key)) { var appCount = changeList.Apps.Count; var packageCount = changeList.Packages.Count; var message = $"Changelist {Colors.BLUE}{changeNumber}{Colors.NORMAL} {Colors.DARKGRAY}({appCount:N0} apps and {packageCount:N0} packages)"; var changesCount = appCount + packageCount; if (changesCount >= 50) { IRC.Instance.SendMain($"Big {message}{Colors.DARKBLUE} {SteamDB.GetChangelistUrl(changeNumber)}"); } if (ChangelistBurstCount++ >= CHANGELIST_BURST_MIN || changesCount > 300) { if (appCount > 0) { message += $" (Apps: {string.Join(", ", changeList.Apps)})"; } if (packageCount > 0) { message += $" (Packages: {string.Join(", ", changeList.Packages)})"; } IRC.Instance.SendAnnounce($"{Colors.RED}»{Colors.NORMAL} {message}"); continue; } IRC.Instance.SendAnnounce($"{Colors.RED}»{Colors.NORMAL} {message}"); if (appCount > 0) { Dictionary <uint, App> apps; await using (var db = await Database.GetConnectionAsync()) { apps = (await db.QueryAsync <App>("SELECT `AppID`, `Name`, `LastKnownName` FROM `Apps` WHERE `AppID` IN @Ids", new { Ids = changeList.Apps })).ToDictionary(x => x.AppID, x => x); } foreach (var appId in changeList.Apps) { apps.TryGetValue(appId, out var data); IRC.Instance.SendAnnounce($" App: {Colors.BLUE}{appId}{Colors.NORMAL} - {Steam.FormatAppName(appId, data)}"); } } if (packageCount > 0) { Dictionary <uint, Package> packages; await using (var db = await Database.GetConnectionAsync()) { packages = (await db.QueryAsync <Package>("SELECT `SubID`, `Name`, `LastKnownName` FROM `Subs` WHERE `SubID` IN @Ids", new { Ids = changeList.Packages })).ToDictionary(x => x.SubID, x => x); } foreach (var packageId in changeList.Packages) { packages.TryGetValue(packageId, out var data); IRC.Instance.SendAnnounce($" Package: {Colors.BLUE}{packageId}{Colors.NORMAL} - {Steam.FormatPackageName(packageId, data)}"); } } } }
private async Task SendChangelistsToIRC(SteamApps.PICSChangesCallback callback) { if (DateTime.Now > ChangelistBurstTime) { ChangelistBurstTime = DateTime.Now.AddMinutes(5); ChangelistBurstCount = 0; } // Group apps and package changes by changelist, this will seperate into individual changelists var appGrouping = callback.AppChanges.Values.GroupBy(a => a.ChangeNumber); var packageGrouping = callback.PackageChanges.Values.GroupBy(p => p.ChangeNumber); // Join apps and packages back together based on changelist number var changeLists = Utils.FullOuterJoin(appGrouping, packageGrouping, a => a.Key, p => p.Key, (a, p, key) => new { ChangeNumber = key, Apps = a.ToList(), Packages = p.ToList(), }, new EmptyGrouping <uint, SteamApps.PICSChangesCallback.PICSChangeData>(), new EmptyGrouping <uint, SteamApps.PICSChangesCallback.PICSChangeData>()) .OrderBy(c => c.ChangeNumber); foreach (var changeList in changeLists) { var appCount = changeList.Apps.Count; var packageCount = changeList.Packages.Count; var message = $"Changelist {Colors.BLUE}{changeList.ChangeNumber}{Colors.NORMAL} {Colors.DARKGRAY}({appCount:N0} apps and {packageCount:N0} packages)"; var changesCount = appCount + packageCount; if (changesCount >= 50) { IRC.Instance.SendMain($"Big {message}{Colors.DARKBLUE} {SteamDB.GetChangelistURL(changeList.ChangeNumber)}"); } if (ChangelistBurstCount++ >= CHANGELIST_BURST_MIN || changesCount > 300) { if (appCount > 0) { message += $" (Apps: {string.Join(", ", changeList.Apps.Select(x => x.ID))})"; } if (packageCount > 0) { message += $" (Packages: {string.Join(", ", changeList.Packages.Select(x => x.ID))})"; } IRC.Instance.SendAnnounce($"{Colors.RED}»{Colors.NORMAL} {message}"); continue; } IRC.Instance.SendAnnounce("{0}»{1} {2}", Colors.RED, Colors.NORMAL, message); if (appCount > 0) { Dictionary <uint, App> apps; using (var db = Database.Get()) { apps = (await db.QueryAsync <App>("SELECT `AppID`, `Name`, `LastKnownName` FROM `Apps` WHERE `AppID` IN @Ids", new { Ids = changeList.Apps.Select(x => x.ID) })).ToDictionary(x => x.AppID, x => x); } foreach (var app in changeList.Apps) { apps.TryGetValue(app.ID, out var data); IRC.Instance.SendAnnounce(" App: {0}{1}{2} - {3}{4}", Colors.BLUE, app.ID, Colors.NORMAL, Steam.FormatAppName(app.ID, data), app.NeedsToken ? SteamDB.StringNeedToken : string.Empty ); } } if (packageCount > 0) { Dictionary <uint, Package> packages; using (var db = Database.Get()) { packages = (await db.QueryAsync <Package>("SELECT `SubID`, `Name`, `LastKnownName` FROM `Subs` WHERE `SubID` IN @Ids", new { Ids = changeList.Packages.Select(x => x.ID) })).ToDictionary(x => x.SubID, x => x); } foreach (var package in changeList.Packages) { packages.TryGetValue(package.ID, out var data); IRC.Instance.SendAnnounce(" Package: {0}{1}{2} - {3}{4}", Colors.BLUE, package.ID, Colors.NORMAL, Steam.FormatPackageName(package.ID, data), package.NeedsToken ? SteamDB.StringNeedToken : string.Empty ); } } } }