double-adding of Accounts is sovled - but now it's double-adding Users.
Some checks failed
gitea.arg.rip/vassago/pipeline/head There was a failure building this commit

whyyyy
This commit is contained in:
adam 2025-03-06 17:02:33 -05:00
parent 18e8f0f36e
commit 0d3a56c8db
7 changed files with 81 additions and 40 deletions

View File

@ -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
{

View File

@ -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;
}

View File

@ -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

View File

@ -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<Func<Account, bool>> predicate)
{
return (new ChattingContext()).Accounts.FirstOrDefault(predicate);
return (new ChattingContext()).Accounts.Include(a => a.IsUser).FirstOrDefault(predicate);
}
public static List<Account> SearchAccounts(Expression<Func<Account, bool>> predicate)
{
@ -28,30 +29,30 @@ public static class Rememberer
}
public static User SearchUser(Expression<Func<User, bool>> 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<Account> AccountsOverview()
{
return db.Accounts.ToList();
}
public static List<Channel> 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<User> UsersOverview()
{
return db.Users.ToList();
}
}

View File

@ -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<IActionResult> 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)
{

View File

@ -12,24 +12,23 @@ namespace vassago.Controllers;
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
private readonly ChattingContext _db;
public HomeController(ILogger<HomeController> logger, ChattingContext db)
public HomeController(ILogger<HomeController> 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)

View File

@ -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;