Fix several database issues
- Specify default value - Cache all guild users on initial download - Fix foreign key constraint issues
This commit is contained in:
parent
da31ce3e0d
commit
6afa6dc2c6
6 changed files with 36 additions and 16 deletions
|
@ -33,6 +33,7 @@ public class BotDatabaseContext : DbContext {
|
||||||
modelBuilder.Entity<CachedGuildUser>(entity => {
|
modelBuilder.Entity<CachedGuildUser>(entity => {
|
||||||
entity.Navigation(e => e.User).AutoInclude();
|
entity.Navigation(e => e.User).AutoInclude();
|
||||||
entity.HasKey(e => new { e.UserId, e.GuildId });
|
entity.HasKey(e => new { e.UserId, e.GuildId });
|
||||||
|
entity.Property(e => e.FirstSeenTime).HasDefaultValueSql("now()");
|
||||||
});
|
});
|
||||||
modelBuilder.Entity<CachedGuildMessage>(entity => entity.Property(e => e.CreatedAt).HasDefaultValueSql("now()"));
|
modelBuilder.Entity<CachedGuildMessage>(entity => entity.Property(e => e.CreatedAt).HasDefaultValueSql("now()"));
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,8 +13,8 @@ using RegexBot.Data;
|
||||||
namespace RegexBot.Data.Migrations
|
namespace RegexBot.Data.Migrations
|
||||||
{
|
{
|
||||||
[DbContext(typeof(BotDatabaseContext))]
|
[DbContext(typeof(BotDatabaseContext))]
|
||||||
[Migration("20220513061851_InitialEFSetup")]
|
[Migration("20220610210059_InitialMigration")]
|
||||||
partial class InitialEFSetup
|
partial class InitialMigration
|
||||||
{
|
{
|
||||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
{
|
{
|
||||||
|
@ -83,8 +83,10 @@ namespace RegexBot.Data.Migrations
|
||||||
.HasColumnName("guild_id");
|
.HasColumnName("guild_id");
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("FirstSeenTime")
|
b.Property<DateTimeOffset>("FirstSeenTime")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp with time zone")
|
||||||
.HasColumnName("first_seen_time");
|
.HasColumnName("first_seen_time")
|
||||||
|
.HasDefaultValueSql("now()");
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("GULastUpdateTime")
|
b.Property<DateTimeOffset>("GULastUpdateTime")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp with time zone")
|
|
@ -7,7 +7,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||||
|
|
||||||
namespace RegexBot.Data.Migrations
|
namespace RegexBot.Data.Migrations
|
||||||
{
|
{
|
||||||
public partial class InitialEFSetup : Migration
|
public partial class InitialMigration : Migration
|
||||||
{
|
{
|
||||||
protected override void Up(MigrationBuilder migrationBuilder)
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
{
|
{
|
||||||
|
@ -73,7 +73,7 @@ namespace RegexBot.Data.Migrations
|
||||||
user_id = table.Column<long>(type: "bigint", nullable: false),
|
user_id = table.Column<long>(type: "bigint", nullable: false),
|
||||||
guild_id = table.Column<long>(type: "bigint", nullable: false),
|
guild_id = table.Column<long>(type: "bigint", nullable: false),
|
||||||
gu_last_update_time = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
|
gu_last_update_time = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
|
||||||
first_seen_time = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
|
first_seen_time = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"),
|
||||||
nickname = table.Column<string>(type: "text", nullable: true)
|
nickname = table.Column<string>(type: "text", nullable: true)
|
||||||
},
|
},
|
||||||
constraints: table =>
|
constraints: table =>
|
|
@ -81,8 +81,10 @@ namespace RegexBot.Data.Migrations
|
||||||
.HasColumnName("guild_id");
|
.HasColumnName("guild_id");
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("FirstSeenTime")
|
b.Property<DateTimeOffset>("FirstSeenTime")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp with time zone")
|
||||||
.HasColumnName("first_seen_time");
|
.HasColumnName("first_seen_time")
|
||||||
|
.HasDefaultValueSql("now()");
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("GULastUpdateTime")
|
b.Property<DateTimeOffset>("GULastUpdateTime")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp with time zone")
|
||||||
|
|
|
@ -29,7 +29,8 @@ class Program {
|
||||||
MessageCacheSize = 0, // using our own
|
MessageCacheSize = 0, // using our own
|
||||||
LogLevel = LogSeverity.Info,
|
LogLevel = LogSeverity.Info,
|
||||||
GatewayIntents = GatewayIntents.All & ~GatewayIntents.GuildPresences,
|
GatewayIntents = GatewayIntents.All & ~GatewayIntents.GuildPresences,
|
||||||
LogGatewayIntentWarnings = false
|
LogGatewayIntentWarnings = false,
|
||||||
|
AlwaysDownloadUsers = true
|
||||||
});
|
});
|
||||||
|
|
||||||
// Kerobot class initialization - will set up services and modules
|
// Kerobot class initialization - will set up services and modules
|
||||||
|
|
|
@ -13,16 +13,35 @@ class UserCachingSubservice {
|
||||||
private static Regex DiscriminatorSearch { get; } = new(@"(.+)#(\d{4}(?!\d))", RegexOptions.Compiled);
|
private static Regex DiscriminatorSearch { get; } = new(@"(.+)#(\d{4}(?!\d))", RegexOptions.Compiled);
|
||||||
|
|
||||||
internal UserCachingSubservice(RegexbotClient bot) {
|
internal UserCachingSubservice(RegexbotClient bot) {
|
||||||
|
bot.DiscordClient.GuildMembersDownloaded += DiscordClient_GuildMembersDownloaded;
|
||||||
bot.DiscordClient.GuildMemberUpdated += DiscordClient_GuildMemberUpdated;
|
bot.DiscordClient.GuildMemberUpdated += DiscordClient_GuildMemberUpdated;
|
||||||
bot.DiscordClient.UserUpdated += DiscordClient_UserUpdated;
|
bot.DiscordClient.UserUpdated += DiscordClient_UserUpdated;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task DiscordClient_GuildMembersDownloaded(SocketGuild arg) {
|
||||||
|
using var db = new BotDatabaseContext();
|
||||||
|
foreach (var user in arg.Users) {
|
||||||
|
UpdateUser(user, db);
|
||||||
|
UpdateGuildUser(user, db);
|
||||||
|
}
|
||||||
|
await db.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task DiscordClient_GuildMemberUpdated(Discord.Cacheable<SocketGuildUser, ulong> old, SocketGuildUser current) {
|
||||||
|
using var db = new BotDatabaseContext();
|
||||||
|
UpdateUser(current, db); // Update user data first (avoid potential foreign key constraint violation)
|
||||||
|
UpdateGuildUser(current, db);
|
||||||
|
|
||||||
|
await db.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
|
||||||
private async Task DiscordClient_UserUpdated(SocketUser old, SocketUser current) {
|
private async Task DiscordClient_UserUpdated(SocketUser old, SocketUser current) {
|
||||||
using var db = new BotDatabaseContext();
|
using var db = new BotDatabaseContext();
|
||||||
UpdateUser(current, db);
|
UpdateUser(current, db);
|
||||||
await db.SaveChangesAsync();
|
await db.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IMPORTANT: Do NOT forget to save changes in database after calling this!
|
||||||
private static void UpdateUser(SocketUser user, BotDatabaseContext db) {
|
private static void UpdateUser(SocketUser user, BotDatabaseContext db) {
|
||||||
CachedUser uinfo;
|
CachedUser uinfo;
|
||||||
try {
|
try {
|
||||||
|
@ -38,23 +57,18 @@ class UserCachingSubservice {
|
||||||
uinfo.ULastUpdateTime = DateTimeOffset.UtcNow;
|
uinfo.ULastUpdateTime = DateTimeOffset.UtcNow;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task DiscordClient_GuildMemberUpdated(Discord.Cacheable<SocketGuildUser, ulong> old, SocketGuildUser current) {
|
private static void UpdateGuildUser(SocketGuildUser user, BotDatabaseContext db) {
|
||||||
using var db = new BotDatabaseContext();
|
|
||||||
UpdateUser(current, db); // Update user data too (avoid potential foreign key constraint violation)
|
|
||||||
|
|
||||||
CachedGuildUser guinfo;
|
CachedGuildUser guinfo;
|
||||||
try {
|
try {
|
||||||
guinfo = db.GuildUserCache.Where(c => c.GuildId == (long)current.Guild.Id && c.UserId == (long)current.Id).First();
|
guinfo = db.GuildUserCache.Where(c => c.GuildId == (long)user.Guild.Id && c.UserId == (long)user.Id).First();
|
||||||
} catch (InvalidOperationException) {
|
} catch (InvalidOperationException) {
|
||||||
guinfo = new() { GuildId = (long)current.Guild.Id, UserId = (long)current.Id };
|
guinfo = new() { GuildId = (long)user.Guild.Id, UserId = (long)user.Id };
|
||||||
db.GuildUserCache.Add(guinfo);
|
db.GuildUserCache.Add(guinfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
guinfo.GULastUpdateTime = DateTimeOffset.UtcNow;
|
guinfo.GULastUpdateTime = DateTimeOffset.UtcNow;
|
||||||
guinfo.Nickname = current.Nickname;
|
guinfo.Nickname = user.Nickname;
|
||||||
// TODO guild-specific avatar, other details?
|
// TODO guild-specific avatar, other details?
|
||||||
|
|
||||||
await db.SaveChangesAsync();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hooked
|
// Hooked
|
||||||
|
|
Loading…
Reference in a new issue