mirror of
https://github.com/RPCS3/discord-bot.git
synced 2026-01-31 01:25:22 +01:00
use read/write locking for sqlite to fix Error 5: 'database is locked'
This commit is contained in:
@@ -18,7 +18,7 @@ public class BotConfigurationAutoCompleteProvider: IAutoCompleteProvider
|
||||
if (!ModProvider.IsSudoer(context.User.Id))
|
||||
return [new($"{Config.Reactions.Denied} You are not authorized to use this command.", -1)];
|
||||
|
||||
await using var db = new BotDb();
|
||||
await using var db = BotDb.OpenRead();
|
||||
IEnumerable<string> result;
|
||||
var input = context.UserInput;
|
||||
if (input is not { Length: > 0 })
|
||||
|
||||
@@ -11,7 +11,7 @@ public class ContentFilterAutoCompleteProvider: IAutoCompleteProvider
|
||||
if (!ModProvider.IsMod(context.User.Id))
|
||||
return [new($"{Config.Reactions.Denied} You are not authorized to use this command.", -1)];
|
||||
|
||||
await using var db = new BotDb();
|
||||
await using var db = BotDb.OpenRead();
|
||||
IEnumerable<(int id, string trigger)> result;
|
||||
if (context.UserInput is not {Length: >0} prefix)
|
||||
result = db.Piracystring
|
||||
|
||||
@@ -7,7 +7,7 @@ public class EventIdAutoCompleteProvider: IAutoCompleteProvider
|
||||
{
|
||||
public async ValueTask<IEnumerable<DiscordAutoCompleteChoice>> AutoCompleteAsync(AutoCompleteContext context)
|
||||
{
|
||||
await using var db = new BotDb();
|
||||
await using var db = BotDb.OpenRead();
|
||||
IEnumerable<EventSchedule> query;
|
||||
if (context.UserInput is not { Length: > 0 } input)
|
||||
{
|
||||
|
||||
@@ -7,7 +7,7 @@ public class EventNameAutoCompleteProvider: IAutoCompleteProvider
|
||||
{
|
||||
public async ValueTask<IEnumerable<DiscordAutoCompleteChoice>> AutoCompleteAsync(AutoCompleteContext context)
|
||||
{
|
||||
await using var db = new BotDb();
|
||||
await using var db = BotDb.OpenRead();
|
||||
#if DEBUG
|
||||
var currentTime = DateTime.UtcNow.AddYears(-10);
|
||||
#else
|
||||
|
||||
@@ -9,7 +9,7 @@ public class ExplainAutoCompleteProvider: IAutoCompleteProvider
|
||||
{
|
||||
public async ValueTask<IEnumerable<DiscordAutoCompleteChoice>> AutoCompleteAsync(AutoCompleteContext context)
|
||||
{
|
||||
await using var db = new BotDb();
|
||||
await using var db = BotDb.OpenRead();
|
||||
IEnumerable<string> result;
|
||||
if (context.UserInput is not { Length: > 0 } prefix)
|
||||
{
|
||||
|
||||
@@ -11,7 +11,7 @@ public class InviteAutoCompleteProvider: IAutoCompleteProvider
|
||||
if (!ModProvider.IsMod(context.User.Id))
|
||||
return [new($"{Config.Reactions.Denied} You are not authorized to use this command.", -1)];
|
||||
|
||||
await using var db = new BotDb();
|
||||
await using var db = BotDb.OpenRead();
|
||||
IQueryable<WhitelistedInvite> result;
|
||||
if (context.UserInput is not { Length: > 0 } input)
|
||||
result = db.WhitelistedInvites
|
||||
|
||||
@@ -8,7 +8,7 @@ public class ProductCodeAutoCompleteProvider: IAutoCompleteProvider
|
||||
{
|
||||
public async ValueTask<IEnumerable<DiscordAutoCompleteChoice>> AutoCompleteAsync(AutoCompleteContext context)
|
||||
{
|
||||
await using var db = new ThumbnailDb();
|
||||
await using var db = ThumbnailDb.OpenRead();
|
||||
IEnumerable<(string code, string title)> result;
|
||||
if (context.UserInput is not { Length: > 0 } prefix)
|
||||
{
|
||||
|
||||
@@ -20,7 +20,7 @@ public class WarningAutoCompleteProvider: IAutoCompleteProvider
|
||||
? w => w.Retracted && w.DiscordId == userId
|
||||
: w => !w.Retracted && w.DiscordId == userId;
|
||||
|
||||
await using var db = new BotDb();
|
||||
await using var db = BotDb.OpenRead();
|
||||
List<Warning> result;
|
||||
if (context.UserInput is not { Length: > 0 } prefix)
|
||||
result = await db.Warning
|
||||
|
||||
@@ -15,7 +15,7 @@ internal static partial class Bot
|
||||
[Description("List set variable names")]
|
||||
public static async ValueTask List(SlashCommandContext ctx)
|
||||
{
|
||||
await using var db = new BotDb();
|
||||
await using var db = BotDb.OpenRead();
|
||||
var setVars = await db.BotState
|
||||
.AsNoTracking()
|
||||
.Where(v => v.Key.StartsWith(SqlConfiguration.ConfigVarPrefix))
|
||||
@@ -49,7 +49,7 @@ internal static partial class Bot
|
||||
Config.InMemorySettings[key] = value;
|
||||
Config.RebuildConfiguration();
|
||||
key = SqlConfiguration.ConfigVarPrefix + key;
|
||||
await using var db = new BotDb();
|
||||
await using var db = BotDb.OpenRead();
|
||||
var stateValue = await db.BotState.FirstOrDefaultAsync(v => v.Key == key).ConfigureAwait(false);
|
||||
if (stateValue == null)
|
||||
{
|
||||
@@ -72,7 +72,7 @@ internal static partial class Bot
|
||||
Config.InMemorySettings.TryRemove(key, out _);
|
||||
Config.RebuildConfiguration();
|
||||
key = SqlConfiguration.ConfigVarPrefix + key;
|
||||
await using var db = new BotDb();
|
||||
await using var db = BotDb.OpenRead();
|
||||
var stateValue = await db.BotState.Where(v => v.Key == key).FirstOrDefaultAsync().ConfigureAwait(false);
|
||||
if (stateValue is not null)
|
||||
{
|
||||
|
||||
@@ -17,7 +17,7 @@ internal static partial class Bot
|
||||
try
|
||||
{
|
||||
await CompatList.ImportMetacriticScoresAsync().ConfigureAwait(false);
|
||||
await using var db = new ThumbnailDb();
|
||||
await using var db = ThumbnailDb.OpenRead();
|
||||
var linkedItems = await db.Thumbnail.CountAsync(i => i.MetacriticId != null).ConfigureAwait(false);
|
||||
await ctx.Channel.SendMessageAsync($"Importing Metacritic info was successful, linked {linkedItems} items").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
@@ -89,7 +89,7 @@ internal static partial class Bot
|
||||
await using var thumbStream = Config.MemoryStreamManager.GetStream();
|
||||
if (name != BackupDbType.Hardware)
|
||||
{
|
||||
await using var db = new ThumbnailDb();
|
||||
await using var db = ThumbnailDb.OpenRead();
|
||||
if (await BackupDbAsync(db, thumbStream, maxSize).ConfigureAwait(false) is { Length: > 0 } error)
|
||||
msg += error + '\n';
|
||||
else
|
||||
@@ -98,7 +98,7 @@ internal static partial class Bot
|
||||
await using var hwStream = Config.MemoryStreamManager.GetStream();
|
||||
if (name != BackupDbType.Thumbs)
|
||||
{
|
||||
await using var db = new HardwareDb();
|
||||
await using var db = HardwareDb.OpenRead();
|
||||
if (await BackupDbAsync(db, hwStream, maxSize).ConfigureAwait(false) is { Length: > 0 } error)
|
||||
msg += error + '\n';
|
||||
else
|
||||
@@ -146,7 +146,7 @@ internal static partial class Bot
|
||||
{
|
||||
try
|
||||
{
|
||||
await using var db = new BotDb();
|
||||
await using var db = BotDb.OpenRead();
|
||||
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 (message is {Length: >0})
|
||||
@@ -187,7 +187,7 @@ internal static partial class Bot
|
||||
string? dbName = null;
|
||||
try
|
||||
{
|
||||
await using var botDb = new BotDb();
|
||||
await using var botDb = BotDb.OpenRead();
|
||||
string dbPath, dbDir;
|
||||
await using (var connection = db.Database.GetDbConnection())
|
||||
{
|
||||
@@ -305,7 +305,7 @@ internal static partial class Bot
|
||||
internal static void Restart(ulong channelId, string? restartMsg)
|
||||
{
|
||||
Config.Log.Info($"Saving channelId {channelId} into settings…");
|
||||
using var db = new BotDb();
|
||||
using var db = BotDb.OpenRead();
|
||||
var ch = db.BotState.FirstOrDefault(k => k.Key == "bot-restart-channel");
|
||||
if (ch is null)
|
||||
{
|
||||
|
||||
@@ -82,7 +82,7 @@ internal static class BotStatus
|
||||
{
|
||||
try
|
||||
{
|
||||
using var db = new BotDb();
|
||||
using var db = BotDb.OpenRead();
|
||||
var timestamps = db.Warning
|
||||
.Where(w => w.Timestamp.HasValue && !w.Retracted)
|
||||
.OrderBy(w => w.Timestamp)
|
||||
@@ -220,7 +220,7 @@ internal static class BotStatus
|
||||
{
|
||||
try
|
||||
{
|
||||
using var db = new ThumbnailDb();
|
||||
using var db = ThumbnailDb.OpenRead();
|
||||
var syscallCount = db.SyscallInfo.AsNoTracking().Where(sci => sci.Function.StartsWith("sys_") || sci.Function.StartsWith("_sys_")).Distinct().Count();
|
||||
var totalFuncCount = db.SyscallInfo.AsNoTracking().Select(sci => sci.Function).Distinct().Count();
|
||||
var fwCallCount = totalFuncCount - syscallCount;
|
||||
@@ -241,7 +241,7 @@ internal static class BotStatus
|
||||
{
|
||||
try
|
||||
{
|
||||
using var db = new HardwareDb();
|
||||
using var db = HardwareDb.OpenRead();
|
||||
var monthAgo = DateTime.UtcNow.AddDays(-30).Ticks;
|
||||
var monthCount = db.HwInfo.Count(i => i.Timestamp > monthAgo);
|
||||
if (monthCount == 0)
|
||||
@@ -272,7 +272,7 @@ internal static class BotStatus
|
||||
{
|
||||
try
|
||||
{
|
||||
using var db = new BotDb();
|
||||
using var db = BotDb.OpenRead();
|
||||
var kots = db.Kot.Count();
|
||||
var doggos = db.Doggo.Count();
|
||||
if (kots == 0 && doggos == 0)
|
||||
|
||||
@@ -155,7 +155,7 @@ internal static partial class CompatList
|
||||
await compatChannel.SendMessageAsync(embed: embed.Build()).ConfigureAwait(false);
|
||||
lastUpdateInfo = latestUpdatePr;
|
||||
lastFullBuildNumber = latestUpdateBuild;
|
||||
await using var db = new BotDb();
|
||||
await using var db = BotDb.OpenRead();
|
||||
var currentState = await db.BotState.FirstOrDefaultAsync(k => k.Key == Rpcs3UpdateStateKey).ConfigureAwait(false);
|
||||
if (currentState == null)
|
||||
await db.BotState.AddAsync(new() {Key = Rpcs3UpdateStateKey, Value = latestUpdatePr}).ConfigureAwait(false);
|
||||
|
||||
@@ -28,7 +28,7 @@ internal static partial class CompatList
|
||||
if (!Enum.TryParse(status, true, out CompatStatus s))
|
||||
s = CompatStatus.Playable;
|
||||
|
||||
await using var db = new ThumbnailDb();
|
||||
await using var db = ThumbnailDb.OpenRead();
|
||||
var queryBase = db.Thumbnail.AsNoTracking();
|
||||
if (exactStatus)
|
||||
queryBase = queryBase.Where(g => g.CompatibilityStatus == s);
|
||||
|
||||
@@ -33,7 +33,7 @@ internal static partial class CompatList
|
||||
|
||||
static CompatList()
|
||||
{
|
||||
using var db = new BotDb();
|
||||
using var db = BotDb.OpenRead();
|
||||
lastUpdateInfo = db.BotState.FirstOrDefault(k => k.Key == Rpcs3UpdateStateKey)?.Value;
|
||||
lastFullBuildNumber = db.BotState.FirstOrDefault(k => k.Key == Rpcs3UpdateBuildKey)?.Value;
|
||||
//lastUpdateInfo = "8022";
|
||||
@@ -133,7 +133,7 @@ internal static partial class CompatList
|
||||
{
|
||||
var timer = Stopwatch.StartNew();
|
||||
var title = requestBuilder.Search;
|
||||
using var db = new ThumbnailDb();
|
||||
using var db = ThumbnailDb.OpenRead();
|
||||
var matches = db.Thumbnail
|
||||
.AsNoTracking()
|
||||
.AsEnumerable()
|
||||
@@ -227,7 +227,7 @@ internal static partial class CompatList
|
||||
if (list is null)
|
||||
return;
|
||||
|
||||
await using var db = new ThumbnailDb();
|
||||
await using var db = ThumbnailDb.OpenRead();
|
||||
foreach (var kvp in list.Results)
|
||||
{
|
||||
var (productCode, info) = kvp;
|
||||
@@ -306,7 +306,7 @@ internal static partial class CompatList
|
||||
.Select(i => i.WithTitle(i.Title.Replace("HAWX", "H.A.W.X")))
|
||||
);
|
||||
|
||||
await using var db = new ThumbnailDb();
|
||||
await using var db = ThumbnailDb.OpenRead();
|
||||
foreach (var mcScore in scoreList.Where(s => s.CriticScore > 0 || s.UserScore > 0))
|
||||
{
|
||||
if (Config.Cts.IsCancellationRequested)
|
||||
|
||||
@@ -37,7 +37,7 @@ internal sealed partial class ContentFilters
|
||||
new AsciiColumn("Actions"),
|
||||
new AsciiColumn("Custom message", maxWidth: 2048)
|
||||
);
|
||||
await using var db = new BotDb();
|
||||
await using var db = BotDb.OpenRead();
|
||||
var duplicates = new Dictionary<string, FilterContext>(StringComparer.InvariantCultureIgnoreCase);
|
||||
var filters = db.Piracystring.Where(ps => !ps.Disabled).AsNoTracking().AsEnumerable().OrderBy(ps => ps.String.ToUpperInvariant()).ToList();
|
||||
var nonUniqueTriggers = (
|
||||
@@ -130,7 +130,7 @@ internal sealed partial class ContentFilters
|
||||
}
|
||||
|
||||
explanation = explanation?.ToLowerInvariant();
|
||||
await using var db = new BotDb();
|
||||
await using var db = BotDb.OpenRead();
|
||||
if (explanation is {Length: >0} && !await db.Explanation.AnyAsync(e => e.Keyword == explanation).ConfigureAwait(false))
|
||||
{
|
||||
await ctx.RespondAsync($"❌ Unknown explanation term: {explanation}", ephemeral: ephemeral).ConfigureAwait(false);
|
||||
@@ -235,7 +235,7 @@ internal sealed partial class ContentFilters
|
||||
return;
|
||||
}
|
||||
|
||||
await using var db = new BotDb();
|
||||
await using var db = BotDb.OpenRead();
|
||||
foreach (var element in xml.Root.Elements("game"))
|
||||
{
|
||||
var name = element.Element("rom")?.Attribute("name")?.Value;
|
||||
@@ -312,7 +312,7 @@ internal sealed partial class ContentFilters
|
||||
}
|
||||
}
|
||||
|
||||
await using var db = new BotDb();
|
||||
await using var db = BotDb.OpenRead();
|
||||
explanation = explanation?.ToLowerInvariant();
|
||||
if (explanation is {Length: >0} && !await db.Explanation.AnyAsync(e => e.Keyword == explanation).ConfigureAwait(false))
|
||||
{
|
||||
@@ -367,7 +367,7 @@ internal sealed partial class ContentFilters
|
||||
)
|
||||
{
|
||||
var ephemeral = !ctx.Channel.IsPrivate;
|
||||
await using var db = new BotDb();
|
||||
await using var db = BotDb.OpenRead();
|
||||
var filter = await db.Piracystring.FirstOrDefaultAsync(ps => ps.Id == id && !ps.Disabled).ConfigureAwait(false);
|
||||
if (filter is null)
|
||||
{
|
||||
@@ -391,7 +391,7 @@ internal sealed partial class ContentFilters
|
||||
var ephemeral = !ctx.Channel.IsPrivate;
|
||||
int removedFilters;
|
||||
var removedTriggers = new StringBuilder();
|
||||
await using (var db = new BotDb())
|
||||
await using (var db = BotDb.OpenRead())
|
||||
{
|
||||
foreach (var f in db.Piracystring.Where(ps => ps.Id == id && !ps.Disabled))
|
||||
{
|
||||
|
||||
@@ -30,7 +30,7 @@ internal static partial class Events
|
||||
var current = DateTime.UtcNow;
|
||||
#endif
|
||||
var currentTicks = current.Ticks;
|
||||
await using var db = new BotDb();
|
||||
await using var db = BotDb.OpenRead();
|
||||
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(name))
|
||||
@@ -203,7 +203,7 @@ internal static partial class Events
|
||||
evt.Name = name;
|
||||
evt.EventName = string.IsNullOrWhiteSpace(@event) || @event == "-" ? null : @event;
|
||||
evt.Year = newTime.Year;
|
||||
await using var db = new BotDb();
|
||||
await using var db = BotDb.OpenRead();
|
||||
await db.EventSchedule.AddAsync(evt).ConfigureAwait(false);
|
||||
await db.SaveChangesAsync().ConfigureAwait(false);
|
||||
await ctx.RespondAsync(embed: FormatEvent(evt).WithTitle("Created new event schedule entry #" + evt.Id), ephemeral: ephemeral).ConfigureAwait(false);
|
||||
@@ -218,7 +218,7 @@ internal static partial class Events
|
||||
)
|
||||
{
|
||||
var ephemeral = !ctx.Channel.IsSpamChannel();
|
||||
await using var db = new BotDb();
|
||||
await using var db = BotDb.OpenRead();
|
||||
var eventsToRemove = await db.EventSchedule.Where(evt => evt.Id == id).ToListAsync().ConfigureAwait(false);
|
||||
db.EventSchedule.RemoveRange(eventsToRemove);
|
||||
var removedCount = await db.SaveChangesAsync().ConfigureAwait(false);
|
||||
@@ -234,7 +234,7 @@ internal static partial class Events
|
||||
public Task ClearGeneric(CommandContext ctx, [Description("Optional year to remove, by default everything before current year")] int? year = null)
|
||||
{
|
||||
var currentYear = DateTime.UtcNow.Year;
|
||||
await using var db = new BotDb();
|
||||
await using var db = BotDb.OpenRead();
|
||||
var itemsToRemove = await db.EventSchedule.Where(e =>
|
||||
year.HasValue
|
||||
? e.Year == year
|
||||
@@ -262,7 +262,7 @@ internal static partial class Events
|
||||
)
|
||||
{
|
||||
var ephemeral = !ctx.Channel.IsSpamChannel();
|
||||
await using var db = new BotDb();
|
||||
await using var db = BotDb.OpenRead();
|
||||
var evt = db.EventSchedule.FirstOrDefault(e => e.Id == id);
|
||||
if (evt is null)
|
||||
{
|
||||
@@ -312,7 +312,7 @@ internal static partial class Events
|
||||
var ephemeral = !ctx.Channel.IsSpamChannel() || ModProvider.IsMod(ctx.User.Id);
|
||||
var showAll = "all".Equals(@event, StringComparison.InvariantCultureIgnoreCase);
|
||||
var currentTicks = DateTime.UtcNow.Ticks;
|
||||
await using var db = new BotDb();
|
||||
await using var db = BotDb.OpenRead();
|
||||
IQueryable<EventSchedule> query = db.EventSchedule;
|
||||
if (year.HasValue)
|
||||
query = query.Where(e => e.Year == year);
|
||||
|
||||
@@ -149,7 +149,7 @@ internal static class Explain
|
||||
|
||||
var response = new DiscordInteractionResponseBuilder().AsEphemeral();
|
||||
await interaction.CreateResponseAsync(DiscordInteractionResponseType.DeferredChannelMessageWithSource, response).ConfigureAwait(false);
|
||||
await using var db = new BotDb();
|
||||
await using var db = BotDb.OpenRead();
|
||||
if (await db.Explanation.AnyAsync(e => e.Keyword == term).ConfigureAwait(false))
|
||||
{
|
||||
await interaction.EditOriginalResponseAsync(
|
||||
@@ -224,7 +224,7 @@ internal static class Explain
|
||||
}
|
||||
}
|
||||
|
||||
await using var db = new BotDb();
|
||||
await using var db = BotDb.OpenRead();
|
||||
var item = await db.Explanation.FirstOrDefaultAsync(e => e.Keyword == term).ConfigureAwait(false);
|
||||
if (item is null)
|
||||
{
|
||||
@@ -298,7 +298,7 @@ internal static class Explain
|
||||
)
|
||||
{
|
||||
term = term.ToLowerInvariant().StripQuotes();
|
||||
await using var db = new BotDb();
|
||||
await using var db = BotDb.OpenRead();
|
||||
var item = await db.Explanation.FirstOrDefaultAsync(e => e.Keyword == term).ConfigureAwait(false);
|
||||
if (item is null)
|
||||
{
|
||||
@@ -335,7 +335,7 @@ internal static class Explain
|
||||
{
|
||||
var ephemeral = !ctx.Channel.IsSpamChannel();
|
||||
await ctx.DeferResponseAsync(ephemeral).ConfigureAwait(false);
|
||||
await using var db = new BotDb();
|
||||
await using var db = BotDb.OpenRead();
|
||||
var allTerms = await db.Explanation.AsNoTracking().Select(e => e.Keyword).ToListAsync();
|
||||
await using var stream = Config.MemoryStreamManager.GetStream();
|
||||
await using var writer = new StreamWriter(stream);
|
||||
@@ -357,7 +357,7 @@ internal static class Explain
|
||||
string term
|
||||
)
|
||||
{
|
||||
await using var db = new BotDb();
|
||||
await using var db = BotDb.OpenRead();
|
||||
var item = await db.Explanation.FirstOrDefaultAsync(e => e.Keyword == term).ConfigureAwait(false);
|
||||
if (item is null)
|
||||
{
|
||||
@@ -435,7 +435,7 @@ internal static class Explain
|
||||
|
||||
internal static async ValueTask<(Explanation? explanation, string? fuzzyMatch, double score)> LookupTerm(string term)
|
||||
{
|
||||
await using var db = new BotDb();
|
||||
await using var db = BotDb.OpenRead();
|
||||
string? fuzzyMatch = null;
|
||||
double coefficient;
|
||||
var explanation = await db.Explanation.FirstOrDefaultAsync(e => e.Keyword == term).ConfigureAwait(false);
|
||||
|
||||
@@ -72,7 +72,7 @@ internal static class ForcedNicknames
|
||||
guilds = [ctx.Guild];
|
||||
|
||||
int changed = 0, noPermissions = 0, failed = 0;
|
||||
await using var db = new BotDb();
|
||||
await using var db = BotDb.OpenRead();
|
||||
foreach (var guild in guilds)
|
||||
{
|
||||
if (!discordUser.IsBotSafeCheck())
|
||||
@@ -157,7 +157,7 @@ internal static class ForcedNicknames
|
||||
return;
|
||||
}
|
||||
|
||||
await using var db = new BotDb();
|
||||
await using var db = BotDb.OpenRead();
|
||||
var enforcedRules = ctx.Guild is null
|
||||
? await db.ForcedNicknames.Where(mem => mem.UserId == discordUser.Id).ToListAsync().ConfigureAwait(false)
|
||||
: await db.ForcedNicknames.Where(mem => mem.UserId == discordUser.Id && mem.GuildId == ctx.Guild.Id).ToListAsync().ConfigureAwait(false);
|
||||
@@ -256,7 +256,7 @@ internal static class ForcedNicknames
|
||||
[Description("Lists all users who has restricted nickname.")]
|
||||
public async Task List(CommandContext ctx)
|
||||
{
|
||||
await using var db = new BotDb();
|
||||
await using var db = BotDb.OpenRead();
|
||||
var selectExpr = db.ForcedNicknames.AsNoTracking();
|
||||
if (ctx.Guild != null)
|
||||
selectExpr = selectExpr.Where(mem => mem.GuildId == ctx.Guild.Id);
|
||||
|
||||
@@ -50,7 +50,7 @@ internal static class Fortune
|
||||
|
||||
await ctx.RespondAsync("Importing…", ephemeral: true).ConfigureAwait(false);
|
||||
var stopwatch = Stopwatch.StartNew();
|
||||
await using var db = new ThumbnailDb();
|
||||
await using var db = ThumbnailDb.OpenRead();
|
||||
using var httpClient = HttpClientFactory.Create(new CompressionMessageHandler());
|
||||
using var request = new HttpRequestMessage(HttpMethod.Get, url);
|
||||
var response = await httpClient.SendAsync(request, cts.Token).ConfigureAwait(false);
|
||||
@@ -149,7 +149,7 @@ internal static class Fortune
|
||||
var count = 0;
|
||||
await using var outputStream = Config.MemoryStreamManager.GetStream();
|
||||
await using var writer = new StreamWriter(outputStream);
|
||||
await using var db = new ThumbnailDb();
|
||||
await using var db = ThumbnailDb.OpenRead();
|
||||
foreach (var fortune in db.Fortune.AsNoTracking())
|
||||
{
|
||||
if (Config.Cts.Token.IsCancellationRequested)
|
||||
@@ -184,7 +184,7 @@ internal static class Fortune
|
||||
}
|
||||
|
||||
await ctx.DeferResponseAsync(true).ConfigureAwait(false);
|
||||
await using var db = new ThumbnailDb();
|
||||
await using var db = ThumbnailDb.OpenRead();
|
||||
db.Fortune.RemoveRange(db.Fortune);
|
||||
var count = await db.SaveChangesAsync(Config.Cts.Token).ConfigureAwait(false);
|
||||
await ctx.RespondAsync($"{Config.Reactions.Success} Removed {count} fortune{(count == 1 ? "" : "s")}", ephemeral: true).ConfigureAwait(false);
|
||||
@@ -194,7 +194,7 @@ internal static class Fortune
|
||||
{
|
||||
var prefix = DateTime.UtcNow.ToString("yyyyMMdd")+ user.Id.ToString("x16");
|
||||
var rng = new Random(prefix.GetStableHash());
|
||||
await using var db = new ThumbnailDb();
|
||||
await using var db = ThumbnailDb.OpenRead();
|
||||
Database.Fortune? fortune;
|
||||
do
|
||||
{
|
||||
|
||||
@@ -17,7 +17,7 @@ internal static class Hardware
|
||||
var maxDays = DateTime.UtcNow - new DateTime(2011, 5, 23, 0, 0, 0, DateTimeKind.Utc);
|
||||
period = Math.Clamp(Math.Abs(period), 1, (int)maxDays.TotalDays);
|
||||
var ts = DateTime.UtcNow.AddDays(-period).Ticks;
|
||||
await using var db = new HardwareDb();
|
||||
await using var db = HardwareDb.OpenRead();
|
||||
var count = await db.HwInfo.AsNoTracking().CountAsync(i => i.Timestamp > ts).ConfigureAwait(false);
|
||||
if (count is 0)
|
||||
{
|
||||
|
||||
@@ -16,7 +16,7 @@ internal static class Invites
|
||||
public static async ValueTask List(SlashCommandContext ctx)
|
||||
{
|
||||
const string linkPrefix = "discord.gg/";
|
||||
await using var db = new BotDb();
|
||||
await using var db = BotDb.OpenRead();
|
||||
var whitelistedInvites = await db.WhitelistedInvites.ToListAsync().ConfigureAwait(false);
|
||||
if (whitelistedInvites.Count is 0)
|
||||
{
|
||||
@@ -144,7 +144,7 @@ internal static class Invites
|
||||
[Description("Custom server name"), MinMaxLength(3)] string name
|
||||
)
|
||||
{
|
||||
await using var db = new BotDb();
|
||||
await using var db = BotDb.OpenRead();
|
||||
var invite = await db.WhitelistedInvites.FirstOrDefaultAsync(i => i.Id == id).ConfigureAwait(false);
|
||||
if (invite is null)
|
||||
{
|
||||
|
||||
@@ -242,7 +242,7 @@ internal static partial class Misc
|
||||
public static async ValueTask Game(SlashCommandContext ctx)
|
||||
{
|
||||
var ephemeral = !ctx.Channel.IsSpamChannel();
|
||||
var db = new ThumbnailDb();
|
||||
var db = ThumbnailDb.OpenRead();
|
||||
await using var _ = db.ConfigureAwait(false);
|
||||
var count = await db.Thumbnail.CountAsync().ConfigureAwait(false);
|
||||
if (count is 0)
|
||||
|
||||
@@ -80,7 +80,7 @@ internal static partial class Psn
|
||||
return;
|
||||
|
||||
var newVersion = fwList[0].Version;
|
||||
await using var db = new BotDb();
|
||||
await using var db = BotDb.OpenRead();
|
||||
var fwVersionState = db.BotState.FirstOrDefault(s => s.Key == "Latest-Firmware-Version");
|
||||
latestFwVersion ??= fwVersionState?.Value;
|
||||
if (latestFwVersion is null
|
||||
|
||||
@@ -25,7 +25,7 @@ internal static partial class Psn
|
||||
{
|
||||
var ephemeral = !ctx.Channel.IsSpamChannel();
|
||||
productCode = productCode.ToUpperInvariant();
|
||||
await using var db = new ThumbnailDb();
|
||||
await using var db = ThumbnailDb.OpenRead();
|
||||
var item = db.Thumbnail.AsNoTracking().FirstOrDefault(t => t.ProductCode == productCode);
|
||||
if (item is null)
|
||||
await ctx.RespondAsync($"{Config.Reactions.Failure} Unknown product code {productCode}", ephemeral: true).ConfigureAwait(false);
|
||||
@@ -67,7 +67,7 @@ internal static partial class Psn
|
||||
return;
|
||||
}
|
||||
|
||||
await using var db = new ThumbnailDb();
|
||||
await using var db = ThumbnailDb.OpenRead();
|
||||
var item = db.Thumbnail.AsNoTracking().FirstOrDefault(t => t.ProductCode == productCode);
|
||||
if (item is null)
|
||||
{
|
||||
|
||||
@@ -25,7 +25,7 @@ internal static partial class Sudo
|
||||
try
|
||||
{
|
||||
var @fixed = 0;
|
||||
await using var db = new BotDb();
|
||||
await using var db = BotDb.OpenRead();
|
||||
foreach (var warning in db.Warning)
|
||||
if (!string.IsNullOrEmpty(warning.FullReason))
|
||||
{
|
||||
@@ -54,7 +54,7 @@ internal static partial class Sudo
|
||||
try
|
||||
{
|
||||
var @fixed = 0;
|
||||
await using var db = new BotDb();
|
||||
await using var db = BotDb.OpenRead();
|
||||
foreach (var warning in db.Warning)
|
||||
{
|
||||
var newReason = await FixChannelMentionAsync(ctx, warning.Reason).ConfigureAwait(false);
|
||||
@@ -106,7 +106,7 @@ internal static partial class Sudo
|
||||
public static async ValueTask TitleMarks(TextCommandContext ctx)
|
||||
{
|
||||
var changed = 0;
|
||||
await using var db = new ThumbnailDb();
|
||||
await using var db = ThumbnailDb.OpenRead();
|
||||
foreach (var thumb in db.Thumbnail)
|
||||
{
|
||||
if (string.IsNullOrEmpty(thumb.Name))
|
||||
@@ -135,7 +135,7 @@ internal static partial class Sudo
|
||||
public static async ValueTask MetacriticLinks(TextCommandContext ctx, [Description("Remove links for trial and demo versions only")] bool demosOnly = true)
|
||||
{
|
||||
var changed = 0;
|
||||
await using var db = new ThumbnailDb();
|
||||
await using var db = ThumbnailDb.OpenRead();
|
||||
foreach (var thumb in db.Thumbnail.Where(t => t.MetacriticId != null))
|
||||
{
|
||||
if (demosOnly
|
||||
|
||||
@@ -26,7 +26,7 @@ internal static class Syscall
|
||||
return;
|
||||
}
|
||||
|
||||
await using var db = new ThumbnailDb();
|
||||
await using var db = ThumbnailDb.OpenRead();
|
||||
if (db.SyscallInfo.Any(sci => sci.Function == search))
|
||||
{
|
||||
var productInfoList = db.SyscallToProductMap
|
||||
@@ -119,7 +119,7 @@ internal static class Syscall
|
||||
string newFunctionName
|
||||
)
|
||||
{
|
||||
await using var db = new ThumbnailDb();
|
||||
await using var db = ThumbnailDb.OpenRead();
|
||||
var oldMatches = await db.SyscallInfo.Where(sci => sci.Function == oldFunctionName).ToListAsync().ConfigureAwait(false);
|
||||
if (oldMatches.Count is 0)
|
||||
{
|
||||
@@ -148,7 +148,7 @@ internal static class Syscall
|
||||
private static async ValueTask ReturnSyscallsByGameAsync(SlashCommandContext ctx, string productId, bool ephemeral)
|
||||
{
|
||||
productId = productId.ToUpperInvariant();
|
||||
await using var db = new ThumbnailDb();
|
||||
await using var db = ThumbnailDb.OpenRead();
|
||||
var title = db.Thumbnail.FirstOrDefault(t => t.ProductCode == productId)?.Name;
|
||||
title = string.IsNullOrEmpty(title) ? productId : $"[{productId}] {title.Trim(40)}";
|
||||
var sysInfoList = db.SyscallToProductMap.AsNoTracking()
|
||||
|
||||
@@ -40,7 +40,7 @@ internal static partial class Warnings
|
||||
new AsciiColumn("Count", alignToRight: true),
|
||||
new AsciiColumn("All time", alignToRight: true)
|
||||
);
|
||||
await using var db = new BotDb();
|
||||
await using var db = BotDb.OpenRead();
|
||||
var query = from warn in db.Warning.AsEnumerable()
|
||||
group warn by warn.DiscordId
|
||||
into userGroup
|
||||
@@ -84,7 +84,7 @@ internal static partial class Warnings
|
||||
new AsciiColumn("Warnings given", alignToRight: true),
|
||||
new AsciiColumn("Including retracted", alignToRight: true)
|
||||
);
|
||||
await using var db = new BotDb();
|
||||
await using var db = BotDb.OpenRead();
|
||||
var query = from warn in db.Warning.AsEnumerable()
|
||||
group warn by warn.IssuerId
|
||||
into modGroup
|
||||
@@ -129,7 +129,7 @@ internal static partial class Warnings
|
||||
new AsciiColumn("Reason"),
|
||||
new AsciiColumn("Context", disabled: !ctx.Channel.IsPrivate)
|
||||
);
|
||||
await using var db = new BotDb();
|
||||
await using var db = BotDb.OpenRead();
|
||||
var query = from warn in db.Warning
|
||||
where warn.IssuerId == moderator.Id && !warn.Retracted
|
||||
orderby warn.Id descending
|
||||
@@ -169,7 +169,7 @@ internal static partial class Warnings
|
||||
new AsciiColumn("Reason"),
|
||||
new AsciiColumn("Context", disabled: !isMod)
|
||||
);
|
||||
await using var db = new BotDb();
|
||||
await using var db = BotDb.OpenRead();
|
||||
IOrderedQueryable<Warning> query;
|
||||
if (isMod)
|
||||
query = from warn in db.Warning
|
||||
|
||||
@@ -53,7 +53,7 @@ internal static partial class Warnings
|
||||
DiscordUser? user = null
|
||||
)
|
||||
{
|
||||
await using var db = new BotDb();
|
||||
await using var db = BotDb.OpenRead();
|
||||
var warnings = await db.Warning.Where(w => id.Equals(w.Id)).ToListAsync().ConfigureAwait(false);
|
||||
if (warnings.Count is 0)
|
||||
{
|
||||
@@ -85,7 +85,7 @@ internal static partial class Warnings
|
||||
DiscordUser? user = null
|
||||
)
|
||||
{
|
||||
await using var db = new BotDb();
|
||||
await using var db = BotDb.OpenRead();
|
||||
var warningsToRemove = await db.Warning.Where(w => w.Id == id).ToListAsync().ConfigureAwait(false);
|
||||
foreach (var w in warningsToRemove)
|
||||
{
|
||||
@@ -117,7 +117,7 @@ internal static partial class Warnings
|
||||
{
|
||||
try
|
||||
{
|
||||
await using var db = new BotDb();
|
||||
await using var db = BotDb.OpenRead();
|
||||
var warningsToRemove = await db.Warning.Where(w => w.DiscordId == user.Id && !w.Retracted).ToListAsync().ConfigureAwait(false);
|
||||
foreach (var w in warningsToRemove)
|
||||
{
|
||||
@@ -146,7 +146,7 @@ internal static partial class Warnings
|
||||
DiscordUser? user = null
|
||||
)
|
||||
{
|
||||
await using var db = new BotDb();
|
||||
await using var db = BotDb.OpenRead();
|
||||
var warn = await db.Warning.FirstOrDefaultAsync(w => w.Id == id).ConfigureAwait(false);
|
||||
if (warn is { Retracted: true })
|
||||
{
|
||||
@@ -166,7 +166,7 @@ internal static partial class Warnings
|
||||
{
|
||||
try
|
||||
{
|
||||
await using var db = new BotDb();
|
||||
await using var db = BotDb.OpenRead();
|
||||
await db.Warning.AddAsync(
|
||||
new()
|
||||
{
|
||||
@@ -211,7 +211,7 @@ internal static partial class Warnings
|
||||
const bool ephemeral = true;
|
||||
int count, removed;
|
||||
bool isKot, isDoggo;
|
||||
await using var db = new BotDb();
|
||||
await using var db = BotDb.OpenRead();
|
||||
count = await db.Warning.CountAsync(w => w.DiscordId == userId && !w.Retracted).ConfigureAwait(false);
|
||||
removed = await db.Warning.CountAsync(w => w.DiscordId == userId && w.Retracted).ConfigureAwait(false);
|
||||
isKot = db.Kot.Any(k => k.UserId == userId);
|
||||
|
||||
@@ -7,6 +7,9 @@ namespace CompatBot.Database;
|
||||
|
||||
internal class BotDb: DbContext
|
||||
{
|
||||
private static ReaderWriterLockSlim dbLock = new();
|
||||
private bool isWriteMode;
|
||||
|
||||
public DbSet<BotState> BotState { get; set; } = null!;
|
||||
public DbSet<Moderator> Moderator { get; set; } = null!;
|
||||
public DbSet<Piracystring> Piracystring { get; set; } = null!;
|
||||
@@ -20,6 +23,20 @@ internal class BotDb: DbContext
|
||||
public DbSet<Kot> Kot { get; set; } = null!;
|
||||
public DbSet<Doggo> Doggo { get; set; } = null!;
|
||||
public DbSet<ForcedNickname> ForcedNicknames { get; set; } = null!;
|
||||
|
||||
private BotDb(bool writeMode = false) => isWriteMode = writeMode;
|
||||
|
||||
public static BotDb OpenRead()
|
||||
{
|
||||
dbLock.EnterReadLock();
|
||||
return new();
|
||||
}
|
||||
|
||||
public static BotDb OpenWrite()
|
||||
{
|
||||
dbLock.EnterWriteLock();
|
||||
return new();
|
||||
}
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
{
|
||||
@@ -54,6 +71,24 @@ internal class BotDb: DbContext
|
||||
//configure name conversion for all configured entities from CamelCase to snake_case
|
||||
modelBuilder.ConfigureMapping(NamingStyles.Underscore);
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
base.Dispose();
|
||||
if (isWriteMode)
|
||||
dbLock.ExitWriteLock();
|
||||
else
|
||||
dbLock.ExitReadLock();
|
||||
}
|
||||
|
||||
public override async ValueTask DisposeAsync()
|
||||
{
|
||||
await base.DisposeAsync();
|
||||
if (isWriteMode)
|
||||
dbLock.ExitWriteLock();
|
||||
else
|
||||
dbLock.ExitReadLock();
|
||||
}
|
||||
}
|
||||
|
||||
internal class BotState
|
||||
|
||||
@@ -10,11 +10,11 @@ public static class DbImporter
|
||||
{
|
||||
public static async Task<bool> UpgradeAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
await using (var db = new BotDb())
|
||||
await using (var db = BotDb.OpenWrite())
|
||||
if (!await UpgradeAsync(db, cancellationToken).ConfigureAwait(false))
|
||||
return false;
|
||||
|
||||
await using (var db = new ThumbnailDb())
|
||||
await using (var db = ThumbnailDb.OpenWrite())
|
||||
{
|
||||
if (!await UpgradeAsync(db,cancellationToken).ConfigureAwait(false))
|
||||
return false;
|
||||
@@ -23,7 +23,7 @@ public static class DbImporter
|
||||
return false;
|
||||
}
|
||||
|
||||
await using (var db = new HardwareDb())
|
||||
await using (var db = HardwareDb.OpenWrite())
|
||||
if (!await UpgradeAsync(db, cancellationToken).ConfigureAwait(false))
|
||||
return false;
|
||||
|
||||
|
||||
@@ -6,8 +6,25 @@ namespace CompatBot.Database;
|
||||
|
||||
internal class HardwareDb : DbContext
|
||||
{
|
||||
private static ReaderWriterLockSlim dbLock = new();
|
||||
private bool isWriteMode;
|
||||
|
||||
public DbSet<HwInfo> HwInfo { get; set; } = null!;
|
||||
|
||||
private HardwareDb(bool writeMode = false) => isWriteMode = writeMode;
|
||||
|
||||
public static HardwareDb OpenRead()
|
||||
{
|
||||
dbLock.EnterReadLock();
|
||||
return new();
|
||||
}
|
||||
|
||||
public static HardwareDb OpenWrite()
|
||||
{
|
||||
dbLock.EnterWriteLock();
|
||||
return new();
|
||||
}
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
{
|
||||
var dbPath = DbImporter.GetDbPath("hw.db", Environment.SpecialFolder.LocalApplicationData);
|
||||
@@ -24,6 +41,24 @@ internal class HardwareDb : DbContext
|
||||
//configure name conversion for all configured entities from CamelCase to snake_case
|
||||
modelBuilder.ConfigureMapping(NamingStyles.Underscore);
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
base.Dispose();
|
||||
if (isWriteMode)
|
||||
dbLock.ExitWriteLock();
|
||||
else
|
||||
dbLock.ExitReadLock();
|
||||
}
|
||||
|
||||
public override async ValueTask DisposeAsync()
|
||||
{
|
||||
await base.DisposeAsync();
|
||||
if (isWriteMode)
|
||||
dbLock.ExitWriteLock();
|
||||
else
|
||||
dbLock.ExitReadLock();
|
||||
}
|
||||
}
|
||||
|
||||
[Flags]
|
||||
|
||||
@@ -58,7 +58,7 @@ internal static class AmdDriverVersionProvider
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task<string> GetFromOpenglAsync(string openglVersion, bool autoRefresh = true)
|
||||
public static async ValueTask<string> GetFromOpenglAsync(string openglVersion, bool autoRefresh = true)
|
||||
{
|
||||
if (OpenglToDriver.TryGetValue(openglVersion, out var result))
|
||||
return result;
|
||||
@@ -111,7 +111,7 @@ internal static class AmdDriverVersionProvider
|
||||
return openglVersion;
|
||||
}
|
||||
|
||||
public static async Task<string> GetFromVulkanAsync(string vulkanVersion, bool autoRefresh = true)
|
||||
public static async ValueTask<string> GetFromVulkanAsync(string vulkanVersion, bool autoRefresh = true)
|
||||
{
|
||||
if (!VulkanToDriver.TryGetValue(vulkanVersion, out var result))
|
||||
await RefreshAsync().ConfigureAwait(false);
|
||||
|
||||
@@ -59,7 +59,7 @@ internal static class ContentFilter
|
||||
public static void RebuildMatcher()
|
||||
{
|
||||
var newFilters = new Dictionary<FilterContext, AhoCorasickDoubleArrayTrie<Piracystring>?>();
|
||||
using var db = new BotDb();
|
||||
using var db = BotDb.OpenRead();
|
||||
foreach (FilterContext ctx in Enum.GetValues<FilterContext>())
|
||||
{
|
||||
var triggerList = db.Piracystring.Where(ps => ps.Disabled == false && ps.Context.HasFlag(ctx)).AsNoTracking()
|
||||
|
||||
@@ -8,7 +8,7 @@ internal static class DisabledCommandsProvider
|
||||
{
|
||||
lock (DisabledCommands)
|
||||
{
|
||||
using var db = new BotDb();
|
||||
using var db = BotDb.OpenRead();
|
||||
foreach (var cmd in db.DisabledCommands.ToList())
|
||||
DisabledCommands.Add(cmd.Command);
|
||||
}
|
||||
@@ -21,7 +21,7 @@ internal static class DisabledCommandsProvider
|
||||
lock (DisabledCommands)
|
||||
if (DisabledCommands.Add(command))
|
||||
{
|
||||
using var db = new BotDb();
|
||||
using var db = BotDb.OpenWrite();
|
||||
db.DisabledCommands.Add(new() {Command = command});
|
||||
db.SaveChanges();
|
||||
}
|
||||
@@ -32,7 +32,7 @@ internal static class DisabledCommandsProvider
|
||||
lock (DisabledCommands)
|
||||
if (DisabledCommands.Remove(command))
|
||||
{
|
||||
using var db = new BotDb();
|
||||
using var db = BotDb.OpenWrite();
|
||||
var cmd = db.DisabledCommands.FirstOrDefault(c => c.Command == command);
|
||||
if (cmd == null)
|
||||
return;
|
||||
@@ -47,7 +47,7 @@ internal static class DisabledCommandsProvider
|
||||
lock (DisabledCommands)
|
||||
{
|
||||
DisabledCommands.Clear();
|
||||
using var db = new BotDb();
|
||||
using var db = BotDb.OpenWrite();
|
||||
db.DisabledCommands.RemoveRange(db.DisabledCommands);
|
||||
db.SaveChanges();
|
||||
}
|
||||
|
||||
@@ -76,8 +76,8 @@ internal static class HwInfoProvider
|
||||
OsName = GetName(osType, items),
|
||||
OsVersion = items["os_version"],
|
||||
};
|
||||
await using var db = new HardwareDb();
|
||||
var existingItem = await db.HwInfo.FindAsync(info.InstallId).ConfigureAwait(false);
|
||||
await using var db = HardwareDb.OpenWrite();
|
||||
var existingItem = await db.HwInfo.FindAsync([info.InstallId], cancellationToken: cancellationToken).ConfigureAwait(false);
|
||||
if (existingItem is null)
|
||||
db.HwInfo.Add(info);
|
||||
else if (existingItem.Timestamp <= info.Timestamp)
|
||||
|
||||
@@ -7,7 +7,7 @@ internal static class InviteWhitelistProvider
|
||||
{
|
||||
public static bool IsWhitelisted(ulong guildId)
|
||||
{
|
||||
using var db = new BotDb();
|
||||
using var db = BotDb.OpenRead();
|
||||
return db.WhitelistedInvites.Any(i => i.GuildId == guildId);
|
||||
}
|
||||
|
||||
@@ -15,9 +15,9 @@ internal static class InviteWhitelistProvider
|
||||
{
|
||||
var code = string.IsNullOrWhiteSpace(invite.Code) ? null : invite.Code;
|
||||
var name = string.IsNullOrWhiteSpace(invite.Guild.Name) ? null : invite.Guild.Name;
|
||||
await using var db = new BotDb();
|
||||
await using var db = BotDb.OpenWrite();
|
||||
var whitelistedInvite = await db.WhitelistedInvites.FirstOrDefaultAsync(i => i.GuildId == invite.Guild.Id);
|
||||
if (whitelistedInvite == null)
|
||||
if (whitelistedInvite is null)
|
||||
return false;
|
||||
|
||||
if (name != null && name != whitelistedInvite.Name)
|
||||
@@ -35,7 +35,7 @@ internal static class InviteWhitelistProvider
|
||||
|
||||
var code = invite.IsRevoked || string.IsNullOrWhiteSpace(invite.Code) ? null : invite.Code;
|
||||
var name = string.IsNullOrWhiteSpace(invite.Guild.Name) ? null : invite.Guild.Name;
|
||||
await using var db = new BotDb();
|
||||
await using var db = BotDb.OpenWrite();
|
||||
await db.WhitelistedInvites.AddAsync(new WhitelistedInvite { GuildId = invite.Guild.Id, Name = name, InviteCode = code }).ConfigureAwait(false);
|
||||
await db.SaveChangesAsync().ConfigureAwait(false);
|
||||
return true;
|
||||
@@ -46,7 +46,7 @@ internal static class InviteWhitelistProvider
|
||||
if (IsWhitelisted(guildId))
|
||||
return false;
|
||||
|
||||
await using var db = new BotDb();
|
||||
await using var db = BotDb.OpenWrite();
|
||||
await db.WhitelistedInvites.AddAsync(new WhitelistedInvite {GuildId = guildId}).ConfigureAwait(false);
|
||||
await db.SaveChangesAsync().ConfigureAwait(false);
|
||||
return true;
|
||||
@@ -54,7 +54,7 @@ internal static class InviteWhitelistProvider
|
||||
|
||||
public static async Task<bool> RemoveAsync(int id)
|
||||
{
|
||||
await using var db = new BotDb();
|
||||
await using var db = BotDb.OpenWrite();
|
||||
var dbItem = await db.WhitelistedInvites.FirstOrDefaultAsync(i => i.Id == id).ConfigureAwait(false);
|
||||
if (dbItem == null)
|
||||
return false;
|
||||
@@ -68,13 +68,13 @@ internal static class InviteWhitelistProvider
|
||||
{
|
||||
while (!Config.Cts.IsCancellationRequested)
|
||||
{
|
||||
await using var db = new BotDb();
|
||||
await using var db = BotDb.OpenWrite();
|
||||
foreach (var invite in db.WhitelistedInvites.Where(i => i.InviteCode != null))
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await client.GetInviteByCodeAsync(invite.InviteCode).ConfigureAwait(false);
|
||||
if (result?.IsRevoked == true)
|
||||
if (result.IsRevoked)
|
||||
invite.InviteCode = null;
|
||||
}
|
||||
catch (NotFoundException)
|
||||
|
||||
@@ -6,12 +6,12 @@ namespace CompatBot.Database.Providers;
|
||||
internal static class ModProvider
|
||||
{
|
||||
private static readonly Dictionary<ulong, Moderator> Moderators;
|
||||
private static readonly BotDb Db = new();
|
||||
public static ReadOnlyDictionary<ulong, Moderator> Mods => new(Moderators);
|
||||
|
||||
static ModProvider()
|
||||
{
|
||||
Moderators = Db.Moderator.AsNoTracking().ToDictionary(m => m.DiscordId, m => m);
|
||||
using var db = BotDb.OpenRead();
|
||||
Moderators = db.Moderator.AsNoTracking().ToDictionary(m => m.DiscordId, m => m);
|
||||
}
|
||||
|
||||
public static bool IsMod(ulong userId) => Moderators.ContainsKey(userId);
|
||||
@@ -23,8 +23,9 @@ internal static class ModProvider
|
||||
return false;
|
||||
|
||||
var newMod = new Moderator {DiscordId = userId};
|
||||
await Db.Moderator.AddAsync(newMod).ConfigureAwait(false);
|
||||
await Db.SaveChangesAsync().ConfigureAwait(false);
|
||||
await using var db = BotDb.OpenWrite();
|
||||
await db.Moderator.AddAsync(newMod).ConfigureAwait(false);
|
||||
await db.SaveChangesAsync().ConfigureAwait(false);
|
||||
lock (Moderators)
|
||||
{
|
||||
if (IsMod(userId))
|
||||
@@ -40,11 +41,12 @@ internal static class ModProvider
|
||||
if (!Moderators.ContainsKey(userId))
|
||||
return false;
|
||||
|
||||
var mod = await Db.Moderator.FirstOrDefaultAsync(m => m.DiscordId == userId).ConfigureAwait(false);
|
||||
await using var db = BotDb.OpenWrite();
|
||||
var mod = await db.Moderator.FirstOrDefaultAsync(m => m.DiscordId == userId).ConfigureAwait(false);
|
||||
if (mod is not null)
|
||||
{
|
||||
Db.Moderator.Remove(mod);
|
||||
await Db.SaveChangesAsync().ConfigureAwait(false);
|
||||
db.Moderator.Remove(mod);
|
||||
await db.SaveChangesAsync().ConfigureAwait(false);
|
||||
}
|
||||
lock (Moderators)
|
||||
{
|
||||
@@ -61,11 +63,12 @@ internal static class ModProvider
|
||||
if (!Moderators.TryGetValue(userId, out var mod) || mod.Sudoer)
|
||||
return false;
|
||||
|
||||
var dbMod = await Db.Moderator.FirstOrDefaultAsync(m => m.DiscordId == userId).ConfigureAwait(false);
|
||||
await using var db = BotDb.OpenWrite();
|
||||
var dbMod = await db.Moderator.FirstOrDefaultAsync(m => m.DiscordId == userId).ConfigureAwait(false);
|
||||
if (dbMod is not null)
|
||||
{
|
||||
dbMod.Sudoer = true;
|
||||
await Db.SaveChangesAsync().ConfigureAwait(false);
|
||||
await db.SaveChangesAsync().ConfigureAwait(false);
|
||||
}
|
||||
mod.Sudoer = true;
|
||||
return true;
|
||||
@@ -76,14 +79,15 @@ internal static class ModProvider
|
||||
if (!Moderators.TryGetValue(userId, out var mod) || !mod.Sudoer)
|
||||
return false;
|
||||
|
||||
var dbMod = await Db.Moderator.FirstOrDefaultAsync(m => m.DiscordId == userId).ConfigureAwait(false);
|
||||
await using var db = BotDb.OpenWrite();
|
||||
var dbMod = await db.Moderator.FirstOrDefaultAsync(m => m.DiscordId == userId).ConfigureAwait(false);
|
||||
if (dbMod is not null)
|
||||
{
|
||||
dbMod.Sudoer = false;
|
||||
await Db.SaveChangesAsync().ConfigureAwait(false);
|
||||
await db.SaveChangesAsync().ConfigureAwait(false);
|
||||
}
|
||||
mod.Sudoer = false;
|
||||
await Db.SaveChangesAsync().ConfigureAwait(false);
|
||||
await db.SaveChangesAsync().ConfigureAwait(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ internal static class ScrapeStateProvider
|
||||
public static bool IsFresh(string locale, string? containerId = null)
|
||||
{
|
||||
var id = GetId(locale, containerId);
|
||||
using var db = new ThumbnailDb();
|
||||
using var db = ThumbnailDb.OpenRead();
|
||||
var timestamp = string.IsNullOrEmpty(id) ? db.State.OrderBy(s => s.Timestamp).FirstOrDefault() : db.State.FirstOrDefault(s => s.Locale == id);
|
||||
if (timestamp is { Timestamp: long checkDate and > 0 })
|
||||
return IsFresh(new DateTime(checkDate, DateTimeKind.Utc));
|
||||
@@ -22,7 +22,7 @@ internal static class ScrapeStateProvider
|
||||
|
||||
public static bool IsFresh(string locale, DateTime dataTimestamp)
|
||||
{
|
||||
using var db = new ThumbnailDb();
|
||||
using var db = ThumbnailDb.OpenRead();
|
||||
var timestamp = string.IsNullOrEmpty(locale) ? db.State.OrderBy(s => s.Timestamp).FirstOrDefault() : db.State.FirstOrDefault(s => s.Locale == locale);
|
||||
if (timestamp is { Timestamp: long checkDate and > 0 })
|
||||
return new DateTime(checkDate, DateTimeKind.Utc) > dataTimestamp;
|
||||
@@ -35,18 +35,18 @@ internal static class ScrapeStateProvider
|
||||
throw new ArgumentException("Locale is mandatory", nameof(locale));
|
||||
|
||||
var id = GetId(locale, containerId);
|
||||
await using var db = new ThumbnailDb();
|
||||
await using var db = ThumbnailDb.OpenWrite();
|
||||
var timestamp = db.State.FirstOrDefault(s => s.Locale == id);
|
||||
if (timestamp == null)
|
||||
await db.State.AddAsync(new State {Locale = id, Timestamp = DateTime.UtcNow.Ticks}).ConfigureAwait(false);
|
||||
if (timestamp is null)
|
||||
await db.State.AddAsync(new() {Locale = id, Timestamp = DateTime.UtcNow.Ticks}).ConfigureAwait(false);
|
||||
else
|
||||
timestamp.Timestamp = DateTime.UtcNow.Ticks;
|
||||
await db.SaveChangesAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public static async Task CleanAsync(CancellationToken cancellationToken)
|
||||
public static async ValueTask CleanAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
await using var db = new ThumbnailDb();
|
||||
await using var db = ThumbnailDb.OpenWrite();
|
||||
var latestTimestamp = db.State.OrderByDescending(s => s.Timestamp).FirstOrDefault()?.Timestamp;
|
||||
if (!latestTimestamp.HasValue)
|
||||
return;
|
||||
|
||||
@@ -10,7 +10,7 @@ internal static class SqlConfiguration
|
||||
|
||||
public static async Task RestoreAsync()
|
||||
{
|
||||
await using var db = new BotDb();
|
||||
await using var db = BotDb.OpenRead();
|
||||
var setVars = await db.BotState.AsNoTracking().Where(v => v.Key.StartsWith(ConfigVarPrefix)).ToListAsync().ConfigureAwait(false);
|
||||
if (setVars.Any())
|
||||
{
|
||||
|
||||
@@ -64,14 +64,14 @@ internal static class StatsStorage
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public static async Task SaveAsync(bool wait = false)
|
||||
public static async ValueTask SaveAsync(bool wait = false)
|
||||
{
|
||||
if (await Barrier.WaitAsync(0).ConfigureAwait(false))
|
||||
{
|
||||
try
|
||||
{
|
||||
Config.Log.Debug("Got stats saving lock");
|
||||
await using var db = new BotDb();
|
||||
await using var db = BotDb.OpenWrite();
|
||||
foreach (var (category, cache) in AllCaches)
|
||||
{
|
||||
var entries = cache.GetCacheEntries<string>();
|
||||
@@ -126,7 +126,7 @@ internal static class StatsStorage
|
||||
public static async Task RestoreAsync()
|
||||
{
|
||||
var now = DateTime.UtcNow;
|
||||
await using var db = new BotDb();
|
||||
await using var db = BotDb.OpenWrite();
|
||||
foreach (var (category, cache) in AllCaches)
|
||||
{
|
||||
var entries = await db.Stats.Where(e => e.Category == category).ToListAsync().ConfigureAwait(false);
|
||||
|
||||
@@ -17,7 +17,7 @@ internal static class SyscallInfoProvider
|
||||
{
|
||||
try
|
||||
{
|
||||
await using var db = new ThumbnailDb();
|
||||
await using var db = ThumbnailDb.OpenWrite();
|
||||
foreach (var productCodeMap in syscallInfo)
|
||||
{
|
||||
var product = db.Thumbnail.AsNoTracking().FirstOrDefault(t => t.ProductCode == productCodeMap.Key)
|
||||
@@ -49,7 +49,7 @@ internal static class SyscallInfoProvider
|
||||
{
|
||||
var syscallStats = new TSyscallStats();
|
||||
int funcs, links = 0;
|
||||
await using var db = new ThumbnailDb();
|
||||
await using var db = ThumbnailDb.OpenWrite();
|
||||
var funcsToRemove = new List<SyscallInfo>(0);
|
||||
try
|
||||
{
|
||||
@@ -106,7 +106,7 @@ internal static class SyscallInfoProvider
|
||||
public static async Task<(int funcs, int links)> FixDuplicatesAsync()
|
||||
{
|
||||
int funcs = 0, links = 0;
|
||||
await using var db = new ThumbnailDb();
|
||||
await using var db = ThumbnailDb.OpenWrite();
|
||||
var duplicateFunctionNames = await db.SyscallInfo.Where(sci => db.SyscallInfo.Count(isci => isci.Function == sci.Function) > 1).Distinct().ToListAsync().ConfigureAwait(false);
|
||||
if (duplicateFunctionNames.Count == 0)
|
||||
return (0, 0);
|
||||
|
||||
@@ -22,7 +22,7 @@ internal static class ThumbnailProvider
|
||||
if (tmdbInfo is { Icon.Url: string tmdbIconUrl })
|
||||
return tmdbIconUrl;
|
||||
|
||||
await using var db = new ThumbnailDb();
|
||||
await using var db = ThumbnailDb.OpenWrite();
|
||||
var thumb = await db.Thumbnail.FirstOrDefaultAsync(t => t.ProductCode == productCode).ConfigureAwait(false);
|
||||
//todo: add search task if not found
|
||||
if (thumb?.EmbeddableUrl is {Length: >0} embeddableUrl)
|
||||
@@ -55,13 +55,13 @@ internal static class ThumbnailProvider
|
||||
return embedUrl;
|
||||
}
|
||||
|
||||
public static async Task<string?> GetTitleNameAsync(string? productCode, CancellationToken cancellationToken)
|
||||
public static async ValueTask<string?> GetTitleNameAsync(string? productCode, CancellationToken cancellationToken)
|
||||
{
|
||||
if (string.IsNullOrEmpty(productCode))
|
||||
return null;
|
||||
|
||||
productCode = productCode.ToUpperInvariant();
|
||||
await using var db = new ThumbnailDb();
|
||||
await using var db = ThumbnailDb.OpenWrite();
|
||||
var thumb = await db.Thumbnail.FirstOrDefaultAsync(
|
||||
t => t.ProductCode == productCode,
|
||||
cancellationToken: cancellationToken
|
||||
@@ -93,13 +93,13 @@ internal static class ThumbnailProvider
|
||||
return title;
|
||||
}
|
||||
|
||||
public static async Task<(string? url, DiscordColor color)> GetThumbnailUrlWithColorAsync(DiscordClient client, string contentId, DiscordColor defaultColor, string? url = null)
|
||||
public static async ValueTask<(string? url, DiscordColor color)> GetThumbnailUrlWithColorAsync(DiscordClient client, string contentId, DiscordColor defaultColor, string? url = null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(contentId))
|
||||
throw new ArgumentException("ContentID can't be empty", nameof(contentId));
|
||||
|
||||
contentId = contentId.ToUpperInvariant();
|
||||
await using var db = new ThumbnailDb();
|
||||
await using var db = ThumbnailDb.OpenWrite();
|
||||
var info = await db.Thumbnail.FirstOrDefaultAsync(ti => ti.ContentId == contentId, Config.Cts.Token).ConfigureAwait(false);
|
||||
info ??= new() {Url = url};
|
||||
if (info.Url is null)
|
||||
|
||||
@@ -8,7 +8,7 @@ public static class TitleUpdateInfoProvider
|
||||
{
|
||||
private static readonly PsnClient.Client Client = new();
|
||||
|
||||
public static async Task<TitlePatch?> GetAsync(string? productId, CancellationToken cancellationToken)
|
||||
public static async ValueTask<TitlePatch?> GetAsync(string? productId, CancellationToken cancellationToken)
|
||||
{
|
||||
if (string.IsNullOrEmpty(productId))
|
||||
return default;
|
||||
@@ -18,7 +18,7 @@ public static class TitleUpdateInfoProvider
|
||||
if (xml is {Length: > 10})
|
||||
{
|
||||
var xmlChecksum = xml.GetStableHash();
|
||||
await using var db = new ThumbnailDb();
|
||||
await using var db = ThumbnailDb.OpenWrite();
|
||||
var updateInfo = db.GameUpdateInfo.FirstOrDefault(ui => ui.ProductCode == productId);
|
||||
if (updateInfo is null)
|
||||
db.GameUpdateInfo.Add(new() {ProductCode = productId, MetaHash = xmlChecksum, MetaXml = xml, Timestamp = DateTime.UtcNow.Ticks});
|
||||
@@ -34,7 +34,7 @@ public static class TitleUpdateInfoProvider
|
||||
}
|
||||
if (update?.Tag?.Packages?.Length is null or 0)
|
||||
{
|
||||
await using var db = new ThumbnailDb();
|
||||
await using var db = ThumbnailDb.OpenRead();
|
||||
var updateInfo = db.GameUpdateInfo.FirstOrDefault(ui => ui.ProductCode == productId);
|
||||
if (updateInfo is null)
|
||||
return update;
|
||||
@@ -52,7 +52,7 @@ public static class TitleUpdateInfoProvider
|
||||
|
||||
public static async Task RefreshGameUpdateInfoAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
await using var db = new ThumbnailDb();
|
||||
await using var db = ThumbnailDb.OpenRead();
|
||||
do
|
||||
{
|
||||
var productCodeList = await db.Thumbnail.AsNoTracking().Select(t => t.ProductCode).ToListAsync(cancellationToken).ConfigureAwait(false);
|
||||
|
||||
@@ -6,6 +6,9 @@ namespace CompatBot.Database;
|
||||
|
||||
internal class ThumbnailDb : DbContext
|
||||
{
|
||||
private static ReaderWriterLockSlim dbLock = new();
|
||||
private bool isWriteMode;
|
||||
|
||||
public DbSet<State> State { get; set; } = null!;
|
||||
public DbSet<Thumbnail> Thumbnail { get; set; } = null!;
|
||||
public DbSet<GameUpdateInfo> GameUpdateInfo { get; set; } = null!;
|
||||
@@ -15,6 +18,20 @@ internal class ThumbnailDb : DbContext
|
||||
public DbSet<Fortune> Fortune { get; set; } = null!;
|
||||
public DbSet<NamePool> NamePool { get; set; } = null!;
|
||||
|
||||
private ThumbnailDb(bool writeMode = false) => isWriteMode = writeMode;
|
||||
|
||||
public static ThumbnailDb OpenRead()
|
||||
{
|
||||
dbLock.EnterReadLock();
|
||||
return new();
|
||||
}
|
||||
|
||||
public static ThumbnailDb OpenWrite()
|
||||
{
|
||||
dbLock.EnterWriteLock();
|
||||
return new();
|
||||
}
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
{
|
||||
var dbPath = DbImporter.GetDbPath("thumbs.db", Environment.SpecialFolder.LocalApplicationData);
|
||||
@@ -43,6 +60,24 @@ internal class ThumbnailDb : DbContext
|
||||
//configure name conversion for all configured entities from CamelCase to snake_case
|
||||
modelBuilder.ConfigureMapping(NamingStyles.Underscore);
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
base.Dispose();
|
||||
if (isWriteMode)
|
||||
dbLock.ExitWriteLock();
|
||||
else
|
||||
dbLock.ExitReadLock();
|
||||
}
|
||||
|
||||
public override async ValueTask DisposeAsync()
|
||||
{
|
||||
await base.DisposeAsync();
|
||||
if (isWriteMode)
|
||||
dbLock.ExitWriteLock();
|
||||
else
|
||||
dbLock.ExitReadLock();
|
||||
}
|
||||
}
|
||||
|
||||
internal class State
|
||||
|
||||
@@ -119,7 +119,7 @@ namespace CompatBot.EventHandlers
|
||||
|
||||
if (!string.IsNullOrEmpty(args.Message.Content) && Paws().Matches(args.Message.Content) is MatchCollection mc)
|
||||
{
|
||||
await using var db = new BotDb();
|
||||
await using var db = BotDb.OpenRead();
|
||||
var matchedGroups = (from m in mc
|
||||
from Group g in m.Groups
|
||||
where g is { Success: true, Value.Length: > 0 }
|
||||
|
||||
@@ -9,7 +9,7 @@ internal static class BotStatusMonitor
|
||||
{
|
||||
try
|
||||
{
|
||||
await using var db = new BotDb();
|
||||
await using var db = BotDb.OpenRead();
|
||||
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);
|
||||
var msg = txt?.Value;
|
||||
|
||||
@@ -7,7 +7,7 @@ internal static class Greeter
|
||||
{
|
||||
public static async Task OnMemberAdded(DiscordClient _, GuildMemberAddedEventArgs args)
|
||||
{
|
||||
await using var db = new BotDb();
|
||||
await using var db = BotDb.OpenRead();
|
||||
if (await db.Explanation.FirstOrDefaultAsync(e => e.Keyword == "motd").ConfigureAwait(false) is {Text.Length: >0} explanation)
|
||||
{
|
||||
var dm = await args.Member.CreateDmChannelAsync().ConfigureAwait(false);
|
||||
|
||||
@@ -58,7 +58,7 @@ internal static partial class PostLogHelpHandler
|
||||
|
||||
public static async Task<Explanation> GetExplanationAsync(string term)
|
||||
{
|
||||
await using var db = new BotDb();
|
||||
await using var db = BotDb.OpenRead();
|
||||
var result = await db.Explanation.FirstOrDefaultAsync(e => e.Keyword == term).ConfigureAwait(false);
|
||||
return result ?? DefaultExplanation[term];
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ internal static class ThumbnailCacheMonitor
|
||||
if (!args.Message.Attachments.Any())
|
||||
return;
|
||||
|
||||
await using var db = new ThumbnailDb();
|
||||
await using var db = ThumbnailDb.OpenRead();
|
||||
var thumb = db.Thumbnail.FirstOrDefault(i => i.ContentId == args.Message.Content);
|
||||
if (thumb is { EmbeddableUrl: { Length: > 0 } url } && args.Message.Attachments.Any(a => a.Url == url))
|
||||
{
|
||||
|
||||
@@ -19,7 +19,7 @@ public static class UsernameValidationMonitor
|
||||
if (guild.Permissions?.HasFlag(DiscordPermission.ChangeNickname) is false)
|
||||
return;
|
||||
|
||||
await using var db = new BotDb();
|
||||
await using var db = BotDb.OpenRead();
|
||||
var forcedNickname = await db.ForcedNicknames.AsNoTracking().FirstOrDefaultAsync(x => x.UserId == guildMember.Id && x.GuildId == guildMember.Guild.Id).ConfigureAwait(false);
|
||||
if (forcedNickname is null)
|
||||
return;
|
||||
@@ -54,7 +54,7 @@ public static class UsernameValidationMonitor
|
||||
if (guild.Permissions?.HasFlag(DiscordPermission.ChangeNickname) is false)
|
||||
continue;
|
||||
|
||||
await using var db = new BotDb();
|
||||
await using var db = BotDb.OpenRead();
|
||||
var forcedNicknames = await db.ForcedNicknames
|
||||
.Where(mem => mem.GuildId == guild.Id)
|
||||
.ToListAsync()
|
||||
|
||||
@@ -206,7 +206,7 @@ public static class UsernameZalgoMonitor
|
||||
{
|
||||
var hash = userId.GetHashCode();
|
||||
var rng = new Random(hash);
|
||||
using var db = new ThumbnailDb();
|
||||
using var db = ThumbnailDb.OpenRead();
|
||||
var count = db.NamePool.Count();
|
||||
var name = db.NamePool.Skip(rng.Next(count)).First().Name;
|
||||
return name + Config.RenameNameSuffix;
|
||||
|
||||
@@ -192,7 +192,7 @@ internal static class Program
|
||||
var msg = new StringBuilder($"Bot admin id{(owners.Count == 1 ? "": "s")}:");
|
||||
if (owners.Count > 1)
|
||||
msg.AppendLine();
|
||||
await using var db = new BotDb();
|
||||
await using var db = BotDb.OpenRead();
|
||||
foreach (var owner in owners)
|
||||
{
|
||||
msg.AppendLine($"\t{owner.Id} ({owner.Username ?? "???"}#{owner.Discriminator ?? "????"})");
|
||||
@@ -310,7 +310,7 @@ internal static class Program
|
||||
|
||||
ulong? channelId = null;
|
||||
string? restartMsg = null;
|
||||
await using (var db = new BotDb())
|
||||
await using (var db = BotDb.OpenRead())
|
||||
{
|
||||
var chState = db.BotState.FirstOrDefault(k => k.Key == "bot-restart-channel");
|
||||
if (chState != null)
|
||||
|
||||
@@ -113,7 +113,7 @@ internal static partial class GameTdbScraper
|
||||
if (string.IsNullOrEmpty(title))
|
||||
continue;
|
||||
|
||||
await using var db = new ThumbnailDb();
|
||||
await using var db = ThumbnailDb.OpenRead();
|
||||
var item = await db.Thumbnail.FirstOrDefaultAsync(t => t.ProductCode == productId, cancellationToken).ConfigureAwait(false);
|
||||
if (item is null)
|
||||
{
|
||||
|
||||
@@ -23,6 +23,7 @@ internal sealed partial class PsnScraper
|
||||
private static DateTime storeRefreshTimestamp = DateTime.MinValue;
|
||||
private static readonly SemaphoreSlim QueueLimiter = new(32, 32);
|
||||
|
||||
[Obsolete]
|
||||
public static async Task RunAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
do
|
||||
@@ -292,7 +293,7 @@ internal sealed partial class PsnScraper
|
||||
|
||||
private static bool NeedLookup(string contentId)
|
||||
{
|
||||
using var db = new ThumbnailDb();
|
||||
using var db = ThumbnailDb.OpenRead();
|
||||
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)))
|
||||
@@ -350,7 +351,7 @@ internal sealed partial class PsnScraper
|
||||
return;
|
||||
|
||||
name = string.IsNullOrEmpty(name) ? null : name;
|
||||
await using var db = new ThumbnailDb();
|
||||
await using var db = ThumbnailDb.OpenRead();
|
||||
var savedItem = db.Thumbnail.FirstOrDefault(t => t.ProductCode == productCode);
|
||||
if (savedItem == null)
|
||||
{
|
||||
|
||||
@@ -62,7 +62,7 @@ internal static class TitleInfoFormatter
|
||||
var productCodePart = string.IsNullOrWhiteSpace(titleId) ? "" : $"[{titleId}] ";
|
||||
if (!StatusColors.TryGetValue(info.Status, out _) && !string.IsNullOrEmpty(titleId))
|
||||
{
|
||||
using var db = new ThumbnailDb();
|
||||
using var db = ThumbnailDb.OpenRead();
|
||||
var thumb = db.Thumbnail.FirstOrDefault(t => t.ProductCode == titleId);
|
||||
if (thumb?.CompatibilityStatus != null)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user