mirror of
https://github.com/RPCS3/discord-bot.git
synced 2026-01-31 01:25:22 +01:00
botupdate, part 2
This commit is contained in:
@@ -171,17 +171,16 @@ namespace PsnClient
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<Container?> GetGameContainerAsync(string locale, string containerId, int start, int take, Dictionary<string, string?>? filters, CancellationToken cancellationToken)
|
||||
public async Task<Container?> GetGameContainerAsync(string locale, string containerId, int start, int take, Dictionary<string, string> 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<string, string?>();
|
||||
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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,12 +23,12 @@ namespace CompatBot.Commands
|
||||
private static readonly Regex Duration = new Regex(@"((?<days>\d+)(\.|d\s*))?((?<hours>\d+)(\:|h\s*))?((?<mins>\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<EventSchedule> events;
|
||||
using (var db = new BotDb())
|
||||
await using var db = new BotDb();
|
||||
IQueryable<EventSchedule> query = db.EventSchedule;
|
||||
if (year.HasValue)
|
||||
query = query.Where(e => e.Year == year);
|
||||
else
|
||||
{
|
||||
IQueryable<EventSchedule> 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<EventSchedule> 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<string> FuzzyMatchEventName(BotDb db, string eventName)
|
||||
private static async Task<string?> 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<string> FuzzyMatchEntryName(BotDb db, string eventName)
|
||||
private static async Task<string?> 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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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<string>(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<string>(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<string>(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);
|
||||
|
||||
@@ -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))
|
||||
{
|
||||
|
||||
@@ -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<FirmwareInfo> fwList = null)
|
||||
internal static async Task CheckFwUpdateForAnnouncementAsync(DiscordClient client, List<FirmwareInfo>? fwList = null)
|
||||
{
|
||||
fwList ??= await Client.GetHighestFwVersionAsync(Config.Cts.Token).ConfigureAwait(false);
|
||||
if (fwList.Count == 0)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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<string>());
|
||||
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<string> 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})";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,25 +18,22 @@ namespace CompatBot.Database.Providers
|
||||
{
|
||||
internal static class ContentFilter
|
||||
{
|
||||
private static Dictionary<FilterContext, AhoCorasickDoubleArrayTrie<Piracystring>> filters = new Dictionary<FilterContext, AhoCorasickDoubleArrayTrie<Piracystring>>();
|
||||
private static Dictionary<FilterContext, AhoCorasickDoubleArrayTrie<Piracystring>?> filters = new Dictionary<FilterContext, AhoCorasickDoubleArrayTrie<Piracystring>?>();
|
||||
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<Piracystring> FindTriggerAsync(FilterContext ctx, string str)
|
||||
public static Task<Piracystring?> 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<FilterContext, AhoCorasickDoubleArrayTrie<Piracystring>>();
|
||||
var newFilters = new Dictionary<FilterContext, AhoCorasickDoubleArrayTrie<Piracystring>?>();
|
||||
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<FilterAction>();
|
||||
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);
|
||||
|
||||
@@ -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<string>();
|
||||
var entries = cache.GetCacheEntries<string>();
|
||||
var savedKeys = new HashSet<string>();
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)/(?<invite_id>[a-z0-9\-]+))|(\.me/(?<me_id>.*?))(\s|>|$))", DefaultOptions);
|
||||
private static readonly Regex DiscordInviteLink = new Regex(@"(https?://)?discord(app\.com/invite|\.gg)/(?<invite_id>[a-z0-9\-]+)", DefaultOptions);
|
||||
private static readonly Regex DiscordMeLink = new Regex(@"(https?://)?discord\.me/(?<me_id>.*?)(\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<DiscordInvite> invites)> GetInvitesAsync(this DiscordClient client, string message, DiscordUser author = null, bool tryMessageAsACode = false)
|
||||
public static async Task<(bool hasInvalidInvite, bool attemptToWorkaround, List<DiscordInvite> invites)> GetInvitesAsync(this DiscordClient client, string message, DiscordUser? author = null, bool tryMessageAsACode = false)
|
||||
{
|
||||
if (string.IsNullOrEmpty(message))
|
||||
return (false, false, new List<DiscordInvite>(0));
|
||||
|
||||
@@ -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<byte> header)
|
||||
public (bool result, string? reason) CanHandle(string fileName, int fileSize, ReadOnlySpan<byte> 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();
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace CompatBot.EventHandlers.LogParsing.ArchiveHandlers
|
||||
{
|
||||
public interface IArchiveHandler
|
||||
{
|
||||
(bool result, string reason) CanHandle(string fileName, int fileSize, ReadOnlySpan<byte> header);
|
||||
(bool result, string? reason) CanHandle(string fileName, int fileSize, ReadOnlySpan<byte> header);
|
||||
Task FillPipeAsync(Stream sourceStream, PipeWriter writer, CancellationToken cancellationToken);
|
||||
long LogSize { get; }
|
||||
long SourcePosition { get; }
|
||||
|
||||
@@ -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<byte> header)
|
||||
public (bool result, string? reason) CanHandle(string fileName, int fileSize, ReadOnlySpan<byte> 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));
|
||||
}
|
||||
|
||||
@@ -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<byte> header)
|
||||
public (bool result, string? reason) CanHandle(string fileName, int fileSize, ReadOnlySpan<byte> 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
|
||||
|
||||
@@ -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<byte> header)
|
||||
public (bool result, string? reason) CanHandle(string fileName, int fileSize, ReadOnlySpan<byte> 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
|
||||
|
||||
@@ -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<byte> header)
|
||||
public (bool result, string? reason) CanHandle(string fileName, int fileSize, ReadOnlySpan<byte> 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
|
||||
|
||||
@@ -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<string> 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");
|
||||
}
|
||||
|
||||
@@ -8,8 +8,8 @@ namespace CompatBot.EventHandlers.LogParsing.POCOs
|
||||
{
|
||||
public class LogParseState
|
||||
{
|
||||
public NameValueCollection CompleteCollection = null;
|
||||
public NameUniqueObjectCollection<string> CompleteMultiValueCollection = null;
|
||||
public NameValueCollection? CompletedCollection;
|
||||
public NameUniqueObjectCollection<string>? CompleteMultiValueCollection;
|
||||
public NameValueCollection WipCollection = new NameValueCollection();
|
||||
public NameUniqueObjectCollection<string> WipMultiValueCollection = new NameUniqueObjectCollection<string>();
|
||||
public readonly Dictionary<string, int> ValueHitStats = new Dictionary<string, int>();
|
||||
@@ -17,8 +17,8 @@ namespace CompatBot.EventHandlers.LogParsing.POCOs
|
||||
public int Id = 0;
|
||||
public ErrorCode Error = ErrorCode.None;
|
||||
public readonly Dictionary<int, (Piracystring filter, string context)> FilterTriggers = new Dictionary<int, (Piracystring filter, string context)>();
|
||||
public Piracystring SelectedFilter;
|
||||
public string SelectedFilterContext;
|
||||
public Piracystring? SelectedFilter;
|
||||
public string? SelectedFilterContext;
|
||||
public long ReadBytes;
|
||||
public long TotalBytes;
|
||||
public int LinesAfterConfig;
|
||||
|
||||
@@ -13,6 +13,6 @@ namespace CompatBot.EventHandlers.LogParsing.SourceHandlers
|
||||
protected const int SnoopBufferSize = 4096;
|
||||
internal static readonly ArrayPool<byte> bufferPool = ArrayPool<byte>.Create(SnoopBufferSize, 64);
|
||||
|
||||
public abstract Task<(ISource source, string failReason)> FindHandlerAsync(DiscordMessage message, ICollection<IArchiveHandler> handlers);
|
||||
public abstract Task<(ISource? source, string? failReason)> FindHandlerAsync(DiscordMessage message, ICollection<IArchiveHandler> handlers);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<IArchiveHandler> handlers)
|
||||
public override async Task<(ISource? source, string? failReason)> FindHandlerAsync(DiscordMessage message, ICollection<IArchiveHandler> 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(@"(?<dropbox_link>(https?://)?(www\.)?dropbox\.com/s/(?<dropbox_id>[^/\s]+)/(?<filename>[^/\?\s])(/dl=[01])?)", DefaultOptions);
|
||||
|
||||
public override async Task<(ISource source, string failReason)> FindHandlerAsync(DiscordMessage message, ICollection<IArchiveHandler> handlers)
|
||||
public override async Task<(ISource? source, string? failReason)> FindHandlerAsync(DiscordMessage message, ICollection<IArchiveHandler> 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace CompatBot.EventHandlers.LogParsing.SourceHandlers
|
||||
{
|
||||
private static readonly Regex ExternalLink = new Regex(@"(?<link>(https?://)?(github\.com/RPCS3/rpcs3|cdn\.discordapp\.com/attachments)/.*/(?<filename>[^/\?\s]+\.(gz|zip|rar|7z|log)))", DefaultOptions);
|
||||
|
||||
public override async Task<(ISource source, string failReason)> FindHandlerAsync(DiscordMessage message, ICollection<IArchiveHandler> handlers)
|
||||
public override async Task<(ISource? source, string? failReason)> FindHandlerAsync(DiscordMessage message, ICollection<IArchiveHandler> 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<IArchiveHandler> handlers)
|
||||
public override async Task<(ISource? source, string? failReason)> FindHandlerAsync(DiscordMessage message, ICollection<IArchiveHandler> 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)
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace CompatBot.EventHandlers.LogParsing.SourceHandlers
|
||||
{
|
||||
public interface ISourceHandler
|
||||
{
|
||||
Task<(ISource source, string failReason)> FindHandlerAsync(DiscordMessage message, ICollection<IArchiveHandler> handlers);
|
||||
Task<(ISource? source, string? failReason)> FindHandlerAsync(DiscordMessage message, ICollection<IArchiveHandler> handlers);
|
||||
}
|
||||
|
||||
public interface ISource
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace CompatBot.EventHandlers.LogParsing.SourceHandlers
|
||||
private static readonly Regex ExternalLink = new Regex(@"(?<mega_link>(https?://)?mega(\.co)?\.nz/(#(?<mega_id>[^/>\s]+)|file/(?<new_mega_id>[^/>\s]+)))", DefaultOptions);
|
||||
private static readonly IProgress<double> doodad = new Progress<double>(_ => { });
|
||||
|
||||
public override async Task<(ISource source, string failReason)> FindHandlerAsync(DiscordMessage message, ICollection<IArchiveHandler> handlers)
|
||||
public override async Task<(ISource? source, string? failReason)> FindHandlerAsync(DiscordMessage message, ICollection<IArchiveHandler> 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace CompatBot.EventHandlers.LogParsing.SourceHandlers
|
||||
private static readonly Regex ExternalLink = new Regex(@"(?<onedrive_link>(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<IArchiveHandler> handlers)
|
||||
public async override Task<(ISource? source, string? failReason)> FindHandlerAsync(DiscordMessage message, ICollection<IArchiveHandler> 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace CompatBot.EventHandlers.LogParsing.SourceHandlers
|
||||
{
|
||||
private static readonly Regex ExternalLink = new Regex(@"(?<pastebin_link>(https?://)pastebin.com/(raw/)?(?<pastebin_id>[^/>\s]+))", DefaultOptions);
|
||||
|
||||
public override async Task<(ISource source, string failReason)> FindHandlerAsync(DiscordMessage message, ICollection<IArchiveHandler> handlers)
|
||||
public override async Task<(ISource? source, string? failReason)> FindHandlerAsync(DiscordMessage message, ICollection<IArchiveHandler> 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<LogParseState> ParseLogAsync(ISource source, Func<Task> onProgressAsync, CancellationToken cancellationToken)
|
||||
public static async Task<LogParseState?> ParseLogAsync(ISource source, Func<Task> onProgressAsync, CancellationToken cancellationToken)
|
||||
{
|
||||
LogParseState result = null;
|
||||
LogParseState? result = null;
|
||||
try
|
||||
{
|
||||
try
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<Explanation> 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];
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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<UserSecretsIdAttribute>() != null)
|
||||
if (Assembly.GetEntryAssembly()?.GetCustomAttribute<UserSecretsIdAttribute>() != 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;
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -134,13 +134,13 @@ namespace CompatBot.Utils
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task<DiscordMessage> ReportAsync(this DiscordClient client, string infraction, DiscordMessage message, IEnumerable<DiscordMember> reporters, string? comment, ReportSeverity severity)
|
||||
public static async Task<DiscordMessage> ReportAsync(this DiscordClient client, string infraction, DiscordMessage message, IEnumerable<DiscordMember?> 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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<string> lines) BuildCpuSection(NameValueCollection items)
|
||||
private static (string? name, List<string>? 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<string> lines) BuildGpuSection(NameValueCollection items)
|
||||
private static (string? name, List<string>? 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<string> lines) colA, (string name, List<string> lines) colB)
|
||||
private static void BuildSettingsSections(DiscordEmbedBuilder builder, NameValueCollection items, (string? name, List<string>? lines) colA, (string? name, List<string>? 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<string>(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<string>(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);
|
||||
|
||||
@@ -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<string>();
|
||||
var (_, brokenDump, longestPath) = await HasBrokenFilesAsync(state).ConfigureAwait(false);
|
||||
@@ -35,8 +35,8 @@ namespace CompatBot.Utils.ResultFormatters
|
||||
if (multiItems["fatal_error"] is UniqueList<string> 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<string> raps && raps.Any())
|
||||
{
|
||||
var limitTo = 5;
|
||||
var licenseNames = raps
|
||||
List<string> 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<char>();
|
||||
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<string>(knownFiles.Select(f => Path.GetDirectoryName(f).Replace('\\', '/')),
|
||||
var knownDirs = new HashSet<string>(
|
||||
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)
|
||||
|
||||
@@ -15,11 +15,11 @@ namespace CompatBot.Utils.ResultFormatters
|
||||
{
|
||||
private static void BuildWeirdSettingsSection(DiscordEmbedBuilder builder, LogParseState state, List<string> generalNotes)
|
||||
{
|
||||
var items = state.CompleteCollection;
|
||||
var items = state.CompletedCollection;
|
||||
var multiItems = state.CompleteMultiValueCollection;
|
||||
var notes = new List<string>();
|
||||
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<string> notes, Dictionary<string, int> ppuPatches, HashSet<string> ppuHashes, List<string> 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<string> notes, Dictionary<string, int> ppuPatches, UniqueList<string> patchNames)
|
||||
private static void CheckKillzone3Settings(string serial, NameValueCollection items, List<string> notes, UniqueList<string> 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<string> notes, List<string> generalNotes)
|
||||
private static void CheckLbpSettings(string serial, NameValueCollection items, List<string> generalNotes)
|
||||
{
|
||||
if (!AllLbpGames.Contains(serial))
|
||||
return;
|
||||
|
||||
@@ -231,10 +231,10 @@ namespace CompatBot.Utils.ResultFormatters
|
||||
public static async Task<DiscordEmbedBuilder> 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;
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace CompatBot.Utils.ResultFormatters
|
||||
internal static class TitlePatchFormatter
|
||||
{
|
||||
// thanks BCES00569
|
||||
public static async Task<List<DiscordEmbedBuilder>> AsEmbedAsync(this TitlePatch patch, DiscordClient client, string productCode)
|
||||
public static async Task<List<DiscordEmbedBuilder>> AsEmbedAsync(this TitlePatch? patch, DiscordClient client, string productCode)
|
||||
{
|
||||
var result = new List<DiscordEmbedBuilder>();
|
||||
var pkgs = patch?.Tag?.Packages;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=ASMJIT/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=blit/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Confusables/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=framerate/@EntryIndexedValue">True</s:Boolean>
|
||||
@@ -6,11 +7,15 @@
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=liblv/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=lwmutex/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Metacritic/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=MLAA/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=mtrsx/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Multithreaded/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Nier/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=recompiler/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Ryzen/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=shaders/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=sprx/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Syscalls/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=vblank/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=vsync/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Vulkan/@EntryIndexedValue">True</s:Boolean>
|
||||
|
||||
Reference in New Issue
Block a user