private static void PrintImportants(SteamApps.PICSChangesCallback callback) { // Apps var important = callback.AppChanges.Keys.Intersect(Application.ImportantApps); foreach (var app in important) { var changeNumber = callback.AppChanges[app].ChangeNumber; TaskManager.Run(async() => await Utils.SendWebhook(new { Type = "ImportantAppUpdate", AppID = app, ChangeNumber = changeNumber, Url = $"{SteamDB.GetAppUrl(app, "history")}?changeid={changeNumber}", })); var appName = Steam.GetAppName(app, out var appType); IRC.Instance.SendAnnounce($"{appType} update: {Colors.BLUE}{appName}{Colors.NORMAL} -{Colors.DARKBLUE} {SteamDB.GetAppUrl(app, "history")}"); } // Packages important = callback.PackageChanges.Keys.Intersect(Application.ImportantSubs); foreach (var package in important) { var changeNumber = callback.PackageChanges[package].ChangeNumber; TaskManager.Run(async() => await Utils.SendWebhook(new { Type = "ImportantSubUpdate", SubID = package, ChangeNumber = changeNumber, Url = $"{SteamDB.GetPackageUrl(package, "history")}?changeid={changeNumber}", })); var subName = Steam.GetPackageName(package); IRC.Instance.SendAnnounce($"Package update: {Colors.BLUE}{subName}{Colors.NORMAL} -{Colors.DARKBLUE} {SteamDB.GetPackageUrl(package, "history")}"); } }
private static async Task ProcessFeed(Uri feedUrl) { var feed = await LoadRSS(feedUrl); if (feed == null) { return; } if (feed.Items.Count == 0) { Log.WriteError(nameof(RSS), $"Did not find any items in {feedUrl}"); return; } await using var db = await Database.GetConnectionAsync(); var items = (await db.QueryAsync <GenericFeedItem>("SELECT `Link` FROM `RSS` WHERE `Link` IN @Ids", new { Ids = feed.Items.Select(x => x.Link) })).ToDictionary(x => x.Link, _ => (byte)1); var newItems = feed.Items.Where(item => !items.ContainsKey(item.Link)); foreach (var item in newItems) { Log.WriteInfo(nameof(RSS), $"[{feed.Title}] {item.Title}: {item.Link}"); IRC.Instance.SendAnnounce($"{Colors.BLUE}{feed.Title}{Colors.NORMAL}: {item.Title} -{Colors.DARKBLUE} {item.Link}"); await db.ExecuteAsync("INSERT INTO `RSS` (`Link`, `Title`) VALUES(@Link, @Title)", new { item.Link, item.Title }); _ = TaskManager.Run(async() => await Utils.SendWebhook(new { Type = "RSS", item.Title, Url = item.Link, })); if (!Settings.IsMillhaven) { continue; } uint appID = 0; if (feed.Title == "Steam RSS News Feed") { if (item.Title.StartsWith("Dota 2 Update", StringComparison.Ordinal)) { appID = 570; } else if (item.Title == "Team Fortress 2 Update Released") { appID = 440; // tf2 changelog cleanup item.Content = item.Content.Replace("<br/>", "\n"); item.Content = item.Content.Replace("<ul style=\"padding-bottom: 0px; margin-bottom: 0px;\">", "\n"); item.Content = item.Content.Replace("<ul style=\"padding-bottom: 0px; margin-bottom: 0px;\" >", "\n"); item.Content = item.Content.Replace("</ul>", "\n"); item.Content = item.Content.Replace("<li>", "* "); } else if (item.Title == "Left 4 Dead 2 - Update") { appID = 550; } else if (item.Title == "Left 4 Dead - Update") { appID = 500; } else if (item.Title == "Portal 2 - Update") { appID = 620; } } else if (feed.Title.Contains("Counter-Strike: Global Offensive") && item.Title.StartsWith("Release Notes", StringComparison.Ordinal)) { appID = 730; // csgo changelog cleanup item.Content = item.Content.Replace("</p>", "\n"); item.Content = new Regex("<p>\\[\\s*(.+)\\s*\\]", RegexOptions.Multiline | RegexOptions.CultureInvariant).Replace(item.Content, "## $1"); item.Content = item.Content.Replace("<p>", ""); } if (appID > 0) { var build = (await db.QueryAsync <Build>( "SELECT `Builds`.`BuildID`, `Builds`.`ChangeID`, `Builds`.`AppID`, `Changelists`.`Date`, LENGTH(`Official`) as `Official` FROM `Builds` " + "LEFT JOIN `Patchnotes` ON `Patchnotes`.`BuildID` = `Builds`.`BuildID` " + "JOIN `Apps` ON `Apps`.`AppID` = `Builds`.`AppID` " + "JOIN `Changelists` ON `Builds`.`ChangeID` = `Changelists`.`ChangeID` " + "WHERE `Builds`.`AppID` = @AppID ORDER BY `Builds`.`BuildID` DESC LIMIT 1", new { appID } )).SingleOrDefault(); if (build == null) { continue; } if (DateTime.UtcNow > build.Date.AddMinutes(60)) { Log.WriteDebug(nameof(RSS), $"Got {appID} update patch notes, but there is no build within last 10 minutes. {item.Link}"); IRC.Instance.SendOps($"{Colors.GREEN}[Patch notes]{Colors.NORMAL} Got {appID} update patch notes, but there is no build within last 10 minutes. {item.Link}"); continue; } if (build.Official > 0) { Log.WriteDebug(nameof(RSS), $"Got {appID} update patch notes, but official patch notes is already filled. {item.Link}"); IRC.Instance.SendOps($"{Colors.GREEN}[Patch notes]{Colors.NORMAL} Got {appID} update patch notes, but official patch notes is already filled. {item.Link}"); continue; } // breaks item.Content = new Regex(@"<br( \/)?>\r?\n?", RegexOptions.Multiline | RegexOptions.CultureInvariant).Replace(item.Content, "\n"); // dashes (CS:GO mainly) item.Content = new Regex("^(?:-|&#(?:8208|8209|8210|8211|8212|8213);|–|—) ?", RegexOptions.Multiline | RegexOptions.CultureInvariant).Replace(item.Content, "* "); item.Content = WebUtility.HtmlDecode(item.Content); Log.WriteDebug(nameof(RSS), $"Inserting {build.AppID} patchnotes for build {build.BuildID}:\n{item.Content}"); var accountId = Steam.Instance.Client?.SteamID?.AccountID ?? 0; await db.ExecuteAsync( "INSERT INTO `Patchnotes` (`BuildID`, `AppID`, `ChangeID`, `Date`, `Official`, `OfficialURL`) " + "VALUES (@BuildID, @AppID, @ChangeID, @Date, @Content, @Link) ON DUPLICATE KEY UPDATE `Official` = VALUES(`Official`), `OfficialURL` = VALUES(`OfficialURL`), `LastEditor` = @AccountID", new { build.BuildID, build.AppID, build.ChangeID, Date = build.Date.AddSeconds(1).ToString("yyyy-MM-dd HH:mm:ss"), item.Content, item.Link, accountId } ); IRC.Instance.SendAnnounce($"\u2699 Official patch notes:{Colors.BLUE} {Steam.GetAppName(build.AppID)}{Colors.NORMAL} -{Colors.DARKBLUE} {SteamDB.GetPatchnotesUrl(build.BuildID)}"); } } }
private async Task <EPurchaseResultDetail> ActivateKey(string key, uint id = 0) { var msg = new ClientMsgProtobuf <CMsgClientRegisterKey>(EMsg.ClientRegisterKey) { SourceJobID = Steam.Instance.Client.GetNextJobID(), Body = { key = key } }; Steam.Instance.Client.Send(msg); PurchaseResponseCallback job; try { job = await new AsyncJob <PurchaseResponseCallback>(Steam.Instance.Client, msg.SourceJobID); } catch (Exception) { return(EPurchaseResultDetail.Timeout); } await using var db = await Database.GetConnectionAsync(); if (id > 0) { using var sha = SHA1.Create(); await db.ExecuteAsync( "UPDATE `SteamKeys` SET `SteamKey` = @HashedKey, `SubID` = @SubID, `Result` = @Result WHERE `ID` = @ID", new { ID = id, Result = job.PurchaseResultDetail, SubID = job.Packages.Count > 0 ? (int)job.Packages.First().Key : -1, HashedKey = Utils.ByteArrayToString(sha.ComputeHash(Encoding.ASCII.GetBytes(key))) }); } if (job.Packages.Count == 0) { if (job.PurchaseResultDetail != EPurchaseResultDetail.BadActivationCode && job.PurchaseResultDetail != EPurchaseResultDetail.DuplicateActivationCode && job.PurchaseResultDetail != EPurchaseResultDetail.RestrictedCountry) { IRC.Instance.SendOps($"{Colors.GREEN}[Keys]{Colors.NORMAL} Key not activated:{Colors.OLIVE} {job.Result} - {job.PurchaseResultDetail}"); } return(job.PurchaseResultDetail); } if (job.PurchaseResultDetail == EPurchaseResultDetail.NoDetail) { _ = TaskManager.Run(async() => await Utils.SendWebhook(new { Type = "KeyActivated", Key = id, job.Packages, })); } foreach (var(subid, name) in job.Packages) { var databaseName = (await db.QueryAsync <string>("SELECT `LastKnownName` FROM `Subs` WHERE `SubID` = @SubID", new { SubID = subid })).FirstOrDefault() ?? string.Empty; if (databaseName.Equals(name, StringComparison.CurrentCultureIgnoreCase)) { continue; } await db.ExecuteAsync("UPDATE `Subs` SET `LastKnownName` = @Name WHERE `SubID` = @SubID", new { SubID = subid, Name = name }); await db.ExecuteAsync(SubProcessor.HistoryQuery, new PICSHistory { ID = subid, Key = SteamDB.DatabaseNameType, OldValue = "key activation", NewValue = name, Action = "created_info" } ); } return(job.PurchaseResultDetail); }
private static async void OnClanState(SteamFriends.ClanStateCallback callback) { if (callback.Events.Count == 0 && callback.Announcements.Count == 0) { return; } var groupName = callback.ClanName ?? Steam.Instance.Friends.GetClanName(callback.ClanID); var groupAvatar = Utils.ByteArrayToString(callback.AvatarHash ?? Steam.Instance.Friends.GetClanAvatar(callback.ClanID) ?? System.Array.Empty <byte>()).ToLowerInvariant(); if (string.IsNullOrEmpty(groupName)) { groupName = "Group"; } foreach (var announcement in callback.Announcements) { var url = $"https://steamcommunity.com/gid/{callback.ClanID.AccountID}/announcements/detail/{announcement.ID}"; IRC.Instance.SendAnnounce($"{Colors.BLUE}{groupName}{Colors.NORMAL} announcement: {Colors.OLIVE}{announcement.Headline}{Colors.NORMAL} -{Colors.DARKBLUE} {url}"); _ = TaskManager.Run(async() => await Utils.SendWebhook(new { Type = "GroupAnnouncement", Title = announcement.Headline, Group = groupName, Avatar = groupAvatar, Url = url, GroupID = callback.ClanID.AccountID, })); Log.WriteInfo(nameof(ClanState), $"{groupName} \"{announcement.Headline}\""); } await using var db = await Database.GetConnectionAsync(); foreach (var groupEvent in callback.Events) { var link = $"https://steamcommunity.com/gid/{callback.ClanID.AccountID}/events/{groupEvent.ID}"; var id = await db.ExecuteScalarAsync <int>("SELECT `ID` FROM `RSS` WHERE `Link` = @Link", new { Link = link }); if (id > 0) { continue; } IRC.Instance.SendAnnounce( $"{Colors.BLUE}{groupName}{Colors.NORMAL} event: {Colors.OLIVE}{groupEvent.Headline}{Colors.NORMAL} -{Colors.DARKBLUE} {link} {Colors.DARKGRAY}({groupEvent.EventTime.ToString("s", CultureInfo.InvariantCulture).Replace("T", " ")})" ); Log.WriteInfo(nameof(ClanState), $"{groupName} Event \"{groupEvent.Headline}\" {link}"); await db.ExecuteAsync("INSERT INTO `RSS` (`Link`, `Title`, `Date`) VALUES(@Link, @Title, @EventTime)", new { Link = link, Title = groupEvent.Headline, groupEvent.EventTime, }); _ = TaskManager.Run(async() => await Utils.SendWebhook(new { Type = "GroupAnnouncement", Title = groupEvent.Headline, Group = groupName, Avatar = groupAvatar, Url = link, GroupID = callback.ClanID.AccountID, })); } }