From 610c01318a7cbf4d130ba9be96b3aae512d59cfa Mon Sep 17 00:00:00 2001 From: 13xforever Date: Fri, 13 Nov 2020 02:48:49 +0500 Subject: [PATCH] botupdate, part 2 --- Clients/PsnClient/PsnClient.cs | 5 +- CompatBot/Commands/ContentFilters.cs | 37 ++-- CompatBot/Commands/Events.cs | 4 +- CompatBot/Commands/EventsBaseCommand.cs | 148 +++++++-------- CompatBot/Commands/Explain.cs | 8 +- CompatBot/Commands/Invites.cs | 4 +- CompatBot/Commands/Misc.cs | 26 +-- CompatBot/Commands/Moderation.cs | 10 +- CompatBot/Commands/Psn.Check.cs | 14 +- CompatBot/Commands/Psn.cs | 32 ++-- CompatBot/Commands/Sudo.Bot.Configuration.cs | 14 +- CompatBot/Commands/Sudo.Bot.cs | 18 +- CompatBot/Commands/Sudo.Fix.cs | 66 +++---- CompatBot/Commands/Sudo.cs | 52 +++--- CompatBot/Commands/Syscall.cs | 22 +-- .../Providers/AmdDriverVersionProvider.cs | 171 +++++++++--------- CompatBot/Database/Providers/ContentFilter.cs | 29 ++- CompatBot/Database/Providers/StatsStorage.cs | 26 ++- .../EventHandlers/DiscordInviteFilter.cs | 5 +- .../LogParsing/ArchiveHandlers/GzipHandler.cs | 7 +- .../ArchiveHandlers/IArchiveHandler.cs | 2 +- .../LogParsing/ArchiveHandlers/PlainText.cs | 3 +- .../LogParsing/ArchiveHandlers/RarHandler.cs | 7 +- .../ArchiveHandlers/SevenZipHandler.cs | 6 +- .../LogParsing/ArchiveHandlers/ZipHandler.cs | 6 +- .../LogParsing/LogParser.LogSections.cs | 4 +- .../LogParsing/POCOs/LogParseState.cs | 8 +- .../SourceHandlers/BaseSourceHandler.cs | 2 +- .../DiscordAttachmentHandler.cs | 6 +- .../SourceHandlers/DropboxHandler.cs | 16 +- .../SourceHandlers/GenericLinkHandler.cs | 16 +- .../SourceHandlers/GoogleDriveHandler.cs | 18 +- .../SourceHandlers/ISourceHandler.cs | 2 +- .../LogParsing/SourceHandlers/MegaHandler.cs | 14 +- .../SourceHandlers/OneDriveSourceHandler.cs | 13 +- .../SourceHandlers/PastebinHandler.cs | 6 +- CompatBot/EventHandlers/LogParsingHandler.cs | 19 +- CompatBot/EventHandlers/NewBuildsMonitor.cs | 2 +- CompatBot/EventHandlers/PostLogHelpHandler.cs | 10 +- CompatBot/EventHandlers/Starbucks.cs | 8 +- .../EventHandlers/UnknownCommandHandler.cs | 2 +- CompatBot/Program.cs | 14 +- CompatBot/ThumbScrapper/PsnScraper.cs | 45 +++-- CompatBot/Utils/DefaultHandlerFilter.cs | 4 +- .../Extensions/DiscordClientExtensions.cs | 4 +- .../Extensions/DiscordMessageExtensions.cs | 1 - .../Extensions/InteractivityExtensions.cs | 2 +- .../IrdSearchResultFormatter.cs | 19 +- ...ResultFormatter.CurrentSettingsSections.cs | 24 +-- ...rserResultFormatter.GeneralNotesSection.cs | 39 ++-- ...serResultFormatter.WeirdSettingsSection.cs | 31 ++-- .../LogParserResultFormatter.cs | 41 +++-- .../ResultFormatters/TitlePatchFormatter.cs | 2 +- Tests/LogParsingProfiler.cs | 2 +- discord-bot-net.sln.DotSettings | 5 + 55 files changed, 543 insertions(+), 558 deletions(-) diff --git a/Clients/PsnClient/PsnClient.cs b/Clients/PsnClient/PsnClient.cs index e3996dc9..eada1fb1 100644 --- a/Clients/PsnClient/PsnClient.cs +++ b/Clients/PsnClient/PsnClient.cs @@ -171,17 +171,16 @@ namespace PsnClient } } - public async Task GetGameContainerAsync(string locale, string containerId, int start, int take, Dictionary? filters, CancellationToken cancellationToken) + public async Task GetGameContainerAsync(string locale, string containerId, int start, int take, Dictionary filters, CancellationToken cancellationToken) { try { var (language, country) = locale.AsLocaleData(); var url = new Uri($"https://store.playstation.com/valkyrie-api/{language}/{country}/999/container/{containerId}"); - filters ??= new Dictionary(); filters["start"] = start.ToString(); filters["size"] = take.ToString(); filters["bucket"] = "games"; - url = url.SetQueryParameters(filters); + url = url.SetQueryParameters(filters!); using var message = new HttpRequestMessage(HttpMethod.Get, url); using var response = await client.SendAsync(message, cancellationToken).ConfigureAwait(false); try diff --git a/CompatBot/Commands/ContentFilters.cs b/CompatBot/Commands/ContentFilters.cs index 0549d546..b2496325 100644 --- a/CompatBot/Commands/ContentFilters.cs +++ b/CompatBot/Commands/ContentFilters.cs @@ -212,7 +212,7 @@ namespace CompatBot.Commands ContentFilter.RebuildMatcher(); } - private async Task EditFilterCmd(CommandContext ctx, BotDb db, Piracystring filter) + private static async Task EditFilterCmd(CommandContext ctx, BotDb db, Piracystring filter) { var (success, msg) = await EditFilterPropertiesAsync(ctx, db, filter).ConfigureAwait(false); if (success) @@ -273,9 +273,10 @@ namespace CompatBot.Commands "Any simple string that is used to flag potential content for a check using Validation regex.\n" + "**Must** be sufficiently long to reduce the number of checks." ); - msg = await msg.UpdateOrCreateMessageAsync(ctx.Channel, "Please specify a new **trigger**", embed: embed).ConfigureAwait(false); errorMsg = null; - (msg, txt, emoji) = await interact.WaitForMessageOrReactionAsync(msg, ctx.User, InteractTimeout, abort, lastPage, nextPage, (filter.IsComplete() ? saveEdit : null)).ConfigureAwait(false); + msg = await msg.UpdateOrCreateMessageAsync(ctx.Channel, "Please specify a new **trigger**", embed: embed).ConfigureAwait(false); + var tmp = await interact.WaitForMessageOrReactionAsync(msg, ctx.User, InteractTimeout, abort, lastPage, nextPage, (filter.IsComplete() ? saveEdit : null)).ConfigureAwait(false); + (msg, txt, emoji) = tmp; //workaround compiler warning if (emoji != null) { if (emoji.Emoji == abort) @@ -317,9 +318,10 @@ namespace CompatBot.Commands $"**`L`** = **`{FilterContext.Log}`** will apply it during log parsing.\n" + "Reactions will toggle the context, text message will set the specified flags." ); - msg = await msg.UpdateOrCreateMessageAsync(ctx.Channel, "Please specify filter **context(s)**", embed: embed).ConfigureAwait(false); errorMsg = null; - (msg, txt, emoji) = await interact.WaitForMessageOrReactionAsync(msg, ctx.User, InteractTimeout, abort, previousPage, nextPage, letterC, letterL, (filter.IsComplete() ? saveEdit : null)).ConfigureAwait(false); + msg = await msg.UpdateOrCreateMessageAsync(ctx.Channel, "Please specify filter **context(s)**", embed: embed).ConfigureAwait(false); + tmp = await interact.WaitForMessageOrReactionAsync(msg, ctx.User, InteractTimeout, abort, previousPage, nextPage, letterC, letterL, (filter.IsComplete() ? saveEdit : null)).ConfigureAwait(false); + (msg, txt, emoji) = tmp; //workaround compiler warning if (emoji != null) { if (emoji.Emoji == abort) @@ -388,9 +390,10 @@ namespace CompatBot.Commands $"**`U`** = **`{FilterAction.MuteModQueue}`** mute mod queue reporting for this action.\n" + "Reactions will toggle the action, text message will set the specified flags." ); - msg = await msg.UpdateOrCreateMessageAsync(ctx.Channel, "Please specify filter **action(s)**", embed: embed).ConfigureAwait(false); errorMsg = null; - (msg, txt, emoji) = await interact.WaitForMessageOrReactionAsync(msg, ctx.User, InteractTimeout, abort, previousPage, nextPage, letterR, letterW, letterM, letterE, letterU, (filter.IsComplete() ? saveEdit : null)).ConfigureAwait(false); + msg = await msg.UpdateOrCreateMessageAsync(ctx.Channel, "Please specify filter **action(s)**", embed: embed).ConfigureAwait(false); + tmp = await interact.WaitForMessageOrReactionAsync(msg, ctx.User, InteractTimeout, abort, previousPage, nextPage, letterR, letterW, letterM, letterE, letterU, (filter.IsComplete() ? saveEdit : null)).ConfigureAwait(false); + (msg, txt, emoji) = tmp; //workaround compiler warning if (emoji != null) { if (emoji.Emoji == abort) @@ -497,10 +500,11 @@ namespace CompatBot.Commands "**Please [test](https://regex101.com/) your regex**. Following flags are enabled: Multiline, IgnoreCase.\n" + "Additional validation can help reduce false positives of a plaintext trigger match." ); - msg = await msg.UpdateOrCreateMessageAsync(ctx.Channel, "Please specify filter **validation regex**", embed: embed).ConfigureAwait(false); errorMsg = null; + msg = await msg.UpdateOrCreateMessageAsync(ctx.Channel, "Please specify filter **validation regex**", embed: embed).ConfigureAwait(false); var next = (filter.Actions & (FilterAction.SendMessage | FilterAction.ShowExplain)) == 0 ? firstPage : nextPage; - (msg, txt, emoji) = await interact.WaitForMessageOrReactionAsync(msg, ctx.User, InteractTimeout, abort, previousPage, next, (string.IsNullOrEmpty(filter.ValidatingRegex) ? null : trash), (filter.IsComplete() ? saveEdit : null)).ConfigureAwait(false); + tmp = await interact.WaitForMessageOrReactionAsync(msg, ctx.User, InteractTimeout, abort, previousPage, next, (string.IsNullOrEmpty(filter.ValidatingRegex) ? null : trash), (filter.IsComplete() ? saveEdit : null)).ConfigureAwait(false); + (msg, txt, emoji) = tmp; //workaround compiler warning if (emoji != null) { if (emoji.Emoji == abort) @@ -554,10 +558,11 @@ namespace CompatBot.Commands "Optional custom message sent to the user.\n" + "If left empty, default piracy warning message will be used." ); - msg = await msg.UpdateOrCreateMessageAsync(ctx.Channel, "Please specify filter **validation regex**", embed: embed).ConfigureAwait(false); errorMsg = null; + msg = await msg.UpdateOrCreateMessageAsync(ctx.Channel, "Please specify filter **validation regex**", embed: embed).ConfigureAwait(false); next = (filter.Actions.HasFlag(FilterAction.ShowExplain) ? nextPage : firstPage); - (msg, txt, emoji) = await interact.WaitForMessageOrReactionAsync(msg, ctx.User, InteractTimeout, abort, previousPage, next, (filter.IsComplete() ? saveEdit : null)).ConfigureAwait(false); + tmp = await interact.WaitForMessageOrReactionAsync(msg, ctx.User, InteractTimeout, abort, previousPage, next, (filter.IsComplete() ? saveEdit : null)).ConfigureAwait(false); + (msg, txt, emoji) = tmp; //workaround compiler warning if (emoji != null) { if (emoji.Emoji == abort) @@ -594,9 +599,10 @@ namespace CompatBot.Commands "Explanation term that is used to show an explanation.\n" + "**__Currently not implemented__**." ); - msg = await msg.UpdateOrCreateMessageAsync(ctx.Channel, "Please specify filter **explanation term**", embed: embed).ConfigureAwait(false); errorMsg = null; - (msg, txt, emoji) = await interact.WaitForMessageOrReactionAsync(msg, ctx.User, InteractTimeout, abort, previousPage, firstPage, (filter.IsComplete() ? saveEdit : null)).ConfigureAwait(false); + msg = await msg.UpdateOrCreateMessageAsync(ctx.Channel, "Please specify filter **explanation term**", embed: embed).ConfigureAwait(false); + tmp = await interact.WaitForMessageOrReactionAsync(msg, ctx.User, InteractTimeout, abort, previousPage, firstPage, (filter.IsComplete() ? saveEdit : null)).ConfigureAwait(false); + (msg, txt, emoji) = tmp; //workaround compiler warning if (emoji != null) { if (emoji.Emoji == abort) @@ -641,9 +647,10 @@ namespace CompatBot.Commands if (errorMsg == null && !filter.IsComplete()) errorMsg = "Some required properties are not defined"; embed = FormatFilter(filter, errorMsg); - msg = await msg.UpdateOrCreateMessageAsync(ctx.Channel, "Does this look good? (y/n)", embed: embed.Build()).ConfigureAwait(false); errorMsg = null; - (msg, txt, emoji) = await interact.WaitForMessageOrReactionAsync(msg, ctx.User, InteractTimeout, abort, previousPage, firstPage, (filter.IsComplete() ? saveEdit : null)).ConfigureAwait(false); + msg = await msg.UpdateOrCreateMessageAsync(ctx.Channel, "Does this look good? (y/n)", embed: embed.Build()).ConfigureAwait(false); + tmp = await interact.WaitForMessageOrReactionAsync(msg, ctx.User, InteractTimeout, abort, previousPage, firstPage, (filter.IsComplete() ? saveEdit : null)).ConfigureAwait(false); + (msg, txt, emoji) = tmp; //workaround compiler warning if (emoji != null) { if (emoji.Emoji == abort) diff --git a/CompatBot/Commands/Events.cs b/CompatBot/Commands/Events.cs index 6a6e1837..ab5332a3 100644 --- a/CompatBot/Commands/Events.cs +++ b/CompatBot/Commands/Events.cs @@ -10,7 +10,7 @@ namespace CompatBot.Commands internal sealed class Events: EventsBaseCommand { [GroupCommand] - public Task NearestGenericEvent(CommandContext ctx, [Description("Optional event name"), RemainingText] string eventName = null) + public Task NearestGenericEvent(CommandContext ctx, [Description("Optional event name"), RemainingText] string? eventName = null) => NearestEvent(ctx, eventName); [Command("add"), RequiresBotModRole] @@ -56,7 +56,7 @@ namespace CompatBot.Commands [Command("countdown")] [Description("Provides countdown for the nearest known event")] - public Task Countdown(CommandContext ctx, string eventName = null) + public Task Countdown(CommandContext ctx, string? eventName = null) => NearestEvent(ctx, eventName); } } diff --git a/CompatBot/Commands/EventsBaseCommand.cs b/CompatBot/Commands/EventsBaseCommand.cs index 0485cd7a..4ea63562 100644 --- a/CompatBot/Commands/EventsBaseCommand.cs +++ b/CompatBot/Commands/EventsBaseCommand.cs @@ -23,12 +23,12 @@ namespace CompatBot.Commands private static readonly Regex Duration = new Regex(@"((?\d+)(\.|d\s*))?((?\d+)(\:|h\s*))?((?\d+)m?)?", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.ExplicitCapture); - protected async Task NearestEvent(CommandContext ctx, string eventName = null) + protected static async Task NearestEvent(CommandContext ctx, string? eventName = null) { - var originalEventName = eventName = eventName.Trim(40); + var originalEventName = eventName = eventName?.Trim(40); var current = DateTime.UtcNow; var currentTicks = current.Ticks; - using var db = new BotDb(); + await using var db = new BotDb(); var currentEvents = await db.EventSchedule.OrderBy(e => e.End).Where(e => e.Start <= currentTicks && e.End >= currentTicks).ToListAsync().ConfigureAwait(false); var nextEvent = await db.EventSchedule.OrderBy(e => e.Start).FirstOrDefaultAsync(e => e.Start > currentTicks).ConfigureAwait(false); if (string.IsNullOrEmpty(eventName)) @@ -76,8 +76,8 @@ namespace CompatBot.Commands return; } - var noEventMsg = $"No information about the upcoming {eventName.Sanitize(replaceBackTicks: true)} at the moment"; - if (eventName.Length > 10) + var noEventMsg = $"No information about the upcoming {eventName?.Sanitize(replaceBackTicks: true)} at the moment"; + if (eventName?.Length > 10) noEventMsg = "No information about such event at the moment"; else if (ctx.User.Id == 259997001880436737ul || ctx.User.Id == 377190919327318018ul) { @@ -109,8 +109,8 @@ namespace CompatBot.Commands firstNamedEvent = await db.EventSchedule.OrderBy(e => e.Start).FirstOrDefaultAsync(e => e.Year >= current.Year + 1 && e.EventName == eventName).ConfigureAwait(false); if (firstNamedEvent == null) { - var noEventMsg = $"No information about the upcoming {eventName.Sanitize(replaceBackTicks: true)} at the moment"; - if (eventName.Length > 10) + var noEventMsg = $"No information about the upcoming {eventName?.Sanitize(replaceBackTicks: true)} at the moment"; + if (eventName?.Length > 10) noEventMsg = "No information about such event at the moment"; else if (ctx.User.Id == 259997001880436737ul || ctx.User.Id == 377190919327318018ul) { @@ -149,17 +149,15 @@ namespace CompatBot.Commands await ctx.SendAutosplitMessageAsync(msg.TrimEnd(), blockStart: "", blockEnd: "").ConfigureAwait(false); } - protected async Task Add(CommandContext ctx, string eventName = null) + protected static async Task Add(CommandContext ctx, string? eventName = null) { var evt = new EventSchedule(); var (success, msg) = await EditEventPropertiesAsync(ctx, evt, eventName).ConfigureAwait(false); if (success) { - using (var db = new BotDb()) - { - await db.EventSchedule.AddAsync(evt).ConfigureAwait(false); - await db.SaveChangesAsync().ConfigureAwait(false); - } + await using var db = new BotDb(); + await db.EventSchedule.AddAsync(evt).ConfigureAwait(false); + await db.SaveChangesAsync().ConfigureAwait(false); await ctx.ReactWithAsync(Config.Reactions.Success).ConfigureAwait(false); if (LimitedToSpamChannel.IsSpamChannel(ctx.Channel)) await msg.UpdateOrCreateMessageAsync(ctx.Channel, embed: FormatEvent(evt).WithTitle("Created new event schedule entry #" + evt.Id)).ConfigureAwait(false); @@ -170,41 +168,35 @@ namespace CompatBot.Commands await msg.UpdateOrCreateMessageAsync(ctx.Channel, "Event creation aborted").ConfigureAwait(false); } - protected async Task Remove(CommandContext ctx, params int[] ids) + protected static async Task Remove(CommandContext ctx, params int[] ids) { - int removedCount; - using (var db = new BotDb()) - { - var eventsToRemove = await db.EventSchedule.Where(e3e => ids.Contains(e3e.Id)).ToListAsync().ConfigureAwait(false); - db.EventSchedule.RemoveRange(eventsToRemove); - removedCount = await db.SaveChangesAsync().ConfigureAwait(false); - } + await using var db = new BotDb(); + var eventsToRemove = await db.EventSchedule.Where(e3e => ids.Contains(e3e.Id)).ToListAsync().ConfigureAwait(false); + db.EventSchedule.RemoveRange(eventsToRemove); + var removedCount = await db.SaveChangesAsync().ConfigureAwait(false); if (removedCount == ids.Length) await ctx.RespondAsync($"Event{StringUtils.GetSuffix(ids.Length)} successfully removed!").ConfigureAwait(false); else await ctx.RespondAsync($"Removed {removedCount} event{StringUtils.GetSuffix(removedCount)}, but was asked to remove {ids.Length}").ConfigureAwait(false); } - protected async Task Clear(CommandContext ctx, int? year = null) + protected static async Task Clear(CommandContext ctx, int? year = null) { var currentYear = DateTime.UtcNow.Year; - int removedCount; - using (var db = new BotDb()) - { - var itemsToRemove = await db.EventSchedule.Where(e => - year.HasValue - ? e.Year == year - : e.Year < currentYear - ).ToListAsync().ConfigureAwait(false); - db.EventSchedule.RemoveRange(itemsToRemove); - removedCount = await db.SaveChangesAsync().ConfigureAwait(false); - } + await using var db = new BotDb(); + var itemsToRemove = await db.EventSchedule.Where(e => + year.HasValue + ? e.Year == year + : e.Year < currentYear + ).ToListAsync().ConfigureAwait(false); + db.EventSchedule.RemoveRange(itemsToRemove); + var removedCount = await db.SaveChangesAsync().ConfigureAwait(false); await ctx.RespondAsync($"Removed {removedCount} event{(removedCount == 1 ? "" : "s")}").ConfigureAwait(false); } - protected async Task Update(CommandContext ctx, int id, string eventName = null) + protected static async Task Update(CommandContext ctx, int id, string? eventName = null) { - using var db = new BotDb(); + await using var db = new BotDb(); var evt = eventName == null ? db.EventSchedule.FirstOrDefault(e => e.Id == id) : db.EventSchedule.FirstOrDefault(e => e.Id == id && e.EventName == eventName); @@ -224,36 +216,31 @@ namespace CompatBot.Commands await msg.UpdateOrCreateMessageAsync(ctx.Channel, "Updated the schedule entry").ConfigureAwait(false); } else - { await msg.UpdateOrCreateMessageAsync(ctx.Channel, "Event update aborted, changes weren't saved").ConfigureAwait(false); - } } - protected async Task List(CommandContext ctx, string eventName = null, int? year = null) + protected static async Task List(CommandContext ctx, string? eventName = null, int? year = null) { var showAll = "all".Equals(eventName, StringComparison.InvariantCultureIgnoreCase); var currentTicks = DateTime.UtcNow.Ticks; - List events; - using (var db = new BotDb()) + await using var db = new BotDb(); + IQueryable query = db.EventSchedule; + if (year.HasValue) + query = query.Where(e => e.Year == year); + else { - IQueryable query = db.EventSchedule; - if (year.HasValue) - query = query.Where(e => e.Year == year); - else - { - if (!ctx.Channel.IsPrivate && !showAll) - query = query.Where(e => e.End > currentTicks); - } - if (!string.IsNullOrEmpty(eventName) && !showAll) - { - eventName = await FuzzyMatchEventName(db, eventName).ConfigureAwait(false); - query = query.Where(e => e.EventName == eventName); - } - events = await query - .OrderBy(e => e.Start) - .ToListAsync() - .ConfigureAwait(false); + if (!ctx.Channel.IsPrivate && !showAll) + query = query.Where(e => e.End > currentTicks); } + if (!string.IsNullOrEmpty(eventName) && !showAll) + { + eventName = await FuzzyMatchEventName(db, eventName).ConfigureAwait(false); + query = query.Where(e => e.EventName == eventName); + } + List events = await query + .OrderBy(e => e.Start) + .ToListAsync() + .ConfigureAwait(false); if (events.Count == 0) { await ctx.RespondAsync("There are no events to show").ConfigureAwait(false); @@ -280,7 +267,7 @@ namespace CompatBot.Commands var printName = string.IsNullOrEmpty(currentEvent) ? "Various independent events" : $"**{currentEvent} {currentYear} schedule**"; msg.AppendLine($"{printName} (UTC):"); } - msg.Append(StringUtils.InvisibleSpacer).Append("`"); + msg.Append(StringUtils.InvisibleSpacer).Append('`'); if (ModProvider.IsMod(ctx.Message.Author.Id)) msg.Append($"[{evt.Id:0000}] "); msg.Append($"{evt.Start.AsUtc():u}"); @@ -292,7 +279,7 @@ namespace CompatBot.Commands await ch.SendAutosplitMessageAsync(msg, blockStart: "", blockEnd: "").ConfigureAwait(false); } - private async Task<(bool success, DiscordMessage message)> EditEventPropertiesAsync(CommandContext ctx, EventSchedule evt, string eventName = null) + private static async Task<(bool success, DiscordMessage? message)> EditEventPropertiesAsync(CommandContext ctx, EventSchedule evt, string? eventName = null) { var interact = ctx.Client.GetInteractivity(); var abort = DiscordEmoji.FromUnicode("🛑"); @@ -304,17 +291,18 @@ namespace CompatBot.Commands var saveEdit = DiscordEmoji.FromUnicode("💾"); var skipEventNameStep = !string.IsNullOrEmpty(eventName); - DiscordMessage msg = null; - string errorMsg = null; - DiscordMessage txt; - MessageReactionAddEventArgs emoji; + DiscordMessage? msg = null; + string? errorMsg = null; + DiscordMessage? txt; + MessageReactionAddEventArgs? emoji; step1: // step 1: get the new start date var embed = FormatEvent(evt, errorMsg, 1).WithDescription($"Example: `{DateTime.UtcNow:yyyy-MM-dd HH:mm} PST`\nBy default all times use UTC, only limited number of time zones supported"); - msg = await msg.UpdateOrCreateMessageAsync(ctx.Channel, "Please specify a new **start date and time**", embed: embed).ConfigureAwait(false); errorMsg = null; - (msg, txt, emoji) = await interact.WaitForMessageOrReactionAsync(msg, ctx.User, InteractTimeout, abort, lastPage, nextPage, (evt.IsComplete() ? saveEdit : null)).ConfigureAwait(false); + msg = await msg.UpdateOrCreateMessageAsync(ctx.Channel, "Please specify a new **start date and time**", embed: embed).ConfigureAwait(false); + var tmp = await interact.WaitForMessageOrReactionAsync(msg, ctx.User, InteractTimeout, abort, lastPage, nextPage, (evt.IsComplete() ? saveEdit : null)).ConfigureAwait(false); + (msg, txt, emoji) = tmp; // nullability code analysis workaround if (emoji != null) { if (emoji.Emoji == abort) @@ -348,9 +336,10 @@ namespace CompatBot.Commands step2: // step 2: get the new duration embed = FormatEvent(evt, errorMsg, 2).WithDescription("Example: `2d 1h 15m`, or `2.1:00`"); - msg = await msg.UpdateOrCreateMessageAsync(ctx.Channel, "Please specify a new **event duration**", embed: embed.Build()).ConfigureAwait(false); errorMsg = null; - (msg, txt, emoji) = await interact.WaitForMessageOrReactionAsync(msg, ctx.User, InteractTimeout, abort, previousPage, nextPage, (evt.IsComplete() ? saveEdit : null)).ConfigureAwait(false); + msg = await msg.UpdateOrCreateMessageAsync(ctx.Channel, "Please specify a new **event duration**", embed: embed.Build()).ConfigureAwait(false); + tmp = await interact.WaitForMessageOrReactionAsync(msg, ctx.User, InteractTimeout, abort, previousPage, nextPage, (evt.IsComplete() ? saveEdit : null)).ConfigureAwait(false); + (msg, txt, emoji) = tmp; // nullability code analysis workaround if (emoji != null) { if (emoji.Emoji == abort) @@ -382,9 +371,10 @@ namespace CompatBot.Commands step3: // step 3: get the new event name embed = FormatEvent(evt, errorMsg, 3); - msg = await msg.UpdateOrCreateMessageAsync(ctx.Channel, "Please specify a new **event name**", embed: embed.Build()).ConfigureAwait(false); errorMsg = null; - (msg, txt, emoji) = await interact.WaitForMessageOrReactionAsync(msg, ctx.User, InteractTimeout, abort, previousPage, (string.IsNullOrEmpty(evt.EventName) ? null : trash), nextPage, (evt.IsComplete() ? saveEdit : null)).ConfigureAwait(false); + msg = await msg.UpdateOrCreateMessageAsync(ctx.Channel, "Please specify a new **event name**", embed: embed.Build()).ConfigureAwait(false); + tmp = await interact.WaitForMessageOrReactionAsync(msg, ctx.User, InteractTimeout, abort, previousPage, (string.IsNullOrEmpty(evt.EventName) ? null : trash), nextPage, (evt.IsComplete() ? saveEdit : null)).ConfigureAwait(false); + (msg, txt, emoji) = tmp; // nullability code analysis workaround if (emoji != null) { if (emoji.Emoji == abort) @@ -409,7 +399,8 @@ namespace CompatBot.Commands embed = FormatEvent(evt, errorMsg, 4); msg = await msg.UpdateOrCreateMessageAsync(ctx.Channel, "Please specify a new **schedule entry title**", embed: embed.Build()).ConfigureAwait(false); errorMsg = null; - (msg, txt, emoji) = await interact.WaitForMessageOrReactionAsync(msg, ctx.User, InteractTimeout, abort, previousPage, firstPage, (evt.IsComplete() ? saveEdit : null)).ConfigureAwait(false); + tmp = await interact.WaitForMessageOrReactionAsync(msg, ctx.User, InteractTimeout, abort, previousPage, firstPage, (evt.IsComplete() ? saveEdit : null)).ConfigureAwait(false); + (msg, txt, emoji) = tmp; // nullability code analysis workaround if (emoji != null) { if (emoji.Emoji == abort) @@ -448,7 +439,8 @@ namespace CompatBot.Commands embed = FormatEvent(evt, errorMsg); msg = await msg.UpdateOrCreateMessageAsync(ctx.Channel, "Does this look good? (y/n)", embed: embed.Build()).ConfigureAwait(false); errorMsg = null; - (msg, txt, emoji) = await interact.WaitForMessageOrReactionAsync(msg, ctx.User, InteractTimeout, abort, previousPage, firstPage, (evt.IsComplete() ? saveEdit : null)).ConfigureAwait(false); + tmp = await interact.WaitForMessageOrReactionAsync(msg, ctx.User, InteractTimeout, abort, previousPage, firstPage, (evt.IsComplete() ? saveEdit : null)).ConfigureAwait(false); + (msg, txt, emoji) = tmp; // nullability code analysis workaround if (emoji != null) { if (emoji.Emoji == abort) @@ -497,7 +489,7 @@ namespace CompatBot.Commands return (false, msg); } - private static string NameWithoutLink(string name) + private static string? NameWithoutLink(string? name) { if (string.IsNullOrEmpty(name)) return name; @@ -514,14 +506,14 @@ namespace CompatBot.Commands return name; } - private static async Task FuzzyMatchEventName(BotDb db, string eventName) + private static async Task FuzzyMatchEventName(BotDb db, string? eventName) { var knownEventNames = await db.EventSchedule.Select(e => e.EventName).Distinct().ToListAsync().ConfigureAwait(false); var (score, name) = knownEventNames.Select(n => (score: eventName.GetFuzzyCoefficientCached(n), name: n)).OrderByDescending(t => t.score).FirstOrDefault(); return score > 0.8 ? name : eventName; } - private static async Task FuzzyMatchEntryName(BotDb db, string eventName) + private static async Task FuzzyMatchEntryName(BotDb db, string? eventName) { var now = DateTime.UtcNow.Ticks; var knownNames = await db.EventSchedule.Where(e => e.End > now).Select(e => e.Name).ToListAsync().ConfigureAwait(false); @@ -539,9 +531,9 @@ namespace CompatBot.Commands return null; } - int.TryParse(d.Groups["days"].Value, out var days); - int.TryParse(d.Groups["hours"].Value, out var hours); - int.TryParse(d.Groups["mins"].Value, out var mins); + _ = int.TryParse(d.Groups["days"].Value, out var days); + _ = int.TryParse(d.Groups["hours"].Value, out var hours); + _ = int.TryParse(d.Groups["mins"].Value, out var mins); if (days == 0 && hours == 0 && mins == 0) { if (react) @@ -575,7 +567,7 @@ namespace CompatBot.Commands return result; } - private static DiscordEmbedBuilder FormatEvent(EventSchedule evt, string error = null, int highlight = -1) + private static DiscordEmbedBuilder FormatEvent(EventSchedule evt, string? error = null, int highlight = -1) { var start = evt.Start.AsUtc(); var field = 1; diff --git a/CompatBot/Commands/Explain.cs b/CompatBot/Commands/Explain.cs index 5f849c5c..f6c741f0 100644 --- a/CompatBot/Commands/Explain.cs +++ b/CompatBot/Commands/Explain.cs @@ -258,7 +258,7 @@ namespace CompatBot.Commands term = term.ToLowerInvariant().StripQuotes(); await using var db = new BotDb(); var item = await db.Explanation.FirstOrDefaultAsync(e => e.Keyword == term).ConfigureAwait(false); - if (item == null) + if (item is null) await ctx.ReactWithAsync(Config.Reactions.Failure, $"Term `{term}` is not defined").ConfigureAwait(false); else { @@ -275,7 +275,7 @@ namespace CompatBot.Commands term = term.ToLowerInvariant().StripQuotes(); await using var db = new BotDb(); var item = await db.Explanation.FirstOrDefaultAsync(e => e.Keyword == term).ConfigureAwait(false); - if (item == null) + if (item is null) await ctx.ReactWithAsync(Config.Reactions.Failure, $"Term `{term}` is not defined").ConfigureAwait(false); else if (string.IsNullOrEmpty(item.AttachmentFilename)) await ctx.ReactWithAsync(Config.Reactions.Failure, $"Term `{term}` doesn't have any attachments").ConfigureAwait(false); @@ -314,7 +314,7 @@ namespace CompatBot.Commands [Command("dump"), Aliases("download")] [Description("Returns explanation text as a file attachment")] - public async Task Dump(CommandContext ctx, [RemainingText, Description("Term to dump **or** a link to a message containing the explanation")] string termOrLink = null) + public async Task Dump(CommandContext ctx, [RemainingText, Description("Term to dump **or** a link to a message containing the explanation")] string? termOrLink = null) { if (string.IsNullOrEmpty(termOrLink)) { @@ -336,7 +336,7 @@ namespace CompatBot.Commands await using var db = new BotDb(); var item = await db.Explanation.FirstOrDefaultAsync(e => e.Keyword == termOrLink).ConfigureAwait(false); - if (item == null) + if (item is null) { var term = ctx.Message.Content.Split(' ', 2).Last(); await ShowExplanation(ctx, term).ConfigureAwait(false); diff --git a/CompatBot/Commands/Invites.cs b/CompatBot/Commands/Invites.cs index 39e8df6f..ebbf3c83 100644 --- a/CompatBot/Commands/Invites.cs +++ b/CompatBot/Commands/Invites.cs @@ -38,12 +38,12 @@ namespace CompatBot.Commands ); foreach (var item in whitelistedInvites) { - string guildName = null; + string? guildName = null; if (!string.IsNullOrEmpty(item.InviteCode)) try { var invite = await ctx.Client.GetInviteByCodeAsync(item.InviteCode).ConfigureAwait(false); - guildName = invite?.Guild.Name; + guildName = invite.Guild.Name; } catch { } if (string.IsNullOrEmpty(guildName)) diff --git a/CompatBot/Commands/Misc.cs b/CompatBot/Commands/Misc.cs index fda0c6cc..96dc47e1 100644 --- a/CompatBot/Commands/Misc.cs +++ b/CompatBot/Commands/Misc.cs @@ -145,9 +145,9 @@ namespace CompatBot.Commands [Command("roll")] [Description("Generates a random number between 1 and maxValue. Can also roll dices like `2d6`. Default is 1d6")] - public async Task Roll(CommandContext ctx, [Description("Some positive natural number")] int maxValue = 6, [RemainingText, Description("Optional text")] string comment = null) + public async Task Roll(CommandContext ctx, [Description("Some positive natural number")] int maxValue = 6, [RemainingText, Description("Optional text")] string? comment = null) { - string result = null; + string? result = null; if (maxValue > 1) lock (rng) result = (rng.Next(maxValue) + 1).ToString(); if (string.IsNullOrEmpty(result)) @@ -177,8 +177,7 @@ namespace CompatBot.Commands lock (rng) rolls = Enumerable.Range(0, num).Select(_ => rng.Next(face) + 1).ToList(); var total = rolls.Sum(); var totalStr = total.ToString(); - int.TryParse(m.Groups["mod"].Value, out var mod); - if (mod > 0) + if (int.TryParse(m.Groups["mod"].Value, out var mod) && mod > 0) totalStr += $" + {mod} = {total + mod}"; var rollsStr = string.Join(' ', rolls); if (rolls.Count > 1) @@ -322,8 +321,9 @@ namespace CompatBot.Commands var kdUser = await ctx.Client.GetUserAsync(272631898877198337ul).ConfigureAwait(false); var kdMember = ctx.Client.GetMember(kdUser); var kdMatch = new HashSet(new[] {kdUser.Id.ToString(), kdUser.Username, kdMember?.DisplayName ?? "kd-11", "kd", "kd-11", "kd11", }); - var botMember = ctx.Client.GetMember(ctx.Client.CurrentUser); - var botMatch = new HashSet(new[] {botMember.Id.ToString(), botMember.Username, botMember.DisplayName, "yourself", "urself", "yoself",}); + var botUser = ctx.Client.CurrentUser; + var botMember = ctx.Client.GetMember(botUser); + var botMatch = new HashSet(new[] {botUser.Id.ToString(), botUser.Username, botMember?.DisplayName ?? "RPCS3 bot", "yourself", "urself", "yoself",}); var prefix = DateTime.UtcNow.ToString("yyyyMMddHH"); var words = whatever.Split(Separators); @@ -350,7 +350,7 @@ namespace CompatBot.Commands word = word[..^2]; } - void MakeCustomRoleRating(DiscordMember mem) + void MakeCustomRoleRating(DiscordMember? mem) { if (mem != null && !choiceFlags.Contains('f')) { @@ -372,7 +372,7 @@ namespace CompatBot.Commands } var appended = false; - DiscordMember member = null; + DiscordMember? member = null; if (Me.Contains(word)) { member = ctx.Member; @@ -401,7 +401,7 @@ namespace CompatBot.Commands result.Clear(); appended = true; } - if (member == null && i == 0 && await ctx.ResolveMemberAsync(word).ConfigureAwait(false) is DiscordMember m) + if (member is null && i == 0 && await ctx.ResolveMemberAsync(word).ConfigureAwait(false) is DiscordMember m) member = m; if (member != null) { @@ -435,7 +435,7 @@ namespace CompatBot.Commands } if (!appended) result.Append(word); - result.Append(suffix).Append(" "); + result.Append(suffix).Append(' '); } whatever = result.ToString(); var cutIdx = whatever.LastIndexOf("never mind"); @@ -465,7 +465,7 @@ namespace CompatBot.Commands [Command("meme"), Aliases("memes"), Cooldown(1, 30, CooldownBucketType.Channel), Hidden] [Description("No, memes are not implemented yet")] - public async Task Memes(CommandContext ctx, [RemainingText] string _ = null) + public async Task Memes(CommandContext ctx, [RemainingText] string? _ = null) { var ch = await ctx.GetChannelForSpamAsync().ConfigureAwait(false); await ch.SendMessageAsync($"{ctx.User.Mention} congratulations, you're the meme").ConfigureAwait(false); @@ -488,8 +488,8 @@ namespace CompatBot.Commands public async Task ProductCode(CommandContext ctx, [RemainingText, Description("Product code such as BLUS12345 or SCES")] string productCode) { productCode = ProductCodeLookup.GetProductIds(productCode).FirstOrDefault() ?? productCode; - productCode = productCode?.ToUpperInvariant(); - if (productCode?.Length > 3) + productCode = productCode.ToUpperInvariant(); + if (productCode.Length > 3) { var dsc = ProductCodeDecoder.Decode(productCode); var info = string.Join('\n', dsc); diff --git a/CompatBot/Commands/Moderation.cs b/CompatBot/Commands/Moderation.cs index 8ce45e01..2632f399 100644 --- a/CompatBot/Commands/Moderation.cs +++ b/CompatBot/Commands/Moderation.cs @@ -14,7 +14,7 @@ namespace CompatBot.Commands { [Command("report"), RequiresWhitelistedRole] [Description("Adds specified message to the moderation queue")] - public async Task Report(CommandContext ctx, [Description("Message ID from current channel to report")] ulong messageId, [RemainingText, Description("Optional report comment")] string comment = null) + public async Task Report(CommandContext ctx, [Description("Message ID from current channel to report")] ulong messageId, [RemainingText, Description("Optional report comment")] string? comment = null) { try { @@ -29,7 +29,7 @@ namespace CompatBot.Commands [Command("report"), RequiresWhitelistedRole] [Description("Adds specified message to the moderation queue")] - public async Task Report(CommandContext ctx, [Description("Message link to report")] string messageLink, [RemainingText, Description("Optional report comment")] string comment = null) + public async Task Report(CommandContext ctx, [Description("Message link to report")] string messageLink, [RemainingText, Description("Optional report comment")] string? comment = null) { try { @@ -94,10 +94,10 @@ namespace CompatBot.Commands await ctx.ReactWithAsync(Config.Reactions.Success).ConfigureAwait(false); } - public static async Task ToggleBadUpdateAnnouncementAsync(DiscordMessage message) + public static async Task ToggleBadUpdateAnnouncementAsync(DiscordMessage? message) { var embed = message?.Embeds?.FirstOrDefault(); - if (embed == null) + if (message is null || embed is null) return; var result = new DiscordEmbedBuilder(embed); @@ -132,7 +132,7 @@ namespace CompatBot.Commands await message.UpdateOrCreateMessageAsync(message.Channel, embed: result).ConfigureAwait(false); } - private static async Task ReportMessage(CommandContext ctx, string comment, DiscordMessage msg) + private static async Task ReportMessage(CommandContext ctx, string? comment, DiscordMessage msg) { if (msg.Reactions.Any(r => r.IsMe && r.Emoji == Config.Reactions.Moderated)) { diff --git a/CompatBot/Commands/Psn.Check.cs b/CompatBot/Commands/Psn.Check.cs index d0212063..0e0b299d 100644 --- a/CompatBot/Commands/Psn.Check.cs +++ b/CompatBot/Commands/Psn.Check.cs @@ -24,7 +24,7 @@ namespace CompatBot.Commands [Description("Commands to check for various stuff on PSN")] public sealed class Check: BaseCommandModuleCustom { - private static string latestFwVersion = null; + private static string? latestFwVersion = null; [Command("updates"), Aliases("update")] [Description("Checks if specified product has any updates")] @@ -75,10 +75,8 @@ namespace CompatBot.Commands if (!ctx.Channel.IsPrivate && ctx.Message.Author.Id == 197163728867688448 - && ( - embeds[0].Title.Contains("africa", StringComparison.InvariantCultureIgnoreCase) || - embeds[0].Title.Contains("afrika", StringComparison.InvariantCultureIgnoreCase) - )) + && (embeds[0].Title.Contains("africa", StringComparison.InvariantCultureIgnoreCase) + || embeds[0].Title.Contains("afrika", StringComparison.InvariantCultureIgnoreCase))) { foreach (var embed in embeds) { @@ -90,10 +88,10 @@ namespace CompatBot.Commands if (!string.IsNullOrEmpty(embed.Thumbnail?.Url)) embed.WithThumbnail("https://cdn.discordapp.com/attachments/417347469521715210/516340151589535745/onionoff.png"); } - var sqvat = ctx.Client.GetEmoji(":sqvat:", Config.Reactions.No); + var sqvat = ctx.Client.GetEmoji(":sqvat:", Config.Reactions.No)!; await ctx.Message.ReactWithAsync(sqvat).ConfigureAwait(false); } - if (embeds.Count > 1 || embeds[0].Fields?.Count > 0) + if (embeds.Count > 1 || embeds[0].Fields.Count > 0) embeds[^1] = embeds.Last().WithFooter("Note that you need to install ALL listed updates, one by one"); foreach (var embed in embeds) await ctx.RespondAsync(embed: embed).ConfigureAwait(false); @@ -135,7 +133,7 @@ namespace CompatBot.Commands await ctx.RespondAsync(embed: embed).ConfigureAwait(false); } - internal static async Task CheckFwUpdateForAnnouncementAsync(DiscordClient client, List fwList = null) + internal static async Task CheckFwUpdateForAnnouncementAsync(DiscordClient client, List? fwList = null) { fwList ??= await Client.GetHighestFwVersionAsync(Config.Cts.Token).ConfigureAwait(false); if (fwList.Count == 0) diff --git a/CompatBot/Commands/Psn.cs b/CompatBot/Commands/Psn.cs index d77c072f..e860aa7b 100644 --- a/CompatBot/Commands/Psn.cs +++ b/CompatBot/Commands/Psn.cs @@ -1,20 +1,14 @@ -using System; -using System.Collections.Generic; -using System.Linq; +using System.Linq; using System.Threading.Tasks; using CompatBot.Commands.Attributes; using CompatBot.Database; -using CompatBot.Database.Providers; using CompatBot.EventHandlers; using CompatBot.ThumbScrapper; using CompatBot.Utils; using DSharpPlus.CommandsNext; using DSharpPlus.CommandsNext.Attributes; using DSharpPlus.Entities; -using DSharpPlus.Interactivity; -using Microsoft.EntityFrameworkCore; using PsnClient; -using PsnClient.POCOs; namespace CompatBot.Commands { @@ -23,15 +17,13 @@ namespace CompatBot.Commands internal sealed partial class Psn: BaseCommandModuleCustom { private static readonly Client Client = new Client(); - private static readonly DiscordColor PsnBlue = new DiscordColor(0x0071cd); - [Command("rename"), Aliases("setname", "settitle"), RequiresBotModRole] [Description("Command to set or change game title for specific product code")] public async Task Rename(CommandContext ctx, [Description("Product code such as BLUS12345")] string productCode, [RemainingText, Description("New game title to save in the database")] string title) { productCode = productCode.ToUpperInvariant(); - using var db = new ThumbnailDb(); + await using var db = new ThumbnailDb(); var item = db.Thumbnail.FirstOrDefault(t => t.ProductCode == productCode); if (item == null) await ctx.ReactWithAsync(Config.Reactions.Failure, $"Unknown product code {productCode}", true).ConfigureAwait(false); @@ -48,9 +40,9 @@ namespace CompatBot.Commands public async Task Add(CommandContext ctx, [Description("Product code such as BLUS12345")] string contentId, [RemainingText, Description("New game title to save in the database")] string title) { contentId = contentId.ToUpperInvariant(); - var productCode = contentId; var productCodeMatch = ProductCodeLookup.ProductCode.Match(contentId); var contentIdMatch = PsnScraper.ContentIdMatcher.Match(contentId); + string productCode; if (contentIdMatch.Success) { productCode = contentIdMatch.Groups["product_id"].Value; @@ -58,30 +50,30 @@ namespace CompatBot.Commands else if (productCodeMatch.Success) { productCode = productCodeMatch.Groups["letters"].Value + productCodeMatch.Groups["numbers"].Value; - contentId = null; + contentId = ""; } else { await ctx.ReactWithAsync(Config.Reactions.Failure, "Invalid content id", true).ConfigureAwait(false); return; } - - using var db = new ThumbnailDb(); + + await using var db = new ThumbnailDb(); var item = db.Thumbnail.FirstOrDefault(t => t.ProductCode == productCode); - if (item != null) - await ctx.ReactWithAsync(Config.Reactions.Failure, $"Product code {contentId} already exists", true).ConfigureAwait(false); - else + if (item is null) { item = new Thumbnail { - ProductCode = contentId, - ContentId = contentId, + ProductCode = productCode, + ContentId = string.IsNullOrEmpty(contentId) ? null : contentId, Name = title, }; - db.Add(item); + await db.AddAsync(item).ConfigureAwait(false); await db.SaveChangesAsync().ConfigureAwait(false); await ctx.ReactWithAsync(Config.Reactions.Success, "Title added successfully").ConfigureAwait(false); } + else + await ctx.ReactWithAsync(Config.Reactions.Failure, $"Product code {contentId} already exists", true).ConfigureAwait(false); } } } diff --git a/CompatBot/Commands/Sudo.Bot.Configuration.cs b/CompatBot/Commands/Sudo.Bot.Configuration.cs index a90a16d7..179e394e 100644 --- a/CompatBot/Commands/Sudo.Bot.Configuration.cs +++ b/CompatBot/Commands/Sudo.Bot.Configuration.cs @@ -23,17 +23,17 @@ namespace CompatBot.Commands [Description("Lists set variable names")] public async Task List(CommandContext ctx) { - using var db = new BotDb(); - var setVars = await db.BotState.AsNoTracking().Where(v => v.Key.StartsWith(SqlConfiguration.ConfigVarPrefix)).ToListAsync().ConfigureAwait(false); + await using var db = new BotDb(); + var setVars = await db.BotState.AsNoTracking().Where(v => v.Key != null && v.Key.StartsWith(SqlConfiguration.ConfigVarPrefix)).ToListAsync().ConfigureAwait(false); if (setVars.Any()) { var result = new StringBuilder("Set variables:").AppendLine(); foreach (var v in setVars) { #if DEBUG - result.Append(v.Key[SqlConfiguration.ConfigVarPrefix.Length ..]).Append(" = ").AppendLine(v.Value); + result.Append(v.Key![SqlConfiguration.ConfigVarPrefix.Length ..]).Append(" = ").AppendLine(v.Value); #else - result.AppendLine(v.Key[(SqlConfiguration.ConfigVarPrefix.Length)..]); + result.AppendLine(v.Key![(SqlConfiguration.ConfigVarPrefix.Length)..]); #endif } await ctx.RespondAsync(result.ToString()).ConfigureAwait(false); @@ -49,12 +49,12 @@ namespace CompatBot.Commands Config.inMemorySettings[key] = value; Config.RebuildConfiguration(); key = SqlConfiguration.ConfigVarPrefix + key; - using var db = new BotDb(); + await using var db = new BotDb(); var v = await db.BotState.Where(v => v.Key == key).FirstOrDefaultAsync().ConfigureAwait(false); if (v == null) { v = new BotState {Key = key, Value = value}; - db.BotState.Add(v); + await db.BotState.AddAsync(v).ConfigureAwait(false); } else v.Value = value; @@ -69,7 +69,7 @@ namespace CompatBot.Commands Config.inMemorySettings.TryRemove(key, out _); Config.RebuildConfiguration(); key = SqlConfiguration.ConfigVarPrefix + key; - using var db = new BotDb(); + await using var db = new BotDb(); var v = await db.BotState.Where(v => v.Key == key).FirstOrDefaultAsync().ConfigureAwait(false); if (v != null) { diff --git a/CompatBot/Commands/Sudo.Bot.cs b/CompatBot/Commands/Sudo.Bot.cs index c5b20b55..9c11099b 100644 --- a/CompatBot/Commands/Sudo.Bot.cs +++ b/CompatBot/Commands/Sudo.Bot.cs @@ -40,7 +40,7 @@ namespace CompatBot.Commands }; git.Start(); var stdout = await git.StandardOutput.ReadToEndAsync().ConfigureAwait(false); - git.WaitForExit(); + await git.WaitForExitAsync().ConfigureAwait(false); if (!string.IsNullOrEmpty(stdout)) await ctx.RespondAsync("```" + stdout + "```").ConfigureAwait(false); } @@ -51,7 +51,7 @@ namespace CompatBot.Commands { if (await lockObj.WaitAsync(0).ConfigureAwait(false)) { - DiscordMessage msg = null; + DiscordMessage? msg = null; try { Config.Log.Info("Checking for available updates..."); @@ -69,7 +69,7 @@ namespace CompatBot.Commands } catch (Exception e) { - msg = await msg.UpdateOrCreateMessageAsync(ctx.Channel, "Updating failed: " + e.Message).ConfigureAwait(false); + await msg.UpdateOrCreateMessageAsync(ctx.Channel, "Updating failed: " + e.Message).ConfigureAwait(false); } finally { @@ -86,7 +86,7 @@ namespace CompatBot.Commands { if (await lockObj.WaitAsync(0).ConfigureAwait(false)) { - DiscordMessage msg = null; + DiscordMessage? msg = null; try { msg = await ctx.RespondAsync("Saving state...").ConfigureAwait(false); @@ -96,7 +96,7 @@ namespace CompatBot.Commands } catch (Exception e) { - msg = await msg.UpdateOrCreateMessageAsync(ctx.Channel, "Restarting failed: " + e.Message).ConfigureAwait(false); + await msg.UpdateOrCreateMessageAsync(ctx.Channel, "Restarting failed: " + e.Message).ConfigureAwait(false); } finally { @@ -126,7 +126,7 @@ namespace CompatBot.Commands { try { - using var db = new BotDb(); + await using var db = new BotDb(); var status = await db.BotState.FirstOrDefaultAsync(s => s.Key == "bot-status-activity").ConfigureAwait(false); var txt = await db.BotState.FirstOrDefaultAsync(s => s.Key == "bot-status-text").ConfigureAwait(false); if (Enum.TryParse(activity, true, out ActivityType activityType) @@ -164,7 +164,7 @@ namespace CompatBot.Commands try { await CompatList.ImportMetacriticScoresAsync().ConfigureAwait(false); - using var db = new ThumbnailDb(); + await using var db = new ThumbnailDb(); var linkedItems = await db.Thumbnail.CountAsync(i => i.MetacriticId != null).ConfigureAwait(false); await ctx.RespondAsync($"Importing Metacritic info was successful, linked {linkedItems} items").ConfigureAwait(false); } @@ -190,7 +190,7 @@ namespace CompatBot.Commands }; git.Start(); var stdout = await git.StandardOutput.ReadToEndAsync().ConfigureAwait(false); - git.WaitForExit(); + await git.WaitForExitAsync().ConfigureAwait(false); if (string.IsNullOrEmpty(stdout)) return (false, stdout); @@ -200,7 +200,7 @@ namespace CompatBot.Commands return (true, stdout); } - internal static void Restart(ulong channelId, string restartMsg) + internal static void Restart(ulong channelId, string? restartMsg) { Config.Log.Info($"Saving channelId {channelId} into settings..."); using var db = new BotDb(); diff --git a/CompatBot/Commands/Sudo.Fix.cs b/CompatBot/Commands/Sudo.Fix.cs index 8c606565..2dfa1ca2 100644 --- a/CompatBot/Commands/Sudo.Fix.cs +++ b/CompatBot/Commands/Sudo.Fix.cs @@ -29,21 +29,19 @@ namespace CompatBot.Commands try { var @fixed = 0; - using (var db = new BotDb()) - { - foreach (var warning in db.Warning) - if (!string.IsNullOrEmpty(warning.FullReason)) + await using var db = new BotDb(); + foreach (var warning in db.Warning) + if (!string.IsNullOrEmpty(warning.FullReason)) + { + var match = Timestamp.Match(warning.FullReason); + if (match.Success && DateTime.TryParse(match.Groups["date"].Value, out var timestamp)) { - var match = Timestamp.Match(warning.FullReason); - if (match.Success && DateTime.TryParse(match.Groups["date"].Value, out var timestamp)) - { - warning.Timestamp = timestamp.Ticks; - warning.FullReason = warning.FullReason[(match.Groups["cutout"].Value.Length)..]; - @fixed++; - } + warning.Timestamp = timestamp.Ticks; + warning.FullReason = warning.FullReason[(match.Groups["cutout"].Value.Length)..]; + @fixed++; } - await db.SaveChangesAsync().ConfigureAwait(false); - } + } + await db.SaveChangesAsync().ConfigureAwait(false); await ctx.RespondAsync($"Fixed {@fixed} records").ConfigureAwait(false); } catch (Exception e) @@ -60,19 +58,17 @@ namespace CompatBot.Commands try { var @fixed = 0; - using (var db = new BotDb()) + await using var db = new BotDb(); + foreach (var warning in db.Warning) { - foreach (var warning in db.Warning) + var newReason = await FixChannelMentionAsync(ctx, warning.Reason).ConfigureAwait(false); + if (newReason != warning.Reason && newReason != null) { - var newReason = await FixChannelMentionAsync(ctx, warning.Reason).ConfigureAwait(false); - if (newReason != warning.Reason) - { - warning.Reason = newReason; - @fixed++; - } + warning.Reason = newReason; + @fixed++; } - await db.SaveChangesAsync().ConfigureAwait(false); } + await db.SaveChangesAsync().ConfigureAwait(false); await ctx.RespondAsync($"Fixed {@fixed} records").ConfigureAwait(false); } catch (Exception e) @@ -114,7 +110,7 @@ namespace CompatBot.Commands public async Task TitleMarks(CommandContext ctx) { var changed = 0; - using var db = new ThumbnailDb(); + await using var db = new ThumbnailDb(); foreach (var thumb in db.Thumbnail) { if (string.IsNullOrEmpty(thumb.Name)) @@ -127,12 +123,12 @@ namespace CompatBot.Commands newTitle = newTitle[..^17]; if (newTitle.EndsWith("downloadable game", StringComparison.OrdinalIgnoreCase)) newTitle = newTitle[..^18]; - newTitle.TrimEnd(); - if (newTitle != thumb.Name) - { - changed++; - thumb.Name = newTitle; - } + newTitle = newTitle.TrimEnd(); + if (newTitle == thumb.Name) + continue; + + changed++; + thumb.Name = newTitle; } await db.SaveChangesAsync(); await ctx.RespondAsync($"Fixed {changed} title{(changed == 1 ? "" : "s")}").ConfigureAwait(false); @@ -143,12 +139,16 @@ namespace CompatBot.Commands public async Task MetacriticLinks(CommandContext ctx, [Description("Remove links for trial and demo versions only")] bool demosOnly = true) { var changed = 0; - using var db = new ThumbnailDb(); + await using var db = new ThumbnailDb(); foreach (var thumb in db.Thumbnail.Where(t => t.MetacriticId != null)) { - if (!demosOnly || Regex.IsMatch(thumb.Name, @"\b(demo|trial)\b", RegexOptions.IgnoreCase | RegexOptions.Singleline)) - thumb.MetacriticId = null; - + if (demosOnly + && thumb.Name != null + && !Regex.IsMatch(thumb.Name, @"\b(demo|trial)\b", RegexOptions.IgnoreCase | RegexOptions.Singleline)) + continue; + + thumb.MetacriticId = null; + changed++; } await db.SaveChangesAsync(); await ctx.RespondAsync($"Fixed {changed} title{(changed == 1 ? "" : "s")}").ConfigureAwait(false); diff --git a/CompatBot/Commands/Sudo.cs b/CompatBot/Commands/Sudo.cs index a79e5211..28daa60d 100644 --- a/CompatBot/Commands/Sudo.cs +++ b/CompatBot/Commands/Sudo.cs @@ -46,9 +46,7 @@ namespace CompatBot.Commands [Command("say"), Priority(10)] [Description("Make bot say things, optionally in a specific channel")] public Task Say(CommandContext ctx, [RemainingText, Description("Message text to send")] string message) - { - return Say(ctx, ctx.Channel, message); - } + => Say(ctx, ctx.Channel, message); [Command("react")] [Description("Add reactions to the specified message")] @@ -61,7 +59,7 @@ namespace CompatBot.Commands try { var message = await ctx.GetMessageAsync(messageLink).ConfigureAwait(false); - if (message == null) + if (message is null) { await ctx.ReactWithAsync(Config.Reactions.Failure, "Couldn't find the message").ConfigureAwait(false); return; @@ -83,7 +81,7 @@ namespace CompatBot.Commands var endIdx = emojis.IndexOf('>', i); if (endIdx < i) endIdx = emojis.Length; - emoji = emojis.Substring(i, endIdx - i); + emoji = emojis[i..endIdx]; i = endIdx - 1; var emojiId = ulong.Parse(emoji[(emoji.LastIndexOf(':') + 1)..]); de = DiscordEmoji.FromGuildEmote(ctx.Client, emojiId); @@ -94,9 +92,7 @@ namespace CompatBot.Commands await message.ReactWithAsync(de).ConfigureAwait(false); } } - catch - { - } + catch { } } } catch (Exception e) @@ -113,20 +109,18 @@ namespace CompatBot.Commands { var logPath = Config.CurrentLogPath; var attachmentSizeLimit = Config.AttachmentSizeLimit; - using var log = File.Open(logPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); - using var result = Config.MemoryStreamManager.GetStream(); - using (var gzip = new GZipStream(result, CompressionLevel.Optimal, true)) - { - await log.CopyToAsync(gzip, Config.Cts.Token).ConfigureAwait(false); - await gzip.FlushAsync().ConfigureAwait(false); - } + await using var log = File.Open(logPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); + await using var result = Config.MemoryStreamManager.GetStream(); + await using var gzip = new GZipStream(result, CompressionLevel.Optimal, true); + await log.CopyToAsync(gzip, Config.Cts.Token).ConfigureAwait(false); + await gzip.FlushAsync().ConfigureAwait(false); if (result.Length <= attachmentSizeLimit) { result.Seek(0, SeekOrigin.Begin); await ctx.RespondWithFileAsync(Path.GetFileName(logPath) + ".gz", result).ConfigureAwait(false); } else - await ctx.ReactWithAsync(Config.Reactions.Failure, "Compressed log size is too large, ask Nicba for help :(", true).ConfigureAwait(false); + await ctx.ReactWithAsync(Config.Reactions.Failure, "Compressed log size is too large, ask 13xforever for help :(", true).ConfigureAwait(false); } catch (Exception e) { @@ -142,28 +136,28 @@ namespace CompatBot.Commands try { string dbPath; - using (var db = new ThumbnailDb()) - using (var connection = db.Database.GetDbConnection()) + await using (var db = new ThumbnailDb()) + await using (var connection = db.Database.GetDbConnection()) dbPath = connection.DataSource; var attachmentSizeLimit = Config.AttachmentSizeLimit; - var dbDir = Path.GetDirectoryName(dbPath); + var dbDir = Path.GetDirectoryName(dbPath) ?? "."; var dbName = Path.GetFileNameWithoutExtension(dbPath); - using var result = Config.MemoryStreamManager.GetStream(); - using (var zip = new ZipArchive(result, ZipArchiveMode.Create, true)) - foreach (var fname in Directory.EnumerateFiles(dbDir, $"{dbName}.*", new EnumerationOptions {IgnoreInaccessible = true, RecurseSubdirectories = false,})) - { - using var dbData = File.Open(fname, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); - using var entryStream = zip.CreateEntry(Path.GetFileName(fname), CompressionLevel.Optimal).Open(); - await dbData.CopyToAsync(entryStream, Config.Cts.Token).ConfigureAwait(false); - await entryStream.FlushAsync().ConfigureAwait(false); - } + await using var result = Config.MemoryStreamManager.GetStream(); + using var zip = new ZipArchive(result, ZipArchiveMode.Create, true); + foreach (var fname in Directory.EnumerateFiles(dbDir, $"{dbName}.*", new EnumerationOptions {IgnoreInaccessible = true, RecurseSubdirectories = false,})) + { + await using var dbData = File.Open(fname, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); + await using var entryStream = zip.CreateEntry(Path.GetFileName(fname), CompressionLevel.Optimal).Open(); + await dbData.CopyToAsync(entryStream, Config.Cts.Token).ConfigureAwait(false); + await entryStream.FlushAsync().ConfigureAwait(false); + } if (result.Length <= attachmentSizeLimit) { result.Seek(0, SeekOrigin.Begin); await ctx.RespondWithFileAsync(Path.GetFileName(dbName) + ".zip", result).ConfigureAwait(false); } else - await ctx.ReactWithAsync(Config.Reactions.Failure, "Compressed Thumbs.db size is too large, ask Nicba for help :(", true).ConfigureAwait(false); + await ctx.ReactWithAsync(Config.Reactions.Failure, "Compressed Thumbs.db size is too large, ask 13xforever for help :(", true).ConfigureAwait(false); } catch (Exception e) { diff --git a/CompatBot/Commands/Syscall.cs b/CompatBot/Commands/Syscall.cs index 0f2a10dc..15656664 100644 --- a/CompatBot/Commands/Syscall.cs +++ b/CompatBot/Commands/Syscall.cs @@ -40,12 +40,13 @@ namespace CompatBot.Commands return; } - using var db = new ThumbnailDb(); + await using var db = new ThumbnailDb(); if (db.SyscallInfo.Any(sci => sci.Function == search)) { var productInfoList = db.SyscallToProductMap.AsNoTracking() .Where(m => m.SyscallInfo.Function == search) - .Select(m => new {m.Product.ProductCode, Name = m.Product.Name.StripMarks() ?? "???"}) + .AsEnumerable() + .Select(m => new {m.Product.ProductCode, Name = m.Product.Name?.StripMarks() ?? "???"}) .Distinct() .ToList(); var groupedList = productInfoList @@ -55,7 +56,6 @@ namespace CompatBot.Commands if (groupedList.Any()) { var bigList = groupedList.Count >= Config.MaxSyscallResultLines; - var result = new StringBuilder(); var fullList = bigList ? new StringBuilder() : null; result.AppendLine($"List of games using `{search}`:```"); @@ -66,14 +66,14 @@ namespace CompatBot.Commands if (c < Config.MaxSyscallResultLines) result.AppendLine($"{gi.Key.Trim(60)} [{productIds}]"); if (bigList) - fullList.AppendLine($"{gi.Key} [{productIds}]"); + fullList!.AppendLine($"{gi.Key} [{productIds}]"); c++; } await ctx.SendAutosplitMessageAsync(result.Append("```")).ConfigureAwait(false); if (bigList) { - using var memoryStream = Config.MemoryStreamManager.GetStream(); - using var streamWriter = new StreamWriter(memoryStream, Encoding.UTF8); + await using var memoryStream = Config.MemoryStreamManager.GetStream(); + await using var streamWriter = new StreamWriter(memoryStream, Encoding.UTF8); await streamWriter.WriteAsync(fullList).ConfigureAwait(false); await streamWriter.FlushAsync().ConfigureAwait(false); memoryStream.Seek(0, SeekOrigin.Begin); @@ -115,7 +115,7 @@ namespace CompatBot.Commands [Description("Provides an option to rename function call")] public async Task Rename(CommandContext ctx, [Description("Old function name")] string oldFunctionName, [Description("New function name")] string newFunctionName) { - using var db = new ThumbnailDb(); + await using var db = new ThumbnailDb(); var oldMatches = await db.SyscallInfo.Where(sci => sci.Function == oldFunctionName).ToListAsync().ConfigureAwait(false); if (oldMatches.Count == 0) { @@ -144,10 +144,10 @@ namespace CompatBot.Commands await ctx.RespondAsync($"Function `{oldFunctionName}` was successfully renamed to `{newFunctionName}`").ConfigureAwait(false); } - private async Task ReturnSyscallsByGameAsync(CommandContext ctx, string productId) + private static async Task ReturnSyscallsByGameAsync(CommandContext ctx, string productId) { productId = productId.ToUpperInvariant(); - using var db = new ThumbnailDb(); + await using var db = new ThumbnailDb(); var title = db.Thumbnail.FirstOrDefault(t => t.ProductCode == productId)?.Name; title = string.IsNullOrEmpty(title) ? productId : $"[{productId}] {title.Trim(40)}"; var sysInfoList = db.SyscallToProductMap.AsNoTracking() @@ -162,8 +162,8 @@ namespace CompatBot.Commands var result = new StringBuilder(); foreach (var sci in sysInfoList) result.AppendLine(sci.Function); - using var memoryStream = Config.MemoryStreamManager.GetStream(); - using var streamWriter = new StreamWriter(memoryStream, Encoding.UTF8); + await using var memoryStream = Config.MemoryStreamManager.GetStream(); + await using var streamWriter = new StreamWriter(memoryStream, Encoding.UTF8); await streamWriter.WriteAsync(result).ConfigureAwait(false); await streamWriter.FlushAsync().ConfigureAwait(false); memoryStream.Seek(0, SeekOrigin.Begin); diff --git a/CompatBot/Database/Providers/AmdDriverVersionProvider.cs b/CompatBot/Database/Providers/AmdDriverVersionProvider.cs index fef853a0..cb5dd136 100644 --- a/CompatBot/Database/Providers/AmdDriverVersionProvider.cs +++ b/CompatBot/Database/Providers/AmdDriverVersionProvider.cs @@ -20,20 +20,31 @@ namespace CompatBot.Database.Providers if (await SyncObj.WaitAsync(0).ConfigureAwait(false)) try { - using (var httpClient = HttpClientFactory.Create(new CompressionMessageHandler())) + using var httpClient = HttpClientFactory.Create(new CompressionMessageHandler()); + await using var response = await httpClient.GetStreamAsync("https://raw.githubusercontent.com/GPUOpen-Drivers/amd-vulkan-versions/master/amdversions.xml").ConfigureAwait(false); + var xml = await XDocument.LoadAsync(response, LoadOptions.None, Config.Cts.Token).ConfigureAwait(false); + if (xml.Root is null) { - using var response = await httpClient.GetStreamAsync("https://raw.githubusercontent.com/GPUOpen-Drivers/amd-vulkan-versions/master/amdversions.xml").ConfigureAwait(false); - var xml = await XDocument.LoadAsync(response, LoadOptions.None, Config.Cts.Token).ConfigureAwait(false); - foreach (var driver in xml.Root.Elements("driver")) - { - var winVer = (string)driver.Element("windows-version"); - var vkVer = (string)driver.Element("vulkan-version"); - var driverVer = (string)driver.Attribute("version"); - if (!VulkanToDriver.TryGetValue(vkVer, out var verList)) - VulkanToDriver[vkVer] = (verList = new List()); - verList.Insert(0, driverVer); + Config.Log.Warn("Failed to update AMD version mapping"); + return; + } + + foreach (var driver in xml.Root.Elements("driver")) + { + var winVer = (string?)driver.Element("windows-version"); + var vkVer = (string?)driver.Element("vulkan-version"); + var driverVer = (string?)driver.Attribute("version"); + if (vkVer is null) + continue; + + if (!VulkanToDriver.TryGetValue(vkVer, out var verList)) + VulkanToDriver[vkVer] = verList = new(); + if (string.IsNullOrEmpty(driverVer)) + continue; + + verList.Insert(0, driverVer); + if (!string.IsNullOrEmpty(winVer)) OpenglToDriver[winVer] = driverVer; - } } foreach (var key in VulkanToDriver.Keys.ToList()) VulkanToDriver[key] = VulkanToDriver[key].Distinct().ToList(); @@ -50,50 +61,50 @@ namespace CompatBot.Database.Providers public static async Task GetFromOpenglAsync(string openglVersion, bool autoRefresh = true) { - if (OpenglToDriver.TryGetValue(openglVersion, out var result) && result != null) + if (OpenglToDriver.TryGetValue(openglVersion, out var result)) return result; - if (Version.TryParse(openglVersion, out var glVersion)) + if (!Version.TryParse(openglVersion, out var glVersion)) + return openglVersion; + + var glVersions = new List<(Version glVer, string driverVer)>(OpenglToDriver.Count); + foreach (var key in OpenglToDriver.Keys) { - var glVersions = new List<(Version v, string vv)>(OpenglToDriver.Count); - foreach (var key in OpenglToDriver.Keys) - { - if (Version.TryParse(key, out var ver)) - glVersions.Add((ver, OpenglToDriver[key])); - } - if (glVersions.Count == 0) - return openglVersion; - - glVersions.Sort((l, r) => l.v < r.v ? -1 : l.v > r.v ? 1 : 0); - if (glVersion < glVersions[0].v) - return $"older than {glVersions[0].vv} ({openglVersion})"; - - var newest = glVersions.Last(); - if (glVersion > newest.v) - { - if (autoRefresh) - { - await RefreshAsync().ConfigureAwait(false); - return await GetFromOpenglAsync(openglVersion, false).ConfigureAwait(false); - } - - return $"newer than {newest.vv} ({openglVersion})"; - } - - var approximate = glVersions.FirstOrDefault(v => v.v.Minor == glVersion.Minor && v.v.Build == glVersion.Build); - if (!string.IsNullOrEmpty(approximate.vv)) - return $"{approximate.vv} rev {glVersion.Revision}"; - - if (string.IsNullOrEmpty(approximate.vv)) - for (var i = 0; i < glVersions.Count - 1; i++) - if (glVersion > glVersions[i].v && glVersion < glVersions[i + 1].v) - { - approximate = glVersions[i]; - break; - } - if (!string.IsNullOrEmpty(approximate.vv)) - return $"probably {approximate.vv}"; + if (Version.TryParse(key, out var ver)) + glVersions.Add((ver, OpenglToDriver[key])); } + if (glVersions.Count == 0) + return openglVersion; + + glVersions.Sort((l, r) => l.glVer < r.glVer ? -1 : l.glVer > r.glVer ? 1 : 0); + if (glVersion < glVersions[0].glVer) + return $"older than {glVersions[0].driverVer} ({openglVersion})"; + + var newest = glVersions.Last(); + if (glVersion > newest.glVer) + { + if (autoRefresh) + { + await RefreshAsync().ConfigureAwait(false); + return await GetFromOpenglAsync(openglVersion, false).ConfigureAwait(false); + } + + return $"newer than {newest.driverVer} ({openglVersion})"; + } + + var approximate = glVersions.FirstOrDefault(v => v.glVer.Minor == glVersion.Minor && v.glVer.Build == glVersion.Build); + if (!string.IsNullOrEmpty(approximate.driverVer)) + return $"{approximate.driverVer} rev {glVersion.Revision}"; + + if (string.IsNullOrEmpty(approximate.driverVer)) + for (var i = 0; i < glVersions.Count - 1; i++) + if (glVersion > glVersions[i].glVer && glVersion < glVersions[i + 1].glVer) + { + approximate = glVersions[i]; + break; + } + if (!string.IsNullOrEmpty(approximate.driverVer)) + return $"probably {approximate.driverVer}"; return openglVersion; } @@ -112,7 +123,7 @@ namespace CompatBot.Database.Providers if (Version.TryParse(vulkanVersion, out var vkVer)) { - var vkVersions = new List<(Version v, string vv)>(VulkanToDriver.Count); + var vkVersions = new List<(Version vkVer, string driverVer)>(VulkanToDriver.Count); foreach (var key in VulkanToDriver.Keys) { if (Version.TryParse(key, out var ver)) @@ -121,39 +132,35 @@ namespace CompatBot.Database.Providers if (vkVersions.Count == 0) return vulkanVersion; - vkVersions.Sort((l, r) => l.v < r.v ? -1 : l.v > r.v ? 1 : 0); - if (vkVer < vkVersions[0].v) - return $"older than {vkVersions[0].vv} ({vulkanVersion})"; + vkVersions.Sort((l, r) => l.vkVer < r.vkVer ? -1 : l.vkVer > r.vkVer ? 1 : 0); + if (vkVer < vkVersions[0].vkVer) + return $"older than {vkVersions[0].driverVer} ({vulkanVersion})"; - var newest = vkVersions.Last(); - if (vkVer > newest.v) + var (version, driverVer) = vkVersions.Last(); + if (vkVer > version) { - if (autoRefresh) - { - await RefreshAsync().ConfigureAwait(false); - return await GetFromVulkanAsync(vulkanVersion, false).ConfigureAwait(false); - } - - return $"newer than {newest.vv} ({vulkanVersion})"; + if (!autoRefresh) + return $"newer than {driverVer} ({vulkanVersion})"; + + await RefreshAsync().ConfigureAwait(false); + return await GetFromVulkanAsync(vulkanVersion, false).ConfigureAwait(false); } - else + + for (var i = 1; i < vkVersions.Count; i++) { - for (var i = 1; i < vkVersions.Count; i++) - { - if (vkVer < vkVersions[i].v) - { - var lowerVer = vkVersions[i - 1].v; - var mapKey = VulkanToDriver.Keys.FirstOrDefault(k => Version.Parse(k) == lowerVer); - if (mapKey != null) - { - if (VulkanToDriver.TryGetValue(mapKey, out var driverList)) - { - var oldestLowerVersion = driverList.Select(Version.Parse).OrderByDescending(v => v).First(); - return $"unknown version between {oldestLowerVersion} and {vkVersions[i].vv} ({vulkanVersion})"; - } - } - } - } + if (vkVer >= vkVersions[i].vkVer) + continue; + + var lowerVer = vkVersions[i - 1].vkVer; + var mapKey = VulkanToDriver.Keys.FirstOrDefault(k => Version.Parse(k) == lowerVer); + if (mapKey is null) + continue; + + if (!VulkanToDriver.TryGetValue(mapKey, out var driverList)) + continue; + + var oldestLowerVersion = driverList.Select(Version.Parse).OrderByDescending(v => v).First(); + return $"unknown version between {oldestLowerVersion} and {vkVersions[i].driverVer} ({vulkanVersion})"; } } diff --git a/CompatBot/Database/Providers/ContentFilter.cs b/CompatBot/Database/Providers/ContentFilter.cs index 9866b768..4bd8eb4c 100644 --- a/CompatBot/Database/Providers/ContentFilter.cs +++ b/CompatBot/Database/Providers/ContentFilter.cs @@ -18,25 +18,22 @@ namespace CompatBot.Database.Providers { internal static class ContentFilter { - private static Dictionary> filters = new Dictionary>(); + private static Dictionary?> filters = new Dictionary?>(); private static readonly MemoryCache ResponseAntispamCache = new MemoryCache(new MemoryCacheOptions{ ExpirationScanFrequency = TimeSpan.FromMinutes(5)}); private static readonly MemoryCache ReportAntispamCache = new MemoryCache(new MemoryCacheOptions{ ExpirationScanFrequency = TimeSpan.FromMinutes(5)}); private static readonly TimeSpan CacheTime = TimeSpan.FromMinutes(15); - static ContentFilter() - { - RebuildMatcher(); - } + static ContentFilter() => RebuildMatcher(); - public static Task FindTriggerAsync(FilterContext ctx, string str) + public static Task FindTriggerAsync(FilterContext ctx, string str) { if (string.IsNullOrEmpty(str)) - return Task.FromResult((Piracystring)null); + return Task.FromResult((Piracystring?)null); if (!filters.TryGetValue(ctx, out var matcher)) - return Task.FromResult((Piracystring)null); + return Task.FromResult((Piracystring?)null); - Piracystring result = null; + Piracystring? result = null; matcher?.ParseText(str, h => { if (string.IsNullOrEmpty(h.Value.ValidatingRegex) || Regex.IsMatch(str, h.Value.ValidatingRegex, RegexOptions.IgnoreCase | RegexOptions.Multiline)) @@ -66,7 +63,7 @@ namespace CompatBot.Database.Providers public static void RebuildMatcher() { - var newFilters = new Dictionary>(); + var newFilters = new Dictionary?>(); using (var db = new BotDb()) foreach (FilterContext ctx in Enum.GetValues(typeof(FilterContext))) { @@ -134,11 +131,8 @@ namespace CompatBot.Database.Providers return (trigger.Actions & (~suppressActions) & (FilterAction.IssueWarning | FilterAction.RemoveContent)) == 0; } - public static async Task PerformFilterActions(DiscordClient client, DiscordMessage message, Piracystring trigger, FilterAction ignoreFlags = 0, string triggerContext = null, string infraction = null, string warningReason = null) + public static async Task PerformFilterActions(DiscordClient client, DiscordMessage message, Piracystring trigger, FilterAction ignoreFlags = 0, string? triggerContext = null, string? infraction = null, string? warningReason = null) { - if (trigger == null) - return; - var severity = ReportSeverity.Low; var completedActions = new List(); if (trigger.Actions.HasFlag(FilterAction.RemoveContent) && !ignoreFlags.HasFlag(FilterAction.RemoveContent)) @@ -157,7 +151,8 @@ namespace CompatBot.Database.Providers try { var author = client.GetMember(message.Author); - Config.Log.Debug($"Removed message from {author.GetMentionWithNickname()} in #{message.Channel.Name}: {message.Content}"); + var username = author?.GetMentionWithNickname() ?? message.Author.GetUsernameWithNickname(client); + Config.Log.Debug($"Removed message from {username} in #{message.Channel.Name}: {message.Content}"); } catch (Exception e) { @@ -203,7 +198,9 @@ namespace CompatBot.Database.Providers } } - if (trigger.Actions.HasFlag(FilterAction.ShowExplain) && !ignoreFlags.HasFlag(FilterAction.ShowExplain)) + if (trigger.Actions.HasFlag(FilterAction.ShowExplain) + && !ignoreFlags.HasFlag(FilterAction.ShowExplain) + && !string.IsNullOrEmpty(trigger.ExplainTerm)) { var result = await Explain.LookupTerm(trigger.ExplainTerm).ConfigureAwait(false); await Explain.SendExplanation(result, trigger.ExplainTerm, message).ConfigureAwait(false); diff --git a/CompatBot/Database/Providers/StatsStorage.cs b/CompatBot/Database/Providers/StatsStorage.cs index 36617cc3..241d6524 100644 --- a/CompatBot/Database/Providers/StatsStorage.cs +++ b/CompatBot/Database/Providers/StatsStorage.cs @@ -31,25 +31,24 @@ namespace CompatBot.Database.Providers try { Config.Log.Debug("Got stats saving lock"); - using var db = new BotDb(); + await using var db = new BotDb(); db.Stats.RemoveRange(db.Stats); await db.SaveChangesAsync().ConfigureAwait(false); - foreach (var cache in AllCaches) + foreach (var (category, cache) in AllCaches) { - var category = cache.name; - var entries = cache.cache.GetCacheEntries(); + var entries = cache.GetCacheEntries(); var savedKeys = new HashSet(); - foreach (var entry in entries) - if (savedKeys.Add(entry.Key)) + foreach (var (key, value) in entries) + if (savedKeys.Add(key)) await db.Stats.AddAsync(new Stats { Category = category, - Key = entry.Key, - Value = ((int?) entry.Value.Value) ?? 0, - ExpirationTimestamp = entry.Value.AbsoluteExpiration?.ToUniversalTime().Ticks ?? 0 + Key = key, + Value = (int?)value?.Value ?? 0, + ExpirationTimestamp = value?.AbsoluteExpiration?.ToUniversalTime().Ticks ?? 0 }).ConfigureAwait(false); else - Config.Log.Warn($"Somehow there's another '{entry.Key}' in the {category} cache"); + Config.Log.Warn($"Somehow there's another '{key}' in the {category} cache"); } await db.SaveChangesAsync().ConfigureAwait(false); } @@ -73,16 +72,15 @@ namespace CompatBot.Database.Providers public static async Task RestoreAsync() { var now = DateTime.UtcNow; - using var db = new BotDb(); - foreach (var cache in AllCaches) + await using var db = new BotDb(); + foreach (var (category, cache) in AllCaches) { - var category = cache.name; var entries = await db.Stats.Where(e => e.Category == category).ToListAsync().ConfigureAwait(false); foreach (var entry in entries) { var time = entry.ExpirationTimestamp.AsUtc(); if (time > now) - cache.cache.Set(entry.Key, entry.Value, time); + cache.Set(entry.Key, entry.Value, time); } } } diff --git a/CompatBot/EventHandlers/DiscordInviteFilter.cs b/CompatBot/EventHandlers/DiscordInviteFilter.cs index 4f74c002..8e6cc6bd 100644 --- a/CompatBot/EventHandlers/DiscordInviteFilter.cs +++ b/CompatBot/EventHandlers/DiscordInviteFilter.cs @@ -22,9 +22,6 @@ namespace CompatBot.EventHandlers { private const RegexOptions DefaultOptions = RegexOptions.Compiled | RegexOptions.ExplicitCapture | RegexOptions.IgnoreCase | RegexOptions.Multiline; private static readonly Regex InviteLink = new Regex(@"(https?://)?discord(((app\.com/invite|\.gg)/(?[a-z0-9\-]+))|(\.me/(?.*?))(\s|>|$))", DefaultOptions); - private static readonly Regex DiscordInviteLink = new Regex(@"(https?://)?discord(app\.com/invite|\.gg)/(?[a-z0-9\-]+)", DefaultOptions); - private static readonly Regex DiscordMeLink = new Regex(@"(https?://)?discord\.me/(?.*?)(\s|$)", DefaultOptions); - private static readonly HttpClient HttpClient = HttpClientFactory.Create(new CompressionMessageHandler()); private static readonly MemoryCache InviteCodeCache = new MemoryCache(new MemoryCacheOptions{ExpirationScanFrequency = TimeSpan.FromHours(1)}); private static readonly TimeSpan CacheDuration = TimeSpan.FromHours(24); @@ -178,7 +175,7 @@ namespace CompatBot.EventHandlers return true; } - public static async Task<(bool hasInvalidInvite, bool attemptToWorkaround, List invites)> GetInvitesAsync(this DiscordClient client, string message, DiscordUser author = null, bool tryMessageAsACode = false) + public static async Task<(bool hasInvalidInvite, bool attemptToWorkaround, List invites)> GetInvitesAsync(this DiscordClient client, string message, DiscordUser? author = null, bool tryMessageAsACode = false) { if (string.IsNullOrEmpty(message)) return (false, false, new List(0)); diff --git a/CompatBot/EventHandlers/LogParsing/ArchiveHandlers/GzipHandler.cs b/CompatBot/EventHandlers/LogParsing/ArchiveHandlers/GzipHandler.cs index 6e88ef94..1126fd63 100644 --- a/CompatBot/EventHandlers/LogParsing/ArchiveHandlers/GzipHandler.cs +++ b/CompatBot/EventHandlers/LogParsing/ArchiveHandlers/GzipHandler.cs @@ -15,7 +15,7 @@ namespace CompatBot.EventHandlers.LogParsing.ArchiveHandlers public long LogSize { get; private set; } public long SourcePosition { get; private set; } - public (bool result, string reason) CanHandle(string fileName, int fileSize, ReadOnlySpan header) + public (bool result, string? reason) CanHandle(string fileName, int fileSize, ReadOnlySpan header) { if (header.Length >= Header.Length) { @@ -31,8 +31,8 @@ namespace CompatBot.EventHandlers.LogParsing.ArchiveHandlers public async Task FillPipeAsync(Stream sourceStream, PipeWriter writer, CancellationToken cancellationToken) { - using var statsStream = new BufferCopyStream(sourceStream); - using var gzipStream = new GZipStream(statsStream, CompressionMode.Decompress); + await using var statsStream = new BufferCopyStream(sourceStream); + await using var gzipStream = new GZipStream(statsStream, CompressionMode.Decompress); try { int read; @@ -44,7 +44,6 @@ namespace CompatBot.EventHandlers.LogParsing.ArchiveHandlers writer.Advance(read); SourcePosition = statsStream.Position; flushed = await writer.FlushAsync(cancellationToken).ConfigureAwait(false); - SourcePosition = statsStream.Position; } while (read > 0 && !(flushed.IsCompleted || flushed.IsCanceled || cancellationToken.IsCancellationRequested)); var buf = statsStream.GetBufferedBytes(); diff --git a/CompatBot/EventHandlers/LogParsing/ArchiveHandlers/IArchiveHandler.cs b/CompatBot/EventHandlers/LogParsing/ArchiveHandlers/IArchiveHandler.cs index 3700db9a..feacbc2e 100644 --- a/CompatBot/EventHandlers/LogParsing/ArchiveHandlers/IArchiveHandler.cs +++ b/CompatBot/EventHandlers/LogParsing/ArchiveHandlers/IArchiveHandler.cs @@ -8,7 +8,7 @@ namespace CompatBot.EventHandlers.LogParsing.ArchiveHandlers { public interface IArchiveHandler { - (bool result, string reason) CanHandle(string fileName, int fileSize, ReadOnlySpan header); + (bool result, string? reason) CanHandle(string fileName, int fileSize, ReadOnlySpan header); Task FillPipeAsync(Stream sourceStream, PipeWriter writer, CancellationToken cancellationToken); long LogSize { get; } long SourcePosition { get; } diff --git a/CompatBot/EventHandlers/LogParsing/ArchiveHandlers/PlainText.cs b/CompatBot/EventHandlers/LogParsing/ArchiveHandlers/PlainText.cs index 225d69d9..c9586764 100644 --- a/CompatBot/EventHandlers/LogParsing/ArchiveHandlers/PlainText.cs +++ b/CompatBot/EventHandlers/LogParsing/ArchiveHandlers/PlainText.cs @@ -12,7 +12,7 @@ namespace CompatBot.EventHandlers.LogParsing.ArchiveHandlers public long LogSize { get; private set; } public long SourcePosition { get; private set; } - public (bool result, string reason) CanHandle(string fileName, int fileSize, ReadOnlySpan header) + public (bool result, string? reason) CanHandle(string fileName, int fileSize, ReadOnlySpan header) { LogSize = fileSize; if (fileName.Contains("tty.log", StringComparison.InvariantCultureIgnoreCase)) @@ -35,6 +35,7 @@ namespace CompatBot.EventHandlers.LogParsing.ArchiveHandlers var memory = writer.GetMemory(Config.MinimumBufferSize); read = await sourceStream.ReadAsync(memory, cancellationToken); writer.Advance(read); + SourcePosition = sourceStream.Position; flushed = await writer.FlushAsync(cancellationToken).ConfigureAwait(false); } while (read > 0 && !(flushed.IsCompleted || flushed.IsCanceled || cancellationToken.IsCancellationRequested)); } diff --git a/CompatBot/EventHandlers/LogParsing/ArchiveHandlers/RarHandler.cs b/CompatBot/EventHandlers/LogParsing/ArchiveHandlers/RarHandler.cs index 97e7da06..4f36956f 100644 --- a/CompatBot/EventHandlers/LogParsing/ArchiveHandlers/RarHandler.cs +++ b/CompatBot/EventHandlers/LogParsing/ArchiveHandlers/RarHandler.cs @@ -5,7 +5,6 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using CompatBot.Utils; -using SharpCompress.Archives.Rar; using SharpCompress.Readers.Rar; namespace CompatBot.EventHandlers.LogParsing.ArchiveHandlers @@ -17,7 +16,7 @@ namespace CompatBot.EventHandlers.LogParsing.ArchiveHandlers public long LogSize { get; private set; } public long SourcePosition { get; private set; } - public (bool result, string reason) CanHandle(string fileName, int fileSize, ReadOnlySpan header) + public (bool result, string? reason) CanHandle(string fileName, int fileSize, ReadOnlySpan header) { if (header.Length >= Header.Length && header.Slice(0, Header.Length).SequenceEqual(Header) || fileName.EndsWith(".rar", StringComparison.InvariantCultureIgnoreCase)) @@ -36,7 +35,7 @@ namespace CompatBot.EventHandlers.LogParsing.ArchiveHandlers { try { - using var statsStream = new BufferCopyStream(sourceStream); + await using var statsStream = new BufferCopyStream(sourceStream); using var rarReader = RarReader.Open(statsStream); while (rarReader.MoveToNextEntry()) { @@ -45,7 +44,7 @@ namespace CompatBot.EventHandlers.LogParsing.ArchiveHandlers && !rarReader.Entry.Key.Contains("tty.log", StringComparison.InvariantCultureIgnoreCase)) { LogSize = rarReader.Entry.Size; - using var rarStream = rarReader.OpenEntryStream(); + await using var rarStream = rarReader.OpenEntryStream(); int read; FlushResult flushed; do diff --git a/CompatBot/EventHandlers/LogParsing/ArchiveHandlers/SevenZipHandler.cs b/CompatBot/EventHandlers/LogParsing/ArchiveHandlers/SevenZipHandler.cs index f05d2f71..3fd666e9 100644 --- a/CompatBot/EventHandlers/LogParsing/ArchiveHandlers/SevenZipHandler.cs +++ b/CompatBot/EventHandlers/LogParsing/ArchiveHandlers/SevenZipHandler.cs @@ -15,7 +15,7 @@ namespace CompatBot.EventHandlers.LogParsing.ArchiveHandlers public long LogSize { get; private set; } public long SourcePosition { get; private set; } - public (bool result, string reason) CanHandle(string fileName, int fileSize, ReadOnlySpan header) + public (bool result, string? reason) CanHandle(string fileName, int fileSize, ReadOnlySpan header) { if (header.Length >= Header.Length && header.Slice(0, Header.Length).SequenceEqual(Header) || fileName.EndsWith(".7z", StringComparison.InvariantCultureIgnoreCase)) @@ -33,7 +33,7 @@ namespace CompatBot.EventHandlers.LogParsing.ArchiveHandlers { try { - using var fileStream = new FileStream(Path.GetTempFileName(), FileMode.Create, FileAccess.ReadWrite, FileShare.Read, 16384, FileOptions.Asynchronous | FileOptions.RandomAccess | FileOptions.DeleteOnClose); + await using var fileStream = new FileStream(Path.GetTempFileName(), FileMode.Create, FileAccess.ReadWrite, FileShare.Read, 16384, FileOptions.Asynchronous | FileOptions.RandomAccess | FileOptions.DeleteOnClose); await sourceStream.CopyToAsync(fileStream, 16384, cancellationToken).ConfigureAwait(false); fileStream.Seek(0, SeekOrigin.Begin); using var zipArchive = SevenZipArchive.Open(fileStream); @@ -44,7 +44,7 @@ namespace CompatBot.EventHandlers.LogParsing.ArchiveHandlers && !zipReader.Entry.Key.Contains("tty.log", StringComparison.InvariantCultureIgnoreCase)) { LogSize = zipReader.Entry.Size; - using var entryStream = zipReader.OpenEntryStream(); + await using var entryStream = zipReader.OpenEntryStream(); int read; FlushResult flushed; do diff --git a/CompatBot/EventHandlers/LogParsing/ArchiveHandlers/ZipHandler.cs b/CompatBot/EventHandlers/LogParsing/ArchiveHandlers/ZipHandler.cs index 6359724a..bf442b9d 100644 --- a/CompatBot/EventHandlers/LogParsing/ArchiveHandlers/ZipHandler.cs +++ b/CompatBot/EventHandlers/LogParsing/ArchiveHandlers/ZipHandler.cs @@ -16,7 +16,7 @@ namespace CompatBot.EventHandlers.LogParsing.ArchiveHandlers public long LogSize { get; private set; } public long SourcePosition { get; private set; } - public (bool result, string reason) CanHandle(string fileName, int fileSize, ReadOnlySpan header) + public (bool result, string? reason) CanHandle(string fileName, int fileSize, ReadOnlySpan header) { if (header.Length >= Header.Length && header.Slice(0, Header.Length).SequenceEqual(Header) @@ -36,7 +36,7 @@ namespace CompatBot.EventHandlers.LogParsing.ArchiveHandlers { try { - using var statsStream = new BufferCopyStream(sourceStream); + await using var statsStream = new BufferCopyStream(sourceStream); using var zipReader = ZipReader.Open(statsStream); while (zipReader.MoveToNextEntry()) { @@ -45,7 +45,7 @@ namespace CompatBot.EventHandlers.LogParsing.ArchiveHandlers && !zipReader.Entry.Key.Contains("tty.log", StringComparison.InvariantCultureIgnoreCase)) { LogSize = zipReader.Entry.Size; - using var rarStream = zipReader.OpenEntryStream(); + await using var rarStream = zipReader.OpenEntryStream(); int read; FlushResult flushed; do diff --git a/CompatBot/EventHandlers/LogParsing/LogParser.LogSections.cs b/CompatBot/EventHandlers/LogParsing/LogParser.LogSections.cs index 9884739f..5ea67da8 100644 --- a/CompatBot/EventHandlers/LogParsing/LogParser.LogSections.cs +++ b/CompatBot/EventHandlers/LogParsing/LogParser.LogSections.cs @@ -306,7 +306,7 @@ namespace CompatBot.EventHandlers.LogParsing { foreach (var key in keys) { - if (state.CompleteCollection?[key] is string value) + if (state.CompletedCollection?[key] is string value) state.WipCollection[key] = value; if (state.CompleteMultiValueCollection?[key] is UniqueList collection) state.WipMultiValueCollection[key] = collection; @@ -329,7 +329,7 @@ namespace CompatBot.EventHandlers.LogParsing private static void MarkAsComplete(LogParseState state) { - state.CompleteCollection = state.WipCollection; + state.CompletedCollection = state.WipCollection; state.CompleteMultiValueCollection = state.WipMultiValueCollection; Config.Log.Trace("----- complete section"); } diff --git a/CompatBot/EventHandlers/LogParsing/POCOs/LogParseState.cs b/CompatBot/EventHandlers/LogParsing/POCOs/LogParseState.cs index 225e79a6..b0cf024d 100644 --- a/CompatBot/EventHandlers/LogParsing/POCOs/LogParseState.cs +++ b/CompatBot/EventHandlers/LogParsing/POCOs/LogParseState.cs @@ -8,8 +8,8 @@ namespace CompatBot.EventHandlers.LogParsing.POCOs { public class LogParseState { - public NameValueCollection CompleteCollection = null; - public NameUniqueObjectCollection CompleteMultiValueCollection = null; + public NameValueCollection? CompletedCollection; + public NameUniqueObjectCollection? CompleteMultiValueCollection; public NameValueCollection WipCollection = new NameValueCollection(); public NameUniqueObjectCollection WipMultiValueCollection = new NameUniqueObjectCollection(); public readonly Dictionary ValueHitStats = new Dictionary(); @@ -17,8 +17,8 @@ namespace CompatBot.EventHandlers.LogParsing.POCOs public int Id = 0; public ErrorCode Error = ErrorCode.None; public readonly Dictionary FilterTriggers = new Dictionary(); - public Piracystring SelectedFilter; - public string SelectedFilterContext; + public Piracystring? SelectedFilter; + public string? SelectedFilterContext; public long ReadBytes; public long TotalBytes; public int LinesAfterConfig; diff --git a/CompatBot/EventHandlers/LogParsing/SourceHandlers/BaseSourceHandler.cs b/CompatBot/EventHandlers/LogParsing/SourceHandlers/BaseSourceHandler.cs index ba4b04e1..11fa44e9 100644 --- a/CompatBot/EventHandlers/LogParsing/SourceHandlers/BaseSourceHandler.cs +++ b/CompatBot/EventHandlers/LogParsing/SourceHandlers/BaseSourceHandler.cs @@ -13,6 +13,6 @@ namespace CompatBot.EventHandlers.LogParsing.SourceHandlers protected const int SnoopBufferSize = 4096; internal static readonly ArrayPool bufferPool = ArrayPool.Create(SnoopBufferSize, 64); - public abstract Task<(ISource source, string failReason)> FindHandlerAsync(DiscordMessage message, ICollection handlers); + public abstract Task<(ISource? source, string? failReason)> FindHandlerAsync(DiscordMessage message, ICollection handlers); } } diff --git a/CompatBot/EventHandlers/LogParsing/SourceHandlers/DiscordAttachmentHandler.cs b/CompatBot/EventHandlers/LogParsing/SourceHandlers/DiscordAttachmentHandler.cs index d067c910..259491a2 100644 --- a/CompatBot/EventHandlers/LogParsing/SourceHandlers/DiscordAttachmentHandler.cs +++ b/CompatBot/EventHandlers/LogParsing/SourceHandlers/DiscordAttachmentHandler.cs @@ -12,14 +12,14 @@ namespace CompatBot.EventHandlers.LogParsing.SourceHandlers { internal sealed class DiscordAttachmentHandler : BaseSourceHandler { - public override async Task<(ISource source, string failReason)> FindHandlerAsync(DiscordMessage message, ICollection handlers) + public override async Task<(ISource? source, string? failReason)> FindHandlerAsync(DiscordMessage message, ICollection handlers) { using var client = HttpClientFactory.Create(); foreach (var attachment in message.Attachments) { try { - using var stream = await client.GetStreamAsync(attachment.Url).ConfigureAwait(false); + await using var stream = await client.GetStreamAsync(attachment.Url).ConfigureAwait(false); var buf = bufferPool.Rent(SnoopBufferSize); try { @@ -68,7 +68,7 @@ namespace CompatBot.EventHandlers.LogParsing.SourceHandlers public async Task FillPipeAsync(PipeWriter writer, CancellationToken cancellationToken) { using var client = HttpClientFactory.Create(); - using var stream = await client.GetStreamAsync(attachment.Url).ConfigureAwait(false); + await using var stream = await client.GetStreamAsync(attachment.Url, cancellationToken).ConfigureAwait(false); await handler.FillPipeAsync(stream, writer, cancellationToken).ConfigureAwait(false); } } diff --git a/CompatBot/EventHandlers/LogParsing/SourceHandlers/DropboxHandler.cs b/CompatBot/EventHandlers/LogParsing/SourceHandlers/DropboxHandler.cs index 5b6ffc2d..b8a8b0ca 100644 --- a/CompatBot/EventHandlers/LogParsing/SourceHandlers/DropboxHandler.cs +++ b/CompatBot/EventHandlers/LogParsing/SourceHandlers/DropboxHandler.cs @@ -18,7 +18,7 @@ namespace CompatBot.EventHandlers.LogParsing.SourceHandlers //https://www.dropbox.com/s/62ls9lw5i52fuib/RPCS3.log.gz?dl=0 private static readonly Regex ExternalLink = new Regex(@"(?(https?://)?(www\.)?dropbox\.com/s/(?[^/\s]+)/(?[^/\?\s])(/dl=[01])?)", DefaultOptions); - public override async Task<(ISource source, string failReason)> FindHandlerAsync(DiscordMessage message, ICollection handlers) + public override async Task<(ISource? source, string? failReason)> FindHandlerAsync(DiscordMessage message, ICollection handlers) { if (string.IsNullOrEmpty(message.Content)) return (null, null); @@ -43,14 +43,14 @@ namespace CompatBot.EventHandlers.LogParsing.SourceHandlers using (var request = new HttpRequestMessage(HttpMethod.Head, uri)) { using var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, Config.Cts.Token); - if (response?.Content?.Headers?.ContentLength > 0) + if (response.Content.Headers.ContentLength > 0) filesize = (int)response.Content.Headers.ContentLength.Value; - if (response?.Content?.Headers?.ContentDisposition?.FileNameStar is string fname && !string.IsNullOrEmpty(fname)) + if (response.Content.Headers.ContentDisposition?.FileNameStar is string fname && !string.IsNullOrEmpty(fname)) filename = fname; - uri = response.RequestMessage.RequestUri; + uri = response.RequestMessage?.RequestUri; } - using var stream = await client.GetStreamAsync(uri).ConfigureAwait(false); + await using var stream = await client.GetStreamAsync(uri).ConfigureAwait(false); var buf = bufferPool.Rent(SnoopBufferSize); try { @@ -81,7 +81,7 @@ namespace CompatBot.EventHandlers.LogParsing.SourceHandlers private sealed class DropboxSource : ISource { - private readonly Uri uri; + private readonly Uri? uri; private readonly IArchiveHandler handler; public string SourceType => "Dropbox"; @@ -90,7 +90,7 @@ namespace CompatBot.EventHandlers.LogParsing.SourceHandlers public long SourceFilePosition => handler.SourcePosition; public long LogFileSize => handler.LogSize; - internal DropboxSource(Uri uri, IArchiveHandler handler, string fileName, int fileSize) + internal DropboxSource(Uri? uri, IArchiveHandler handler, string fileName, int fileSize) { this.uri = uri; this.handler = handler; @@ -101,7 +101,7 @@ namespace CompatBot.EventHandlers.LogParsing.SourceHandlers public async Task FillPipeAsync(PipeWriter writer, CancellationToken cancellationToken) { using var client = HttpClientFactory.Create(); - using var stream = await client.GetStreamAsync(uri).ConfigureAwait(false); + await using var stream = await client.GetStreamAsync(uri, cancellationToken).ConfigureAwait(false); await handler.FillPipeAsync(stream, writer, cancellationToken).ConfigureAwait(false); } } diff --git a/CompatBot/EventHandlers/LogParsing/SourceHandlers/GenericLinkHandler.cs b/CompatBot/EventHandlers/LogParsing/SourceHandlers/GenericLinkHandler.cs index b3d7f003..153f129c 100644 --- a/CompatBot/EventHandlers/LogParsing/SourceHandlers/GenericLinkHandler.cs +++ b/CompatBot/EventHandlers/LogParsing/SourceHandlers/GenericLinkHandler.cs @@ -16,7 +16,7 @@ namespace CompatBot.EventHandlers.LogParsing.SourceHandlers { private static readonly Regex ExternalLink = new Regex(@"(?(https?://)?(github\.com/RPCS3/rpcs3|cdn\.discordapp\.com/attachments)/.*/(?[^/\?\s]+\.(gz|zip|rar|7z|log)))", DefaultOptions); - public override async Task<(ISource source, string failReason)> FindHandlerAsync(DiscordMessage message, ICollection handlers) + public override async Task<(ISource? source, string? failReason)> FindHandlerAsync(DiscordMessage message, ICollection handlers) { if (string.IsNullOrEmpty(message.Content)) return (null, null); @@ -42,14 +42,14 @@ namespace CompatBot.EventHandlers.LogParsing.SourceHandlers using (var request = new HttpRequestMessage(HttpMethod.Head, uri)) { using var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, Config.Cts.Token); - if (response?.Content?.Headers?.ContentLength > 0) + if (response.Content.Headers.ContentLength > 0) filesize = (int)response.Content.Headers.ContentLength.Value; - if (response?.Content?.Headers?.ContentDisposition?.FileNameStar is string fname && !string.IsNullOrEmpty(fname)) + if (response.Content.Headers.ContentDisposition?.FileNameStar is string fname && !string.IsNullOrEmpty(fname)) filename = fname; - uri = response.RequestMessage.RequestUri; + uri = response.RequestMessage?.RequestUri; } - using var stream = await client.GetStreamAsync(uri).ConfigureAwait(false); + await using var stream = await client.GetStreamAsync(uri).ConfigureAwait(false); var buf = bufferPool.Rent(SnoopBufferSize); try { @@ -79,7 +79,7 @@ namespace CompatBot.EventHandlers.LogParsing.SourceHandlers private sealed class GenericSource : ISource { - private readonly Uri uri; + private readonly Uri? uri; private readonly IArchiveHandler handler; public string SourceType => "Generic link"; @@ -89,7 +89,7 @@ namespace CompatBot.EventHandlers.LogParsing.SourceHandlers public long SourceFilePosition => handler.SourcePosition; public long LogFileSize => handler.LogSize; - internal GenericSource(Uri uri, IArchiveHandler handler, string host, string fileName, int fileSize) + internal GenericSource(Uri? uri, IArchiveHandler handler, string host, string fileName, int fileSize) { this.uri = uri; this.handler = handler; @@ -101,7 +101,7 @@ namespace CompatBot.EventHandlers.LogParsing.SourceHandlers public async Task FillPipeAsync(PipeWriter writer, CancellationToken cancellationToken) { using var client = HttpClientFactory.Create(); - using var stream = await client.GetStreamAsync(uri).ConfigureAwait(false); + await using var stream = await client.GetStreamAsync(uri, cancellationToken).ConfigureAwait(false); await handler.FillPipeAsync(stream, writer, cancellationToken).ConfigureAwait(false); } } diff --git a/CompatBot/EventHandlers/LogParsing/SourceHandlers/GoogleDriveHandler.cs b/CompatBot/EventHandlers/LogParsing/SourceHandlers/GoogleDriveHandler.cs index 4299d4e3..cedafbf2 100644 --- a/CompatBot/EventHandlers/LogParsing/SourceHandlers/GoogleDriveHandler.cs +++ b/CompatBot/EventHandlers/LogParsing/SourceHandlers/GoogleDriveHandler.cs @@ -22,7 +22,7 @@ namespace CompatBot.EventHandlers.LogParsing.SourceHandlers private static readonly string[] Scopes = { DriveService.Scope.DriveReadonly }; private static readonly string ApplicationName = "RPCS3 Compatibility Bot 2.0"; - public override async Task<(ISource source, string failReason)> FindHandlerAsync(DiscordMessage message, ICollection handlers) + public override async Task<(ISource? source, string? failReason)> FindHandlerAsync(DiscordMessage message, ICollection handlers) { if (string.IsNullOrEmpty(message.Content)) return (null, null); @@ -45,15 +45,15 @@ namespace CompatBot.EventHandlers.LogParsing.SourceHandlers var fileInfoRequest = client.Files.Get(fid); fileInfoRequest.Fields = "name, size, kind"; var fileMeta = await fileInfoRequest.ExecuteAsync(Config.Cts.Token).ConfigureAwait(false); - if (fileMeta.Kind == "drive#file") + if (fileMeta.Kind == "drive#file" && fileMeta.Size > 0) { var buf = bufferPool.Rent(SnoopBufferSize); try { int read; - using (var stream = new MemoryStream(buf, true)) + await using (var stream = new MemoryStream(buf, true)) { - var limit = Math.Min(SnoopBufferSize, (int)fileMeta.Size) - 1; + var limit = Math.Min(SnoopBufferSize, fileMeta.Size.Value) - 1; var progress = await fileInfoRequest.DownloadRangeAsync(stream, new RangeHeaderValue(0, limit), Config.Cts.Token).ConfigureAwait(false); if (progress.Status != DownloadStatus.Completed) continue; @@ -103,9 +103,9 @@ namespace CompatBot.EventHandlers.LogParsing.SourceHandlers public long SourceFilePosition => handler.SourcePosition; public long LogFileSize => handler.LogSize; - private FilesResource.GetRequest fileInfoRequest; - private FileMeta fileMeta; - private IArchiveHandler handler; + private readonly FilesResource.GetRequest fileInfoRequest; + private readonly FileMeta fileMeta; + private readonly IArchiveHandler handler; public GoogleDriveSource(FilesResource.GetRequest fileInfoRequest, FileMeta fileMeta, IArchiveHandler handler) { @@ -119,9 +119,9 @@ namespace CompatBot.EventHandlers.LogParsing.SourceHandlers try { var pipe = new Pipe(); - using var pushStream = pipe.Writer.AsStream(); + await using var pushStream = pipe.Writer.AsStream(); var progressTask = fileInfoRequest.DownloadAsync(pushStream, cancellationToken); - using var pullStream = pipe.Reader.AsStream(); + await using var pullStream = pipe.Reader.AsStream(); var pipingTask = handler.FillPipeAsync(pullStream, writer, cancellationToken); var result = await progressTask.ConfigureAwait(false); if (result.Status != DownloadStatus.Completed) diff --git a/CompatBot/EventHandlers/LogParsing/SourceHandlers/ISourceHandler.cs b/CompatBot/EventHandlers/LogParsing/SourceHandlers/ISourceHandler.cs index b6a04a68..6c22df1e 100644 --- a/CompatBot/EventHandlers/LogParsing/SourceHandlers/ISourceHandler.cs +++ b/CompatBot/EventHandlers/LogParsing/SourceHandlers/ISourceHandler.cs @@ -9,7 +9,7 @@ namespace CompatBot.EventHandlers.LogParsing.SourceHandlers { public interface ISourceHandler { - Task<(ISource source, string failReason)> FindHandlerAsync(DiscordMessage message, ICollection handlers); + Task<(ISource? source, string? failReason)> FindHandlerAsync(DiscordMessage message, ICollection handlers); } public interface ISource diff --git a/CompatBot/EventHandlers/LogParsing/SourceHandlers/MegaHandler.cs b/CompatBot/EventHandlers/LogParsing/SourceHandlers/MegaHandler.cs index b00246bd..7ab96b0b 100644 --- a/CompatBot/EventHandlers/LogParsing/SourceHandlers/MegaHandler.cs +++ b/CompatBot/EventHandlers/LogParsing/SourceHandlers/MegaHandler.cs @@ -18,7 +18,7 @@ namespace CompatBot.EventHandlers.LogParsing.SourceHandlers private static readonly Regex ExternalLink = new Regex(@"(?(https?://)?mega(\.co)?\.nz/(#(?[^/>\s]+)|file/(?[^/>\s]+)))", DefaultOptions); private static readonly IProgress doodad = new Progress(_ => { }); - public override async Task<(ISource source, string failReason)> FindHandlerAsync(DiscordMessage message, ICollection handlers) + public override async Task<(ISource? source, string? failReason)> FindHandlerAsync(DiscordMessage message, ICollection handlers) { if (string.IsNullOrEmpty(message.Content)) return (null, null); @@ -44,7 +44,7 @@ namespace CompatBot.EventHandlers.LogParsing.SourceHandlers try { int read; - using (var stream = await client.DownloadAsync(uri, doodad, Config.Cts.Token).ConfigureAwait(false)) + await using (var stream = await client.DownloadAsync(uri, doodad, Config.Cts.Token).ConfigureAwait(false)) read = await stream.ReadBytesAsync(buf).ConfigureAwait(false); foreach (var handler in handlers) { @@ -72,10 +72,10 @@ namespace CompatBot.EventHandlers.LogParsing.SourceHandlers private sealed class MegaSource : ISource { - private IMegaApiClient client; - private Uri uri; - private INodeInfo node; - private IArchiveHandler handler; + private readonly IMegaApiClient client; + private readonly Uri uri; + private readonly INodeInfo node; + private readonly IArchiveHandler handler; public string SourceType => "Mega"; public string FileName => node.Name; @@ -93,7 +93,7 @@ namespace CompatBot.EventHandlers.LogParsing.SourceHandlers public async Task FillPipeAsync(PipeWriter writer, CancellationToken cancellationToken) { - using var stream = await client.DownloadAsync(uri, doodad, cancellationToken).ConfigureAwait(false); + await using var stream = await client.DownloadAsync(uri, doodad, cancellationToken).ConfigureAwait(false); await handler.FillPipeAsync(stream, writer, cancellationToken).ConfigureAwait(false); } } diff --git a/CompatBot/EventHandlers/LogParsing/SourceHandlers/OneDriveSourceHandler.cs b/CompatBot/EventHandlers/LogParsing/SourceHandlers/OneDriveSourceHandler.cs index 78a483b3..6fa39b68 100644 --- a/CompatBot/EventHandlers/LogParsing/SourceHandlers/OneDriveSourceHandler.cs +++ b/CompatBot/EventHandlers/LogParsing/SourceHandlers/OneDriveSourceHandler.cs @@ -21,7 +21,7 @@ namespace CompatBot.EventHandlers.LogParsing.SourceHandlers private static readonly Regex ExternalLink = new Regex(@"(?(https?://)?(1drv\.ms|onedrive\.live\.com)/[^>\s]+)", DefaultOptions); private static readonly Client client = new Client(); - public async override Task<(ISource source, string failReason)> FindHandlerAsync(DiscordMessage message, ICollection handlers) + public async override Task<(ISource? source, string? failReason)> FindHandlerAsync(DiscordMessage message, ICollection handlers) { if (string.IsNullOrEmpty(message.Content)) return (null, null); @@ -38,15 +38,16 @@ namespace CompatBot.EventHandlers.LogParsing.SourceHandlers if (m.Groups["onedrive_link"].Value is string lnk && !string.IsNullOrEmpty(lnk) && Uri.TryCreate(lnk, UriKind.Absolute, out var uri) - && await client.ResolveContentLinkAsync(uri, Config.Cts.Token).ConfigureAwait(false) is DriveItemMeta itemMeta) + && await client.ResolveContentLinkAsync(uri, Config.Cts.Token).ConfigureAwait(false) is DriveItemMeta itemMeta + && itemMeta.ContentDownloadUrl is string downloadUrl) { try { - var filename = itemMeta.Name; + var filename = itemMeta.Name ?? ""; var filesize = itemMeta.Size; - uri = new Uri(itemMeta.ContentDownloadUrl); + uri = new Uri(downloadUrl); - using var stream = await httpClient.GetStreamAsync(uri).ConfigureAwait(false); + await using var stream = await httpClient.GetStreamAsync(uri).ConfigureAwait(false); var buf = bufferPool.Rent(SnoopBufferSize); try { @@ -102,7 +103,7 @@ namespace CompatBot.EventHandlers.LogParsing.SourceHandlers public async Task FillPipeAsync(PipeWriter writer, CancellationToken cancellationToken) { using var client = HttpClientFactory.Create(); - using var stream = await client.GetStreamAsync(uri).ConfigureAwait(false); + await using var stream = await client.GetStreamAsync(uri, cancellationToken).ConfigureAwait(false); await handler.FillPipeAsync(stream, writer, cancellationToken).ConfigureAwait(false); } } diff --git a/CompatBot/EventHandlers/LogParsing/SourceHandlers/PastebinHandler.cs b/CompatBot/EventHandlers/LogParsing/SourceHandlers/PastebinHandler.cs index 985f0726..2f11ef9d 100644 --- a/CompatBot/EventHandlers/LogParsing/SourceHandlers/PastebinHandler.cs +++ b/CompatBot/EventHandlers/LogParsing/SourceHandlers/PastebinHandler.cs @@ -15,7 +15,7 @@ namespace CompatBot.EventHandlers.LogParsing.SourceHandlers { private static readonly Regex ExternalLink = new Regex(@"(?(https?://)pastebin.com/(raw/)?(?[^/>\s]+))", DefaultOptions); - public override async Task<(ISource source, string failReason)> FindHandlerAsync(DiscordMessage message, ICollection handlers) + public override async Task<(ISource? source, string? failReason)> FindHandlerAsync(DiscordMessage message, ICollection handlers) { if (string.IsNullOrEmpty(message.Content)) return (null, null); @@ -33,7 +33,7 @@ namespace CompatBot.EventHandlers.LogParsing.SourceHandlers && !string.IsNullOrEmpty(pid)) { var uri = new Uri("https://pastebin.com/raw/" + pid); - using var stream = await client.GetStreamAsync(uri).ConfigureAwait(false); + await using var stream = await client.GetStreamAsync(uri).ConfigureAwait(false); var buf = bufferPool.Rent(SnoopBufferSize); try { @@ -85,7 +85,7 @@ namespace CompatBot.EventHandlers.LogParsing.SourceHandlers public async Task FillPipeAsync(PipeWriter writer, CancellationToken cancellationToken) { using var client = HttpClientFactory.Create(); - using var stream = await client.GetStreamAsync(uri).ConfigureAwait(false); + await using var stream = await client.GetStreamAsync(uri, cancellationToken).ConfigureAwait(false); await handler.FillPipeAsync(stream, writer, cancellationToken).ConfigureAwait(false); } } diff --git a/CompatBot/EventHandlers/LogParsingHandler.cs b/CompatBot/EventHandlers/LogParsingHandler.cs index 1ce1866c..8f23aba1 100644 --- a/CompatBot/EventHandlers/LogParsingHandler.cs +++ b/CompatBot/EventHandlers/LogParsingHandler.cs @@ -48,7 +48,7 @@ namespace CompatBot.EventHandlers }; private static readonly SemaphoreSlim QueueLimiter = new SemaphoreSlim(Math.Max(1, Environment.ProcessorCount / 2), Math.Max(1, Environment.ProcessorCount / 2)); - private delegate void OnLog(DiscordClient client, DiscordChannel channel, DiscordMessage message, DiscordMember requester = null, bool checkExternalLinks = false, bool force = false); + private delegate void OnLog(DiscordClient client, DiscordChannel channel, DiscordMessage message, DiscordMember? requester = null, bool checkExternalLinks = false, bool force = false); private static event OnLog OnNewLog; static LogParsingHandler() => OnNewLog += EnqueueLogProcessing; @@ -72,12 +72,11 @@ namespace CompatBot.EventHandlers return Task.CompletedTask; } - public static async void EnqueueLogProcessing(DiscordClient client, DiscordChannel channel, DiscordMessage message, DiscordMember requester = null, bool checkExternalLinks = false, bool force = false) + public static async void EnqueueLogProcessing(DiscordClient client, DiscordChannel channel, DiscordMessage message, DiscordMember? requester = null, bool checkExternalLinks = false, bool force = false) { var start = DateTimeOffset.UtcNow; try { - // ReSharper disable once MethodHasAsyncOverload if (!QueueLimiter.Wait(0)) { Config.TelemetryClient?.TrackRequest(nameof(LogParsingHandler), start, TimeSpan.Zero, HttpStatusCode.TooManyRequests.ToString(), false); @@ -85,9 +84,9 @@ namespace CompatBot.EventHandlers return; } - bool parsedLog = false; + var parsedLog = false; var startTime = Stopwatch.StartNew(); - DiscordMessage botMsg = null; + DiscordMessage? botMsg = null; try { var possibleHandlers = sourceHandlers.Select(h => h.FindHandlerAsync(message, archiveHandlers).ConfigureAwait(false).GetAwaiter().GetResult()).ToList(); @@ -105,7 +104,7 @@ namespace CompatBot.EventHandlers botMsg = await channel.SendMessageAsync(embed: analyzingProgressEmbed.AddAuthor(client, message, source)).ConfigureAwait(false); parsedLog = true; - LogParseState result = null, tmpResult = null; + LogParseState? result = null, tmpResult; using (var timeout = new CancellationTokenSource(Config.LogParsingTimeoutInSec)) { using var combinedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(timeout.Token, Config.Cts.Token); @@ -196,7 +195,7 @@ namespace CompatBot.EventHandlers if (result.SelectedFilter != null) { var ignoreFlags = FilterAction.IssueWarning | FilterAction.SendMessage | FilterAction.ShowExplain; - await ContentFilter.PerformFilterActions(client, message, result.SelectedFilter, ignoreFlags, result.SelectedFilterContext).ConfigureAwait(false); + await ContentFilter.PerformFilterActions(client, message, result.SelectedFilter, ignoreFlags, result.SelectedFilterContext!).ConfigureAwait(false); } if (!force && string.IsNullOrEmpty(message.Content) && !isSpamChannel) @@ -268,7 +267,7 @@ namespace CompatBot.EventHandlers return; } - var linkStart = message.Content.IndexOf("http"); + var linkStart = message.Content.IndexOf("http", StringComparison.Ordinal); if (linkStart > -1) { var link = message.Content[linkStart..].Split(linkSeparator, 2)[0]; @@ -294,9 +293,9 @@ namespace CompatBot.EventHandlers } } - public static async Task ParseLogAsync(ISource source, Func onProgressAsync, CancellationToken cancellationToken) + public static async Task ParseLogAsync(ISource source, Func onProgressAsync, CancellationToken cancellationToken) { - LogParseState result = null; + LogParseState? result = null; try { try diff --git a/CompatBot/EventHandlers/NewBuildsMonitor.cs b/CompatBot/EventHandlers/NewBuildsMonitor.cs index 643afe9f..04877b56 100644 --- a/CompatBot/EventHandlers/NewBuildsMonitor.cs +++ b/CompatBot/EventHandlers/NewBuildsMonitor.cs @@ -40,7 +40,7 @@ namespace CompatBot.EventHandlers public static async Task MonitorAsync(DiscordClient client) { var lastCheck = DateTime.UtcNow.AddDays(-1); - Exception lastException = null; + Exception? lastException = null; while (!Config.Cts.IsCancellationRequested) { var now = DateTime.UtcNow; diff --git a/CompatBot/EventHandlers/PostLogHelpHandler.cs b/CompatBot/EventHandlers/PostLogHelpHandler.cs index 2d14e003..5aa4e729 100644 --- a/CompatBot/EventHandlers/PostLogHelpHandler.cs +++ b/CompatBot/EventHandlers/PostLogHelpHandler.cs @@ -26,16 +26,14 @@ namespace CompatBot.EventHandlers public static async Task OnMessageCreated(DiscordClient _, MessageCreateEventArgs args) { - if (DefaultHandlerFilter.IsFluff(args?.Message)) + if (DefaultHandlerFilter.IsFluff(args.Message)) return; -#if !DEBUG - if (!"help".Equals(args?.Channel?.Name, StringComparison.InvariantCultureIgnoreCase)) + if (!"help".Equals(args.Channel.Name, StringComparison.InvariantCultureIgnoreCase)) return; if (DateTime.UtcNow - lastMention < ThrottlingThreshold) return; -#endif var match = UploadLogMention.Match(args.Message.Content); if (!match.Success || string.IsNullOrEmpty(match.Groups["help"].Value)) @@ -50,7 +48,7 @@ namespace CompatBot.EventHandlers var lastBotMessages = await args.Channel.GetMessagesBeforeCachedAsync(args.Message.Id, 10).ConfigureAwait(false); foreach (var msg in lastBotMessages) if (BotReactionsHandler.NeedToSilence(msg).needToChill - || (msg.Author.IsCurrent && msg.Content == explanation.Text)) + || msg.Author.IsCurrent && msg.Content == explanation.Text) return; await args.Channel.SendMessageAsync(explanation.Text, explanation.Attachment, explanation.AttachmentFilename).ConfigureAwait(false); @@ -64,7 +62,7 @@ namespace CompatBot.EventHandlers public static async Task GetExplanationAsync(string term) { - using var db = new BotDb(); + await using var db = new BotDb(); var result = await db.Explanation.FirstOrDefaultAsync(e => e.Keyword == term).ConfigureAwait(false); return result ?? DefaultExplanation[term]; } diff --git a/CompatBot/EventHandlers/Starbucks.cs b/CompatBot/EventHandlers/Starbucks.cs index 8e7e819e..abf53f8d 100644 --- a/CompatBot/EventHandlers/Starbucks.cs +++ b/CompatBot/EventHandlers/Starbucks.cs @@ -85,9 +85,7 @@ namespace CompatBot.EventHandlers }; public static Task Handler(DiscordClient c, MessageReactionAddEventArgs args) - { - return CheckMessageAsync(c, args.Channel, args.User, args.Message, args.Emoji, false); - } + => CheckMessageAsync(c, args.Channel, args.User, args.Message, args.Emoji, false); public static async Task CheckBacklogAsync(DiscordClient client, DiscordGuild guild) { @@ -163,12 +161,12 @@ namespace CompatBot.EventHandlers .Distinct() .Select(u => channel.Guild .GetMemberAsync(u.Id) - .ContinueWith(ct => ct.IsCompletedSuccessfully ? ct : Task.FromResult((DiscordMember)null), TaskScheduler.Default)) + .ContinueWith(ct => ct.IsCompletedSuccessfully ? ct : Task.FromResult((DiscordMember?)null), TaskScheduler.Default)) .ToList() //force eager task creation .Select(t => t.Unwrap().ConfigureAwait(false).GetAwaiter().GetResult()) .Where(m => m != null) .ToList(); - var reporters = members.Where(m => m.Roles.Any()).ToList(); + var reporters = members.Where(m => m!.Roles.Any()).ToList(); if (reporters.Count < Config.Moderation.StarbucksThreshold) return; diff --git a/CompatBot/EventHandlers/UnknownCommandHandler.cs b/CompatBot/EventHandlers/UnknownCommandHandler.cs index 378cdd88..080787f6 100644 --- a/CompatBot/EventHandlers/UnknownCommandHandler.cs +++ b/CompatBot/EventHandlers/UnknownCommandHandler.cs @@ -25,7 +25,7 @@ namespace CompatBot.EventHandlers if (ex is InvalidOperationException && ex.Message.Contains("No matching subcommands were found")) ex = new CommandNotFoundException(e.Command.Name); - if (!(ex is CommandNotFoundException cnfe)) + if (ex is not CommandNotFoundException cnfe) { Config.Log.Error(e.Exception); diff --git a/CompatBot/Program.cs b/CompatBot/Program.cs index 00f9f601..11c6964b 100644 --- a/CompatBot/Program.cs +++ b/CompatBot/Program.cs @@ -39,12 +39,12 @@ namespace CompatBot if (args.Length > 0 && args[0] == "--dry-run") { Console.WriteLine("Database path: " + Path.GetDirectoryName(Path.GetFullPath(DbImporter.GetDbPath("fake.db", Environment.SpecialFolder.ApplicationData)))); - if (Assembly.GetEntryAssembly().GetCustomAttribute() != null) + if (Assembly.GetEntryAssembly()?.GetCustomAttribute() != null) Console.WriteLine("Bot config path: " + Path.GetDirectoryName(Path.GetFullPath(Config.GoogleApiConfigPath))); return; } - if (Process.GetCurrentProcess().Id == 0) + if (Environment.ProcessId == 0) Config.Log.Info("Well, this was unexpected"); var singleInstanceCheckThread = new Thread(() => { @@ -95,11 +95,11 @@ namespace CompatBot } } - using (var db = new BotDb()) + await using (var db = new BotDb()) if (!await DbImporter.UpgradeAsync(db, Config.Cts.Token)) return; - using (var db = new ThumbnailDb()) + await using (var db = new ThumbnailDb()) if (!await DbImporter.UpgradeAsync(db, Config.Cts.Token)) return; @@ -296,8 +296,8 @@ namespace CompatBot } ulong? channelId = null; - string restartMsg = null; - using (var db = new BotDb()) + string? restartMsg = null; + await using (var db = new BotDb()) { var chState = db.BotState.FirstOrDefault(k => k.Key == "bot-restart-channel"); if (chState != null) @@ -312,7 +312,7 @@ namespace CompatBot restartMsg = msgState.Value; db.BotState.Remove(msgState); } - db.SaveChanges(); + await db.SaveChangesAsync().ConfigureAwait(false); } if (string.IsNullOrEmpty(restartMsg)) restartMsg = null; diff --git a/CompatBot/ThumbScrapper/PsnScraper.cs b/CompatBot/ThumbScrapper/PsnScraper.cs index 6763681b..ee217a1a 100644 --- a/CompatBot/ThumbScrapper/PsnScraper.cs +++ b/CompatBot/ThumbScrapper/PsnScraper.cs @@ -23,7 +23,7 @@ namespace CompatBot.ThumbScrapper private static DateTime StoreRefreshTimestamp = DateTime.MinValue; private static readonly SemaphoreSlim QueueLimiter = new SemaphoreSlim(32, 32); - public async Task RunAsync(CancellationToken cancellationToken) + public static async Task RunAsync(CancellationToken cancellationToken) { do { @@ -53,7 +53,7 @@ namespace CompatBot.ThumbScrapper if (!match.Success) return; - if (!QueueLimiter.Wait(0)) + if (!QueueLimiter.Wait(0, cancellationToken)) return; try @@ -153,14 +153,15 @@ namespace CompatBot.ThumbScrapper // get all containers from all the menus var stores = await Client.GetStoresAsync(locale, cancellationToken).ConfigureAwait(false); if (!string.IsNullOrEmpty(stores?.Data.BaseUrl)) - containerIds.Add(Path.GetFileName(stores.Data.BaseUrl)); - foreach (var id in containerIds) - { - if (cancellationToken.IsCancellationRequested) - return; + containerIds?.Add(Path.GetFileName(stores.Data.BaseUrl)); + if (containerIds != null) + foreach (var id in containerIds) + { + if (cancellationToken.IsCancellationRequested) + return; - await ScrapeContainerIdsAsync(locale, id, knownContainers, cancellationToken).ConfigureAwait(false); - } + await ScrapeContainerIdsAsync(locale, id, knownContainers, cancellationToken).ConfigureAwait(false); + } Config.Log.Debug($"\tFound {knownContainers.Count} containers"); // now let's scrape for actual games in every container @@ -192,8 +193,8 @@ namespace CompatBot.ThumbScrapper do { var tries = 0; - Container container = null; - bool error = false; + Container? container = null; + var error = false; do { try @@ -224,7 +225,7 @@ namespace CompatBot.ThumbScrapper // returned items are just ids that need to be resolved if (returned > 0) { - foreach (var item in container.Data.Relationships.Children.Data) + foreach (var item in container.Data!.Relationships!.Children!.Data!) { if (cancellationToken.IsCancellationRequested) return; @@ -291,11 +292,11 @@ namespace CompatBot.ThumbScrapper private static bool NeedLookup(string contentId) { - using (var db = new ThumbnailDb()) - if (db.Thumbnail.FirstOrDefault(t => t.ContentId == contentId) is Thumbnail thumbnail) - if (!string.IsNullOrEmpty(thumbnail.Url)) - if (ScrapeStateProvider.IsFresh(new DateTime(thumbnail.Timestamp, DateTimeKind.Utc))) - return false; + using var db = new ThumbnailDb(); + if (db.Thumbnail.FirstOrDefault(t => t.ContentId == contentId) is Thumbnail thumbnail) + if (!string.IsNullOrEmpty(thumbnail.Url)) + if (ScrapeStateProvider.IsFresh(new DateTime(thumbnail.Timestamp, DateTimeKind.Utc))) + return false; return true; } @@ -338,7 +339,7 @@ namespace CompatBot.ThumbScrapper } } - private static async Task AddOrUpdateThumbnailAsync(string contentId, string name, string url, CancellationToken cancellationToken) + private static async Task AddOrUpdateThumbnailAsync(string contentId, string? name, string? url, CancellationToken cancellationToken) { var match = ContentIdMatcher.Match(contentId); if (!match.Success) @@ -349,7 +350,7 @@ namespace CompatBot.ThumbScrapper return; name = string.IsNullOrEmpty(name) ? null : name; - using var db = new ThumbnailDb(); + await using var db = new ThumbnailDb(); var savedItem = db.Thumbnail.FirstOrDefault(t => t.ProductCode == productCode); if (savedItem == null) { @@ -361,7 +362,7 @@ namespace CompatBot.ThumbScrapper Url = url, Timestamp = DateTime.UtcNow.Ticks, }; - db.Thumbnail.Add(newItem); + await db.Thumbnail.AddAsync(newItem, cancellationToken).ConfigureAwait(false); } else if (!string.IsNullOrEmpty(url)) { @@ -426,8 +427,6 @@ namespace CompatBot.ThumbScrapper } private static void PrintError(Exception e) - { - Config.Log.Error(e, "Error scraping thumbnails"); - } + => Config.Log.Error(e, "Error scraping thumbnails"); } } diff --git a/CompatBot/Utils/DefaultHandlerFilter.cs b/CompatBot/Utils/DefaultHandlerFilter.cs index b4fe3973..048eec55 100644 --- a/CompatBot/Utils/DefaultHandlerFilter.cs +++ b/CompatBot/Utils/DefaultHandlerFilter.cs @@ -5,9 +5,9 @@ namespace CompatBot.Utils { internal static class DefaultHandlerFilter { - internal static bool IsFluff(DiscordMessage message) + internal static bool IsFluff(DiscordMessage? message) { - if (message == null) + if (message is null) return true; if (message.Author.IsBotSafeCheck()) diff --git a/CompatBot/Utils/Extensions/DiscordClientExtensions.cs b/CompatBot/Utils/Extensions/DiscordClientExtensions.cs index c49609c1..ea9ad18e 100644 --- a/CompatBot/Utils/Extensions/DiscordClientExtensions.cs +++ b/CompatBot/Utils/Extensions/DiscordClientExtensions.cs @@ -134,13 +134,13 @@ namespace CompatBot.Utils } } - public static async Task ReportAsync(this DiscordClient client, string infraction, DiscordMessage message, IEnumerable reporters, string? comment, ReportSeverity severity) + public static async Task ReportAsync(this DiscordClient client, string infraction, DiscordMessage message, IEnumerable reporters, string? comment, ReportSeverity severity) { var getLogChannelTask = client.GetChannelAsync(Config.BotLogId); var embedBuilder = MakeReportTemplate(client, infraction, message, severity); var reportText = string.IsNullOrEmpty(comment) ? "" : comment.Sanitize() + Environment.NewLine; embedBuilder.Description = (reportText + embedBuilder.Description).Trim(EmbedPager.MaxDescriptionLength); - var mentions = reporters.Select(GetMentionWithNickname); + var mentions = reporters.Where(m => m is not null).Select(GetMentionWithNickname!); embedBuilder.AddField("Reporters", string.Join(Environment.NewLine, mentions)); var logChannel = await getLogChannelTask.ConfigureAwait(false); return await logChannel.SendMessageAsync(embed: embedBuilder.Build(), mentions: Config.AllowedMentions.Nothing).ConfigureAwait(false); diff --git a/CompatBot/Utils/Extensions/DiscordMessageExtensions.cs b/CompatBot/Utils/Extensions/DiscordMessageExtensions.cs index ce869e05..ab971e07 100644 --- a/CompatBot/Utils/Extensions/DiscordMessageExtensions.cs +++ b/CompatBot/Utils/Extensions/DiscordMessageExtensions.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.IO; -using System.Linq; using System.Net.Http; using System.Threading.Tasks; using CompatApiClient.Compression; diff --git a/CompatBot/Utils/Extensions/InteractivityExtensions.cs b/CompatBot/Utils/Extensions/InteractivityExtensions.cs index eb1d1b04..3bc76f27 100644 --- a/CompatBot/Utils/Extensions/InteractivityExtensions.cs +++ b/CompatBot/Utils/Extensions/InteractivityExtensions.cs @@ -15,7 +15,7 @@ namespace CompatBot.Utils this InteractivityExtension interactivity, DiscordMessage message, DiscordUser user, - params DiscordEmoji[] reactions) + params DiscordEmoji?[] reactions) => WaitForMessageOrReactionAsync(interactivity, message, user, null, reactions); public static async Task<(DiscordMessage? message, DiscordMessage? text, MessageReactionAddEventArgs? reaction)> WaitForMessageOrReactionAsync( diff --git a/CompatBot/Utils/ResultFormatters/IrdSearchResultFormatter.cs b/CompatBot/Utils/ResultFormatters/IrdSearchResultFormatter.cs index b39256fc..4e0dc28b 100644 --- a/CompatBot/Utils/ResultFormatters/IrdSearchResultFormatter.cs +++ b/CompatBot/Utils/ResultFormatters/IrdSearchResultFormatter.cs @@ -7,14 +7,14 @@ namespace CompatBot.Utils.ResultFormatters { public static class IrdSearchResultFormatter { - public static DiscordEmbedBuilder AsEmbed(this SearchResult searchResult) + public static DiscordEmbedBuilder AsEmbed(this SearchResult? searchResult) { var result = new DiscordEmbedBuilder { //Title = "IRD Library Search Result", Color = Config.Colors.DownloadLinks, }; - if (searchResult.Data.Count == 0) + if (searchResult?.Data is null or {Count: 0}) { result.Color = Config.Colors.LogResultFailed; result.Description = "No matches were found"; @@ -23,14 +23,15 @@ namespace CompatBot.Utils.ResultFormatters foreach (var item in searchResult.Data) { - var parts = item.Filename?.Split('-'); - if (parts == null) - parts = new string[] {null, null}; - else if (parts.Length == 1) - parts = new[] {null, item.Filename}; + if (string.IsNullOrEmpty(item.Filename)) + continue; + + string[] parts = item.Filename.Split('-'); + if (parts.Length == 1) + parts = new[] {"", item.Filename}; result.AddField( - $"[{parts?[0]} v{item.GameVersion}] {item.Title?.Sanitize().Trim(EmbedPager.MaxFieldTitleLength)}", - $"[⏬ `{parts[1]?.Sanitize().Trim(200)}`]({IrdClient.GetDownloadLink(item.Filename)}) [ℹ Info]({IrdClient.GetInfoLink(item.Filename)})" + $"[{parts[0]} v{item.GameVersion}] {item.Title?.Sanitize().Trim(EmbedPager.MaxFieldTitleLength)}", + $"[⏬ `{parts[1].Sanitize().Trim(200)}`]({IrdClient.GetDownloadLink(item.Filename)}) [ℹ Info]({IrdClient.GetInfoLink(item.Filename)})" ); } return result; diff --git a/CompatBot/Utils/ResultFormatters/LogParserResultFormatter.CurrentSettingsSections.cs b/CompatBot/Utils/ResultFormatters/LogParserResultFormatter.CurrentSettingsSections.cs index 36f226e3..34e5dacb 100644 --- a/CompatBot/Utils/ResultFormatters/LogParserResultFormatter.CurrentSettingsSections.cs +++ b/CompatBot/Utils/ResultFormatters/LogParserResultFormatter.CurrentSettingsSections.cs @@ -51,8 +51,8 @@ namespace CompatBot.Utils.ResultFormatters items["cpu_model"] = cpuModel; items["thread_count"] = cpuInfo.Groups["thread_count"].Value; items["memory_amount"] = cpuInfo.Groups["memory_amount"].Value; - items["cpu_tsc"] = cpuInfo.Groups["tsc"]?.Value; - items["cpu_extensions"] = cpuInfo.Groups["cpu_extensions"]?.Value; + items["cpu_tsc"] = cpuInfo.Groups["tsc"].Value; + items["cpu_extensions"] = cpuInfo.Groups["cpu_extensions"].Value; } if (osInfo.Success) { @@ -135,7 +135,7 @@ namespace CompatBot.Utils.ResultFormatters private const int ColumnWidth = 30; - private static (string name, List lines) BuildCpuSection(NameValueCollection items) + private static (string? name, List? lines) BuildCpuSection(NameValueCollection items) { if (string.IsNullOrEmpty(items["ppu_decoder"])) return (null, null); @@ -155,7 +155,7 @@ namespace CompatBot.Utils.ResultFormatters return ("CPU Settings", lines); } - private static (string name, List lines) BuildGpuSection(NameValueCollection items) + private static (string? name, List? lines) BuildGpuSection(NameValueCollection items) { if (string.IsNullOrEmpty(items["renderer"])) return (null, null); @@ -199,7 +199,7 @@ namespace CompatBot.Utils.ResultFormatters return ("GPU Settings", lines); } - private static void BuildSettingsSections(DiscordEmbedBuilder builder, NameValueCollection items, (string name, List lines) colA, (string name, List lines) colB) + private static void BuildSettingsSections(DiscordEmbedBuilder builder, NameValueCollection items, (string? name, List? lines) colA, (string? name, List? lines) colB) { if (colA.lines?.Count > 0 && colB.lines?.Count > 0) { @@ -212,19 +212,19 @@ namespace CompatBot.Utils.ResultFormatters var linesToSkip = colAToRemove - linesToRemove; var tmp = colA.lines; colA.lines = new List(tmp.Count - linesToRemove); - for (var i = 0; i < tmp.Count; i++) - if (!tmp[i].EndsWith("N/A") || (linesToSkip--) > 0) - colA.lines.Add(tmp[i]); + foreach (var t in tmp) + if (!t.EndsWith("N/A") || linesToSkip-- > 0) + colA.lines.Add(t); linesToSkip = colBToRemove - linesToRemove; tmp = colB.lines; colB.lines = new List(tmp.Count - linesToRemove); for (var i = 0; i < tmp.Count; i++) - if (!tmp[i].EndsWith("N/A") || (linesToSkip--) > 0) + if (!tmp[i].EndsWith("N/A") || linesToSkip-- > 0) colB.lines.Add(tmp[i]); } - AddSettingsSection(builder, colA.name, colA.lines, isCustomSettings); - AddSettingsSection(builder, colB.name, colB.lines, isCustomSettings); + AddSettingsSection(builder, colA.name!, colA.lines, isCustomSettings); + AddSettingsSection(builder, colB.name!, colB.lines, isCustomSettings); } } @@ -232,7 +232,7 @@ namespace CompatBot.Utils.ResultFormatters { var result = new StringBuilder(); foreach (var line in lines) - result.Append("`").Append(line).AppendLine("`"); + result.Append('`').Append(line).AppendLine("`"); if (isCustomSettings) name = "Per-game " + name; builder.AddField(name, result.ToString().FixSpaces(), true); diff --git a/CompatBot/Utils/ResultFormatters/LogParserResultFormatter.GeneralNotesSection.cs b/CompatBot/Utils/ResultFormatters/LogParserResultFormatter.GeneralNotesSection.cs index 6604f34b..f9adbc51 100644 --- a/CompatBot/Utils/ResultFormatters/LogParserResultFormatter.GeneralNotesSection.cs +++ b/CompatBot/Utils/ResultFormatters/LogParserResultFormatter.GeneralNotesSection.cs @@ -21,7 +21,7 @@ namespace CompatBot.Utils.ResultFormatters private static async Task BuildNotesSectionAsync(DiscordEmbedBuilder builder, LogParseState state, DiscordClient discordClient) { - var items = state.CompleteCollection; + var items = state.CompletedCollection; var multiItems = state.CompleteMultiValueCollection; var notes = new List(); var (_, brokenDump, longestPath) = await HasBrokenFilesAsync(state).ConfigureAwait(false); @@ -35,8 +35,8 @@ namespace CompatBot.Utils.ResultFormatters if (multiItems["fatal_error"] is UniqueList fatalErrors && fatalErrors.Any()) { var contexts = multiItems["fatal_error_context"]; - var reducedFatalErros = GroupSimilar(fatalErrors); - foreach (var (fatalError, count, similarity) in reducedFatalErros) + var reducedFatalErrors = GroupSimilar(fatalErrors); + foreach (var (fatalError, count, similarity) in reducedFatalErrors) { var knownFatal = false; if (fatalError.Contains("psf.cpp", StringComparison.InvariantCultureIgnoreCase) @@ -176,13 +176,13 @@ namespace CompatBot.Utils.ResultFormatters items["ldr_disc_full"], items["ldr_path_full"], items["ldr_boot_path_full"], - items["elf_boot_path_full"] + items["elf_boot_path_full"], }.Where(s => !string.IsNullOrEmpty(s)); const int maxPath = 260; const int maxFolderPath = 260 - 1 - 8 - 3; foreach (var p in knownPaths) { - if (p.Length > maxPath) + if (p!.Length > maxPath) { notes.Add($"⚠ Some file paths are longer than {maxPath} characters"); break; @@ -309,7 +309,7 @@ namespace CompatBot.Utils.ResultFormatters if (cpu.StartsWith("Intel") || cpu.StartsWith("Pentium")) { - if (!items["cpu_extensions"].Contains("TSX") + if (items["cpu_extensions"]?.Contains("TSX") is not true && (cpu.Contains("Core2") || cpu.Contains("Celeron") || cpu.Contains("Atom") @@ -323,9 +323,9 @@ namespace CompatBot.Utils.ResultFormatters } } - Version oglVersion = null; + Version? oglVersion = null; if (items["opengl_version"] is string oglVersionString) - Version.TryParse(oglVersionString, out oglVersion); + _ = Version.TryParse(oglVersionString, out oglVersion); if (items["glsl_version"] is string glslVersionString && Version.TryParse(glslVersionString, out var glslVersion)) { @@ -352,7 +352,7 @@ namespace CompatBot.Utils.ResultFormatters var modelNumber = intelMatch.Groups["gpu_model_number"].Value; if (!string.IsNullOrEmpty(modelNumber) && modelNumber.StartsWith('P')) modelNumber = modelNumber[1..]; - int.TryParse(modelNumber, out var modelNumberInt); + _ = int.TryParse(modelNumber, out var modelNumberInt); if (family == "UHD" || family == "Iris Plus" || modelNumberInt > 500 && modelNumberInt < 1000) notes.Add("⚠ Intel iGPUs are not officially supported; visual glitches are to be expected"); else @@ -566,12 +566,13 @@ namespace CompatBot.Utils.ResultFormatters if (items["rap_file"] is UniqueList raps && raps.Any()) { var limitTo = 5; - var licenseNames = raps + List licenseNames = raps .Select(Path.GetFileName) + .Where(l => !string.IsNullOrEmpty(l)) .Distinct() .Except(KnownBogusLicenses) .OrderBy(l => l) - .ToList(); + .ToList()!; var formattedLicenseNames = licenseNames .Select(p => $"{StringUtils.InvisibleSpacer}`{p}`") .ToList(); @@ -591,7 +592,12 @@ namespace CompatBot.Utils.ResultFormatters builder.AddField("Missing Licenses", content); var gameRegion = serial?.Length > 3 ? new[] {serial[2]} : Enumerable.Empty(); - var dlcRegions = licenseNames.Select(n => n[9]).Concat(gameRegion).Distinct().ToArray(); + var dlcRegions = licenseNames + .Where(l => l.Length > 9) + .Select(n => n[9]) + .Concat(gameRegion) + .Distinct() + .ToArray(); if (dlcRegions.Length > 1) generalNotes.Add($"🤔 That is a very interesting DLC collection from {dlcRegions.Length} different regions"); if (KnownCustomLicenses.Overlaps(licenseNames)) @@ -602,10 +608,10 @@ namespace CompatBot.Utils.ResultFormatters private static async Task<(bool irdChecked, bool broken, int longestPath)> HasBrokenFilesAsync(LogParseState state) { - var items = state.CompleteCollection; + var items = state.CompletedCollection; var multiItems = state.CompleteMultiValueCollection; var defaultLongestPath = "/PS3_GAME/USRDIR/".Length + (1+8+3)*2; // usually there's at least one more level for data files - if (!(items["serial"] is string productCode)) + if (items["serial"] is not string productCode) return (false, false, defaultLongestPath); if (!productCode.StartsWith("B") && !productCode.StartsWith("M")) @@ -656,7 +662,10 @@ namespace CompatBot.Utils.ResultFormatters return (true, true, longestPath); } - var knownDirs = new HashSet(knownFiles.Select(f => Path.GetDirectoryName(f).Replace('\\', '/')), + var knownDirs = new HashSet( + knownFiles + .Select(f => Path.GetDirectoryName(f)?.Replace('\\', '/')) + .Where(p => !string.IsNullOrEmpty(p))!, StringComparer.InvariantCultureIgnoreCase); var brokenDirs = missingDirs.Where(knownDirs.Contains).ToList(); if (brokenDirs.Count > 0) diff --git a/CompatBot/Utils/ResultFormatters/LogParserResultFormatter.WeirdSettingsSection.cs b/CompatBot/Utils/ResultFormatters/LogParserResultFormatter.WeirdSettingsSection.cs index 496fd561..2668d878 100644 --- a/CompatBot/Utils/ResultFormatters/LogParserResultFormatter.WeirdSettingsSection.cs +++ b/CompatBot/Utils/ResultFormatters/LogParserResultFormatter.WeirdSettingsSection.cs @@ -15,11 +15,11 @@ namespace CompatBot.Utils.ResultFormatters { private static void BuildWeirdSettingsSection(DiscordEmbedBuilder builder, LogParseState state, List generalNotes) { - var items = state.CompleteCollection; + var items = state.CompletedCollection; var multiItems = state.CompleteMultiValueCollection; var notes = new List(); var serial = items["serial"] ?? ""; - int.TryParse(items["thread_count"], out var threadCount); + _ = int.TryParse(items["thread_count"], out var threadCount); if (items["disable_logs"] == EnabledMark) notes.Add("❗ `Silence All Logs` is enabled, please disable and upload a new log"); else if (!string.IsNullOrWhiteSpace(items["log_disabled_channels"]) @@ -83,8 +83,8 @@ namespace CompatBot.Utils.ResultFormatters && !(items["resolution"] == "1920x1080" && Known1080pIds.Contains(serial))) notes.Add("⚠ `Resolution` was changed from the recommended `1280x720`"); - var dimensions = items["resolution"].Split("x"); - if (dimensions.Length > 1 + var dimensions = items["resolution"]?.Split("x"); + if (dimensions?.Length > 1 && int.TryParse(dimensions[0], out var width) && int.TryParse(dimensions[1], out var height)) { @@ -172,7 +172,7 @@ namespace CompatBot.Utils.ResultFormatters } var vsync = items["vsync"] == EnabledMark; - string vkPm; + string? vkPm; if (items["rsx_present_mode"] is string pm) RsxPresentModeMap.TryGetValue(pm, out vkPm); else @@ -354,8 +354,8 @@ namespace CompatBot.Utils.ResultFormatters CheckGt6Settings(serial, items, notes, generalNotes); CheckSly4Settings(serial, items, notes); CheckDragonsCrownSettings(serial, items, notes); - CheckLbpSettings(serial, items, notes, generalNotes); - CheckKillzone3Settings(serial, items, notes, ppuPatches, patchNames); + CheckLbpSettings(serial, items, generalNotes); + CheckKillzone3Settings(serial, items, notes, patchNames); } else if (items["game_title"] == "vsh.self") CheckVshSettings(items, notes, generalNotes); @@ -393,7 +393,7 @@ namespace CompatBot.Utils.ResultFormatters if (items["shader_mode"] == "Interpreter only") notes.Add("⚠ `Shader Interpreter Only` mode is not accurate and very demanding"); - else if (!items["shader_mode"].StartsWith("Async")) + else if (items["shader_mode"]?.StartsWith("Async") is not true) notes.Add("❔ Async shader compilation is disabled"); if (items["driver_recovery_timeout"] is string driverRecoveryTimeout && int.TryParse(driverRecoveryTimeout, out var drtValue) @@ -579,7 +579,7 @@ namespace CompatBot.Utils.ResultFormatters private static void CheckJojoSettings(string serial, LogParseState state, List notes, Dictionary ppuPatches, HashSet ppuHashes, List generalNotes) { - var items = state.CompleteCollection; + var items = state.CompletedCollection; if (AllStarBattleIds.Contains(serial) || serial == "BLJS10318" || serial == "NPJB00753") { if (items["audio_buffering"] == EnabledMark && items["audio_buffer_duration"] != "20") @@ -733,7 +733,7 @@ namespace CompatBot.Utils.ResultFormatters && items["spu_block_size"] is string blockSize && blockSize != "Safe") notes.Add("⚠ Please change `SPU Block Size` to `Safe` for this game"); - */ + */ } else if (GowAscIds.Contains(serial)) generalNotes.Add("ℹ This game is known to be very unstable"); @@ -918,7 +918,7 @@ namespace CompatBot.Utils.ResultFormatters "NPEA00321", "NPEA90084", "NPEA90085", "NPEA90086", "NPHA80140", "NPJA90178", "NPUA70133", }; - private static void CheckKillzone3Settings(string serial, NameValueCollection items, List notes, Dictionary ppuPatches, UniqueList patchNames) + private static void CheckKillzone3Settings(string serial, NameValueCollection items, List notes, UniqueList patchNames) { if (!Killzone3Ids.Contains(serial)) return; @@ -1074,13 +1074,6 @@ namespace CompatBot.Utils.ResultFormatters notes.Add("⚠ Please enable `Read Color Buffer`"); needChanges = true; } -/* - if (items["write_depth_buffers"] == DisabledMark) - { - notes.Add("ℹ `Write Depth Buffers` might be required"); - needChanges = true; - } -*/ if (items["read_depth_buffer"] == DisabledMark) { notes.Add("ℹ `Read Depth Buffer` might be required"); @@ -1183,7 +1176,7 @@ namespace CompatBot.Utils.ResultFormatters "NPUA70117", "NPHA80163", // lbp2 demo and beta }; - private static void CheckLbpSettings(string serial, NameValueCollection items, List notes, List generalNotes) + private static void CheckLbpSettings(string serial, NameValueCollection items, List generalNotes) { if (!AllLbpGames.Contains(serial)) return; diff --git a/CompatBot/Utils/ResultFormatters/LogParserResultFormatter.cs b/CompatBot/Utils/ResultFormatters/LogParserResultFormatter.cs index 201a6001..8b8f880b 100644 --- a/CompatBot/Utils/ResultFormatters/LogParserResultFormatter.cs +++ b/CompatBot/Utils/ResultFormatters/LogParserResultFormatter.cs @@ -231,10 +231,10 @@ namespace CompatBot.Utils.ResultFormatters public static async Task AsEmbedAsync(this LogParseState state, DiscordClient client, DiscordMessage message, ISource source) { DiscordEmbedBuilder builder; - state.CompleteCollection ??= state.WipCollection; + state.CompletedCollection ??= state.WipCollection; state.CompleteMultiValueCollection ??= state.WipMultiValueCollection; - var collection = state.CompleteCollection; - if (collection?.Count > 0) + var collection = state.CompletedCollection; + if (collection.Count > 0) { var ldrGameSerial = collection["ldr_game_serial"] ?? collection["ldr_path_serial"]; if (collection["serial"] is string serial @@ -296,8 +296,8 @@ namespace CompatBot.Utils.ResultFormatters private static void CleanupValues(LogParseState state) { - var items = state.CompleteCollection; - var multiItems = state.CompleteMultiValueCollection; + var items = state.CompletedCollection!; + var multiItems = state.CompleteMultiValueCollection!; if (items["strict_rendering_mode"] == "true") items["resolution_scale"] = "Strict Mode"; if (items["spu_threads"] == "0") @@ -639,7 +639,7 @@ namespace CompatBot.Utils.ResultFormatters 8 => "7", 9 => "8", 10 => "8.1", - int v when v >= 20 && v < 30 => ((v % 10) switch + >= 20 and < 30 => (driverVer.Major % 10) switch { // see https://en.wikipedia.org/wiki/Windows_Display_Driver_Model#WDDM_2.0 0 => "10", @@ -651,7 +651,7 @@ namespace CompatBot.Utils.ResultFormatters 6 => "10 1903", 7 => "10 2004", _ => null, - }), + }, _ => null, }; } @@ -676,34 +676,37 @@ namespace CompatBot.Utils.ResultFormatters }, 10 => windowsVersion.Build switch { - int v when v < 10240 => ("10 TH1 Build " + v), + < 10240 => "10 TH1 Build " + windowsVersion.Build, 10240 => "10 1507", - int v when v < 10586 => ("10 TH2 Build " + v), + < 10586 => "10 TH2 Build " + windowsVersion.Build, 10586 => "10 1511", - int v when v < 14393 => ("10 RS1 Build " + v), + < 14393 => "10 RS1 Build " + windowsVersion.Build, 14393 => "10 1607", - int v when v < 15063 => ("10 RS2 Build " + v), + < 15063 => "10 RS2 Build " + windowsVersion.Build, 15063 => "10 1703", - int v when v < 16299 => ("10 RS3 Build " + v), + < 16299 => "10 RS3 Build " + windowsVersion.Build, 16299 => "10 1709", - int v when v < 17134 => ("10 RS4 Build " + v), + < 17134 => "10 RS4 Build " + windowsVersion.Build, 17134 => "10 1803", - int v when v < 17763 => ("10 RS5 Build " + v), + < 17763 => "10 RS5 Build " + windowsVersion.Build, 17763 => "10 1809", - int v when v < 18362 => ("10 19H1 Build " + v), + < 18362 => "10 19H1 Build " + windowsVersion.Build, 18362 => "10 1903", 18363 => "10 1909", - int v when v < 19041 => ("10 20H1 Build " + v), + < 19041 => "10 20H1 Build " + windowsVersion.Build, 19041 => "10 2004", 19042 => "10 20H2", - int v when v < 19536 => ("10 Beta Build " + v), - _ => ("10 21H1 Build " + windowsVersion.Build) + < 19536 => "10 Beta Build " + windowsVersion.Build, + _ => "10 21H1 Build " + windowsVersion.Build }, _ => null, }; - private static string GetLinuxVersion(string release, string version) + private static string? GetLinuxVersion(string? release, string version) { + if (string.IsNullOrEmpty(release)) + return null; + var kernelVersion = release; if (LinuxKernelVersion.Match(release) is Match m && m.Success) kernelVersion = m.Groups["version"].Value; diff --git a/CompatBot/Utils/ResultFormatters/TitlePatchFormatter.cs b/CompatBot/Utils/ResultFormatters/TitlePatchFormatter.cs index f2ba0026..d22677cc 100644 --- a/CompatBot/Utils/ResultFormatters/TitlePatchFormatter.cs +++ b/CompatBot/Utils/ResultFormatters/TitlePatchFormatter.cs @@ -14,7 +14,7 @@ namespace CompatBot.Utils.ResultFormatters internal static class TitlePatchFormatter { // thanks BCES00569 - public static async Task> AsEmbedAsync(this TitlePatch patch, DiscordClient client, string productCode) + public static async Task> AsEmbedAsync(this TitlePatch? patch, DiscordClient client, string productCode) { var result = new List(); var pkgs = patch?.Tag?.Packages; diff --git a/Tests/LogParsingProfiler.cs b/Tests/LogParsingProfiler.cs index 0b1f7b66..89f0aed2 100644 --- a/Tests/LogParsingProfiler.cs +++ b/Tests/LogParsingProfiler.cs @@ -54,7 +54,7 @@ namespace Tests Config.Log.Debug(msg); } Config.Log.Debug("~~~~~~~~~~~~~~~~~~~~"); - Assert.That(result.CompleteCollection, Is.Not.Null.And.Not.Empty); + Assert.That(result.CompletedCollection, Is.Not.Null.And.Not.Empty); } } } diff --git a/discord-bot-net.sln.DotSettings b/discord-bot-net.sln.DotSettings index be7c6377..a70e8e63 100644 --- a/discord-bot-net.sln.DotSettings +++ b/discord-bot-net.sln.DotSettings @@ -1,4 +1,5 @@  + True True True True @@ -6,11 +7,15 @@ True True True + True + True + True True True True True True + True True True True