diff --git a/RegexBot/Data/BotDatabaseContext.cs b/RegexBot/Data/BotDatabaseContext.cs index 40bb2c5..e6fc0a6 100644 --- a/RegexBot/Data/BotDatabaseContext.cs +++ b/RegexBot/Data/BotDatabaseContext.cs @@ -33,6 +33,7 @@ public class BotDatabaseContext : DbContext { modelBuilder.Entity(entity => { entity.Navigation(e => e.User).AutoInclude(); entity.HasKey(e => new { e.UserId, e.GuildId }); + entity.Property(e => e.FirstSeenTime).HasDefaultValueSql("now()"); }); modelBuilder.Entity(entity => entity.Property(e => e.CreatedAt).HasDefaultValueSql("now()")); } diff --git a/RegexBot/Data/Migrations/20220513061851_InitialEFSetup.Designer.cs b/RegexBot/Data/Migrations/20220610210059_InitialMigration.Designer.cs similarity index 96% rename from RegexBot/Data/Migrations/20220513061851_InitialEFSetup.Designer.cs rename to RegexBot/Data/Migrations/20220610210059_InitialMigration.Designer.cs index e5ffb2a..b2552b0 100644 --- a/RegexBot/Data/Migrations/20220513061851_InitialEFSetup.Designer.cs +++ b/RegexBot/Data/Migrations/20220610210059_InitialMigration.Designer.cs @@ -13,8 +13,8 @@ using RegexBot.Data; namespace RegexBot.Data.Migrations { [DbContext(typeof(BotDatabaseContext))] - [Migration("20220513061851_InitialEFSetup")] - partial class InitialEFSetup + [Migration("20220610210059_InitialMigration")] + partial class InitialMigration { protected override void BuildTargetModel(ModelBuilder modelBuilder) { @@ -83,8 +83,10 @@ namespace RegexBot.Data.Migrations .HasColumnName("guild_id"); b.Property("FirstSeenTime") + .ValueGeneratedOnAdd() .HasColumnType("timestamp with time zone") - .HasColumnName("first_seen_time"); + .HasColumnName("first_seen_time") + .HasDefaultValueSql("now()"); b.Property("GULastUpdateTime") .HasColumnType("timestamp with time zone") diff --git a/RegexBot/Data/Migrations/20220513061851_InitialEFSetup.cs b/RegexBot/Data/Migrations/20220610210059_InitialMigration.cs similarity index 97% rename from RegexBot/Data/Migrations/20220513061851_InitialEFSetup.cs rename to RegexBot/Data/Migrations/20220610210059_InitialMigration.cs index 4c7261e..6614a6b 100644 --- a/RegexBot/Data/Migrations/20220513061851_InitialEFSetup.cs +++ b/RegexBot/Data/Migrations/20220610210059_InitialMigration.cs @@ -7,7 +7,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; namespace RegexBot.Data.Migrations { - public partial class InitialEFSetup : Migration + public partial class InitialMigration : Migration { protected override void Up(MigrationBuilder migrationBuilder) { @@ -73,7 +73,7 @@ namespace RegexBot.Data.Migrations user_id = table.Column(type: "bigint", nullable: false), guild_id = table.Column(type: "bigint", nullable: false), gu_last_update_time = table.Column(type: "timestamp with time zone", nullable: false), - first_seen_time = table.Column(type: "timestamp with time zone", nullable: false), + first_seen_time = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"), nickname = table.Column(type: "text", nullable: true) }, constraints: table => diff --git a/RegexBot/Data/Migrations/BotDatabaseContextModelSnapshot.cs b/RegexBot/Data/Migrations/BotDatabaseContextModelSnapshot.cs index 0a8a9a2..753f530 100644 --- a/RegexBot/Data/Migrations/BotDatabaseContextModelSnapshot.cs +++ b/RegexBot/Data/Migrations/BotDatabaseContextModelSnapshot.cs @@ -81,8 +81,10 @@ namespace RegexBot.Data.Migrations .HasColumnName("guild_id"); b.Property("FirstSeenTime") + .ValueGeneratedOnAdd() .HasColumnType("timestamp with time zone") - .HasColumnName("first_seen_time"); + .HasColumnName("first_seen_time") + .HasDefaultValueSql("now()"); b.Property("GULastUpdateTime") .HasColumnType("timestamp with time zone") diff --git a/RegexBot/Program.cs b/RegexBot/Program.cs index e8d821d..662fe61 100644 --- a/RegexBot/Program.cs +++ b/RegexBot/Program.cs @@ -29,7 +29,8 @@ class Program { MessageCacheSize = 0, // using our own LogLevel = LogSeverity.Info, GatewayIntents = GatewayIntents.All & ~GatewayIntents.GuildPresences, - LogGatewayIntentWarnings = false + LogGatewayIntentWarnings = false, + AlwaysDownloadUsers = true }); // Kerobot class initialization - will set up services and modules diff --git a/RegexBot/Services/EntityCache/UserCachingSubservice.cs b/RegexBot/Services/EntityCache/UserCachingSubservice.cs index f1fb673..0e2a416 100644 --- a/RegexBot/Services/EntityCache/UserCachingSubservice.cs +++ b/RegexBot/Services/EntityCache/UserCachingSubservice.cs @@ -13,16 +13,35 @@ class UserCachingSubservice { private static Regex DiscriminatorSearch { get; } = new(@"(.+)#(\d{4}(?!\d))", RegexOptions.Compiled); internal UserCachingSubservice(RegexbotClient bot) { + bot.DiscordClient.GuildMembersDownloaded += DiscordClient_GuildMembersDownloaded; bot.DiscordClient.GuildMemberUpdated += DiscordClient_GuildMemberUpdated; 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 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) { using var db = new BotDatabaseContext(); UpdateUser(current, db); await db.SaveChangesAsync(); } + // IMPORTANT: Do NOT forget to save changes in database after calling this! private static void UpdateUser(SocketUser user, BotDatabaseContext db) { CachedUser uinfo; try { @@ -38,23 +57,18 @@ class UserCachingSubservice { uinfo.ULastUpdateTime = DateTimeOffset.UtcNow; } - private async Task DiscordClient_GuildMemberUpdated(Discord.Cacheable old, SocketGuildUser current) { - using var db = new BotDatabaseContext(); - UpdateUser(current, db); // Update user data too (avoid potential foreign key constraint violation) - + private static void UpdateGuildUser(SocketGuildUser user, BotDatabaseContext db) { CachedGuildUser guinfo; 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) { - 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); } guinfo.GULastUpdateTime = DateTimeOffset.UtcNow; - guinfo.Nickname = current.Nickname; + guinfo.Nickname = user.Nickname; // TODO guild-specific avatar, other details? - - await db.SaveChangesAsync(); } // Hooked