/// <summary> /// Gets the latest blog post of the specified type. /// </summary> /// <param name="type">The type of build to get. Specify <see cref="BuildType.Unknown"/> to get the latest build regardless of type.</param> /// <returns>A <see cref="BlogEntry"/> representing the build blog post or null if no build was found.</returns> public async Task <BlogEntry> GetLatestBuildPostAsync(BuildType type = BuildType.Unknown) { BlogEntry post = null; try { for (int page = 1; page <= 10; page++) { // Get page. var doc = XDocument.Parse(await httpClient.GetStringAsync($"https://blogs.windows.com/windows-insider/feed/?paged={page}")); var entries = from item in doc.Root.Descendants().First(i => i.Name.LocalName == "channel").Elements().Where(i => i.Name.LocalName == "item") where item.Elements().First(i => i.Name.LocalName == "link").Value.ToLowerInvariant().ContainsAny("insider-preview", "windows-10-build", "windows-11-build") select item; var posts = entries.Select(async item => await BlogEntry.Create( httpClient, item.Elements().First(i => i.Name.LocalName == "title").Value, item.Elements().First(i => i.Name.LocalName == "link").Value, item.Elements().First(i => i.Name.LocalName == "description").Value)) .Select(t => t.Result) .Where(i => i != null) .ToList(); // Get first post of desired type if a type was specified. if (type == BuildType.Unknown) { post = posts.FirstOrDefault(); } else if (type == BuildType.BetaPc) { post = posts.Where(p => p.BuildType == BuildType.BetaPc || p.BuildType == BuildType.DevBetaPc || p.BuildType == BuildType.BetaReleasePreviewPc).FirstOrDefault(); } else if (type == BuildType.ReleasePreviewPc) { post = posts.Where(p => p.BuildType == BuildType.ReleasePreviewPc || p.BuildType == BuildType.BetaReleasePreviewPc).FirstOrDefault(); } else if (type == BuildType.DevPc) { post = posts.Where(p => p.BuildType == BuildType.DevPc || p.BuildType == BuildType.DevBetaPc).FirstOrDefault(); } else { post = posts.Where(p => p.BuildType == type).FirstOrDefault(); } if (post != null) { return(post); } } } catch (HttpRequestException ex) { LogError($"Exception when getting post for type {type}: {ex}"); return(null); } LogError($"Unable to get new post for type {type}"); return(null); }
public async void SendNewBuildToShard(DiscordSocketClient shard, BlogEntry blogEntry) { // If the MS server is in this shard, announce there first. var msGuild = shard.Guilds.SingleOrDefault(g => g.Id == Constants.MsGuildId); if (msGuild != null) { await SendBuildToGuild(shard, msGuild, blogEntry); } foreach (var guild in shard.Guilds) { // Skip MS guild. if (guild.Id == Constants.MsGuildId) { continue; } await SendBuildToGuild(shard, guild, blogEntry); } }
private async Task SendBuildToGuild(DiscordSocketClient shard, SocketGuild guild, BlogEntry blogEntry) { var channel = GetSpeakingChannelForSocketGuild(guild); if (channel == null) { LogInfo($"Rolling over {guild.Name} (disabled) ({shard.ShardId}/{Shards.Count - 1})"); return; } // Verify we have permission to speak. if (guild.CurrentUser?.GetPermissions(channel).SendMessages != true) { LogInfo($"Rolling over {guild.Name} (no perms) ({shard.ShardId}/{Shards.Count - 1})"); return; } // Get all roles. var roleDev = GetRoleForIGuild(guild, RoleType.InsiderDev); var roleBeta = GetRoleForIGuild(guild, RoleType.InsiderBeta); var roleReleasePreview = GetRoleForIGuild(guild, RoleType.InsiderReleasePreview); var roleText = string.Empty; var typeText = string.Empty; var emotesText = ":smiley_cat:"; switch (blogEntry.BuildType) { case BuildType.DevPc: roleText = $"{roleDev?.Mention} "; typeText = " to the Dev Channel"; emotesText += " :tools:"; break; case BuildType.BetaPc: roleText = $"{roleBeta?.Mention} "; typeText = " to the Beta Channel"; emotesText += " :paintbrush:"; break; case BuildType.ReleasePreviewPc: roleText = $"{roleReleasePreview?.Mention} "; typeText = " to the Release Preview Channel"; emotesText += " :package:"; break; case BuildType.BetaReleasePreviewPc: roleText = $"{roleBeta?.Mention} {roleReleasePreview?.Mention} "; typeText = " to the Beta and Release Preview Channels"; emotesText += " :paintbrush: :package:"; break; case BuildType.DevBetaPc: roleText = $"{roleDev?.Mention} {roleBeta?.Mention} "; typeText = " to the Dev and Beta Channels"; emotesText += " :tools: :paintbrush:"; break; case BuildType.Server: typeText = " for Server"; emotesText += " :desktop:"; break; } try { await StartTyping(channel); switch (GetRandomNumber(3)) { default: await channel.SendMessageAsync($"{roleText}{blogEntry.OSName} Insider Preview Build {blogEntry.BuildNumber} has just been released{typeText}! {emotesText}\n{blogEntry.Link}"); break; case 1: await channel.SendMessageAsync($"{roleText}{blogEntry.OSName} Insider Preview Build {blogEntry.BuildNumber} has just been released{typeText}! Yes! {emotesText}\n{blogEntry.Link}"); break; case 2: await channel.SendMessageAsync($"{roleText}Better check for updates now! {blogEntry.OSName} Insider Preview Build {blogEntry.BuildNumber} has just been released{typeText}! {emotesText}\n{blogEntry.Link}"); break; } } catch (Exception ex) { LogError($"Failed to speak in {guild.Name} ({shard.ShardId}/{Shards.Count - 1}): {ex}"); } // Log server. LogInfo($"Spoke in {guild.Name} ({shard.ShardId}/{Shards.Count - 1})"); }