From 0d3a56c8db75bf36e76cc3d0fc77cb3f7a45d03a Mon Sep 17 00:00:00 2001 From: adam Date: Thu, 6 Mar 2025 17:02:33 -0500 Subject: [PATCH] double-adding of Accounts is sovled - but now it's double-adding Users. whyyyy --- Behaver.cs | 1 + .../DiscordInterface/DiscordInterface.cs | 36 +++++++++++++------ README.md | 10 ++++++ Rememberer.cs | 36 ++++++++++++++----- .../Controllers/ChannelsController.cs | 20 +++++------ WebInterface/Controllers/HomeController.cs | 17 +++++---- .../Controllers/api/ChannelsControler.cs | 1 + 7 files changed, 81 insertions(+), 40 deletions(-) diff --git a/Behaver.cs b/Behaver.cs index e255e80..859f6f7 100644 --- a/Behaver.cs +++ b/Behaver.cs @@ -8,6 +8,7 @@ using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; using System.Collections.Generic; +using vassago.ProtocolInterfaces.DiscordInterface; public class Behaver { diff --git a/ProtocolInterfaces/DiscordInterface/DiscordInterface.cs b/ProtocolInterfaces/DiscordInterface/DiscordInterface.cs index 05434f2..2487c20 100644 --- a/ProtocolInterfaces/DiscordInterface/DiscordInterface.cs +++ b/ProtocolInterfaces/DiscordInterface/DiscordInterface.cs @@ -70,7 +70,7 @@ public class DiscordInterface protocolAsChannel.DisplayName = "discord (itself)"; protocolAsChannel.SendMessage = (t) => { throw new InvalidOperationException($"protocol isn't a real channel, cannot accept text"); }; protocolAsChannel.SendFile = (f, t) => { throw new InvalidOperationException($"protocol isn't a real channel, cannot send file"); }; - protocolAsChannel= Rememberer.RememberChannel(protocolAsChannel); + protocolAsChannel = Rememberer.RememberChannel(protocolAsChannel); Console.WriteLine($"protocol as channel addeed; {protocolAsChannel}"); } finally @@ -115,7 +115,7 @@ public class DiscordInterface { await discordChannelSetup.WaitAsync(); - try + try { var selfAccount = UpsertAccount(client.CurrentUser, protocolAsChannel); selfAccount.DisplayName = client.CurrentUser.Username; @@ -129,7 +129,7 @@ public class DiscordInterface private async Task MessageReceived(SocketMessage messageParam) { - if(messageParam is not SocketUserMessage) + if (messageParam is not SocketUserMessage) { Console.WriteLine($"{messageParam.Content}, but not a user message"); return; @@ -271,7 +271,7 @@ public class DiscordInterface if (channel is IGuildChannel) { parentChannel = Rememberer.SearchChannel(c => c.ExternalId == (channel as IGuildChannel).Guild.Id.ToString() && c.Protocol == PROTOCOL); - if(parentChannel is null) + if (parentChannel is null) { Console.Error.WriteLine("why am I still null?"); } @@ -290,7 +290,7 @@ public class DiscordInterface c.SendMessage = (t) => { return channel.SendMessageAsync(t); }; c.SendFile = (f, t) => { return channel.SendFileAsync(f, t); }; - + return Rememberer.RememberChannel(c); } internal Channel UpsertChannel(IGuild channel) @@ -313,14 +313,19 @@ public class DiscordInterface c.SendMessage = (t) => { throw new InvalidOperationException($"channel {channel.Name} is guild; cannot accept text"); }; c.SendFile = (f, t) => { throw new InvalidOperationException($"channel {channel.Name} is guild; send file"); }; - + return Rememberer.RememberChannel(c); } internal static Account UpsertAccount(IUser user, Channel inChannel) { var acc = Rememberer.SearchAccount(ui => ui.ExternalId == user.Id.ToString() && ui.SeenInChannel.Id == inChannel.Id); - acc ??= new Account(); - + Console.WriteLine($"upserting account, retrieved {acc?.Id}."); + if (acc != null) + { + Console.WriteLine($"acc's user: {acc.IsUser?.Id}"); + } + acc ??= new Account() { IsUser = new User() }; + acc.Username = user.Username; acc.ExternalId = user.Id.ToString(); acc.IsBot = user.IsBot || user.IsWebhook; @@ -328,9 +333,18 @@ public class DiscordInterface acc.SeenInChannel = inChannel; acc.IsUser = Rememberer.SearchUser(u => u.Accounts.Any(a => a.ExternalId == acc.ExternalId && a.Protocol == acc.Protocol)); - acc.IsUser ??= new User() { Accounts = [ acc ] }; - inChannel.Users ??= []; - inChannel.Users.Add(acc); + + Console.WriteLine($"we asked rememberer to search for acc's user. {acc.IsUser?.Id}"); + if (acc.IsUser != null) + { + Console.WriteLine($"user has record of {acc.IsUser.Accounts?.Count ?? 0} accounts"); + } + acc.IsUser ??= new User() { Accounts = [acc] }; + if (inChannel.Users?.Count > 0) + { + Console.WriteLine($"channel has {inChannel.Users.Count} accounts"); + } + inChannel.Users ??= [acc]; Rememberer.RememberAccount(acc); return acc; } diff --git a/README.md b/README.md index ca9e08f..a2e53b5 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,16 @@ that's read messages/view channels, send messages, send messages in threads, and ## Data Types +database diagram. is a fancy term. + +message 1:n attachment +user 1:n account +channel 1:n account +channel 1:n message +account 1:n message + +featurepermission n:n ? + ### Accounts a `User` can have multiple `Account`s. e.g., @adam:greyn.club? that's an "account". I, however, am a `User`. An `Account` has references to the `Channels` its seen in - as in, leaf-level. If you're in a subchannel, you'll have an appropriate listing there - i.e., you will never have an account in "discord (itself)", you'll have one in the guild text-channels diff --git a/Rememberer.cs b/Rememberer.cs index 45c2a93..3371cc8 100644 --- a/Rememberer.cs +++ b/Rememberer.cs @@ -2,13 +2,14 @@ namespace vassago; using System.Linq.Expressions; using vassago.Models; +using Microsoft.EntityFrameworkCore; public static class Rememberer { private static readonly ChattingContext db = new(); public static Account SearchAccount(Expression> predicate) { - return (new ChattingContext()).Accounts.FirstOrDefault(predicate); + return (new ChattingContext()).Accounts.Include(a => a.IsUser).FirstOrDefault(predicate); } public static List SearchAccounts(Expression> predicate) { @@ -28,30 +29,30 @@ public static class Rememberer } public static User SearchUser(Expression> predicate) { - return (new ChattingContext()).Users.FirstOrDefault(predicate); + return (new ChattingContext()).Users.Include(u => u.Accounts).FirstOrDefault(predicate); } public static void RememberAccount(Account toRemember) { - db.Update(toRemember); + toRemember.IsUser ??= new User{ Accounts = [toRemember]}; + db.Update(toRemember.IsUser); db.SaveChanges(); } public static void RememberAttachment(Attachment toRemember) { - db.Update(toRemember); - + toRemember.Message ??= new Message() { Attachments = [toRemember]}; + db.Update(toRemember.Message); db.SaveChanges(); } public static Channel RememberChannel(Channel toRemember) { db.Update(toRemember); - db.SaveChanges(); return toRemember; } public static void RememberMessage(Message toRemember) { - db.Update(toRemember); - + toRemember.Channel ??= new (){ Messages = [toRemember] }; + db.Update(toRemember.Channel); db.SaveChanges(); } public static void RememberUser(User toRemember) @@ -66,4 +67,23 @@ public static class Rememberer db.SaveChanges(); } + public static List AccountsOverview() + { + return db.Accounts.ToList(); + } + public static List ChannelsOverview() + { + return db.Channels.Include(u => u.SubChannels).Include(c => c.ParentChannel).ToList(); + } + public static Channel ChannelDetail(Guid Id) + { + return db.Channels.Find(Id); + // .Include(u => u.SubChannels) + // .Include(u => u.Users) + // .Include(u => u.ParentChannel); + } + public static List UsersOverview() + { + return db.Users.ToList(); + } } \ No newline at end of file diff --git a/WebInterface/Controllers/ChannelsController.cs b/WebInterface/Controllers/ChannelsController.cs index 8e45a82..164208d 100644 --- a/WebInterface/Controllers/ChannelsController.cs +++ b/WebInterface/Controllers/ChannelsController.cs @@ -8,30 +8,26 @@ using vassago.WebInterface.Models; namespace vassago.WebInterface.Controllers; -public class ChannelsController(ChattingContext db) : Controller +public class ChannelsController() : Controller { - private ChattingContext Database => db; - public IActionResult Index() { - return Database.Channels != null ? - View(Database.Channels.Include(u => u.ParentChannel).ToList().OrderBy(c => c.LineageSummary)) : + var channels = Rememberer.ChannelsOverview(); + return channels != null ? + View(channels.OrderBy(c => c.LineageSummary)) : Problem("Entity set '_db.Channels' is null."); } public async Task Details(Guid id) { - if(Database.Channels == null) + var allChannels = Rememberer.ChannelsOverview(); + if(allChannels == null) return Problem("Entity set '_db.Channels' is null."); //"but adam", says the strawman, "why load *every* channel and walk your way up? surely there's a .Load command that works or something." //eh. I checked. Not really. You could make an SQL view that recurses its way up, meh idk how. You could just eagerly load *every* related object... //but that would take in all the messages. //realistically I expect this will have less than 1MB of total "channels", and several GB of total messages per (text) channel. - var AllChannels = await Database.Channels - .Include(u => u.SubChannels) - .Include(u => u.Users) - .Include(u => u.ParentChannel) - .ToListAsync(); - var channel = AllChannels.First(u => u.Id == id); + + var channel = allChannels.First(u => u.Id == id); var walker = channel; while(walker != null) { diff --git a/WebInterface/Controllers/HomeController.cs b/WebInterface/Controllers/HomeController.cs index 6515e07..b28e16e 100644 --- a/WebInterface/Controllers/HomeController.cs +++ b/WebInterface/Controllers/HomeController.cs @@ -12,24 +12,23 @@ namespace vassago.Controllers; public class HomeController : Controller { private readonly ILogger _logger; - private readonly ChattingContext _db; - public HomeController(ILogger logger, ChattingContext db) + public HomeController(ILogger logger) { _logger = logger; - _db = db; } public IActionResult Index() { - var allAccounts = _db.Accounts.ToList(); - var allChannels = _db.Channels.Include(c => c.Users).ToList(); + var allAccounts = Rememberer.AccountsOverview(); + var allChannels = Rememberer.ChannelsOverview(); + Console.WriteLine($"accounts: {allAccounts?.Count ?? 0}, channels: {allChannels?.Count ?? 0}"); var sb = new StringBuilder(); - sb.Append("["); + sb.Append('['); sb.Append("{text: \"channels\", nodes: ["); var first = true; - var topLevelChannels = _db.Channels.Where(x => x.ParentChannel == null); + var topLevelChannels = Rememberer.ChannelsOverview().Where(x => x.ParentChannel == null); foreach (var topLevelChannel in topLevelChannels) { if (first) @@ -85,13 +84,13 @@ public class HomeController : Controller } sb.Append("]}"); } - var users = _db.Users.ToList(); + var users = Rememberer.UsersOverview();// _db.Users.ToList(); if(users.Any()) { sb.Append(",{text: \"users\", nodes: ["); first=true; //refresh list; we'll be knocking them out again in serializeUser - allAccounts = _db.Accounts.ToList(); + allAccounts = Rememberer.AccountsOverview(); foreach(var user in users) { if (first) diff --git a/WebInterface/Controllers/api/ChannelsControler.cs b/WebInterface/Controllers/api/ChannelsControler.cs index 8487e64..4a6db9f 100644 --- a/WebInterface/Controllers/api/ChannelsControler.cs +++ b/WebInterface/Controllers/api/ChannelsControler.cs @@ -2,6 +2,7 @@ using System.Diagnostics; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using vassago.Models; +using vassago.ProtocolInterfaces.DiscordInterface; namespace vassago.Controllers.api;