Compare commits

..

No commits in common. "0d3a56c8db75bf36e76cc3d0fc77cb3f7a45d03a" and "c971add13727c3246b1ddbd4cc0a4ebb71f2e2f1" have entirely different histories.

7 changed files with 171 additions and 95 deletions

View File

@ -8,7 +8,6 @@ using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Collections.Generic; using System.Collections.Generic;
using vassago.ProtocolInterfaces.DiscordInterface;
public class Behaver public class Behaver
{ {

View File

@ -236,8 +236,8 @@ public class DiscordInterface
Channel c = Rememberer.SearchChannel(ci => ci.ExternalId == channel.Id.ToString() && ci.Protocol == PROTOCOL); Channel c = Rememberer.SearchChannel(ci => ci.ExternalId == channel.Id.ToString() && ci.Protocol == PROTOCOL);
if (c == null) if (c == null)
{ {
Console.WriteLine($"couldn't find channel under protocol {PROTOCOL} with externalId {channel.Id.ToString()}");
c = new Channel(); c = new Channel();
Console.WriteLine($"adding channel {channel.Name}");
} }
c.DisplayName = channel.Name; c.DisplayName = channel.Name;
@ -298,7 +298,6 @@ public class DiscordInterface
Channel c = Rememberer.SearchChannel(ci => ci.ExternalId == channel.Id.ToString() && ci.Protocol == PROTOCOL); Channel c = Rememberer.SearchChannel(ci => ci.ExternalId == channel.Id.ToString() && ci.Protocol == PROTOCOL);
if (c == null) if (c == null)
{ {
Console.WriteLine($"couldn't find channel under protocol {PROTOCOL} with externalId {channel.Id.ToString()}");
c = new Channel(); c = new Channel();
} }
@ -319,12 +318,7 @@ public class DiscordInterface
internal static Account UpsertAccount(IUser user, Channel inChannel) internal static Account UpsertAccount(IUser user, Channel inChannel)
{ {
var acc = Rememberer.SearchAccount(ui => ui.ExternalId == user.Id.ToString() && ui.SeenInChannel.Id == inChannel.Id); var acc = Rememberer.SearchAccount(ui => ui.ExternalId == user.Id.ToString() && ui.SeenInChannel.Id == inChannel.Id);
Console.WriteLine($"upserting account, retrieved {acc?.Id}."); acc ??= new Account();
if (acc != null)
{
Console.WriteLine($"acc's user: {acc.IsUser?.Id}");
}
acc ??= new Account() { IsUser = new User() };
acc.Username = user.Username; acc.Username = user.Username;
acc.ExternalId = user.Id.ToString(); acc.ExternalId = user.Id.ToString();
@ -333,18 +327,9 @@ public class DiscordInterface
acc.SeenInChannel = inChannel; acc.SeenInChannel = inChannel;
acc.IsUser = Rememberer.SearchUser(u => u.Accounts.Any(a => a.ExternalId == acc.ExternalId && a.Protocol == acc.Protocol)); acc.IsUser = Rememberer.SearchUser(u => u.Accounts.Any(a => a.ExternalId == acc.ExternalId && a.Protocol == acc.Protocol));
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 ] }; acc.IsUser ??= new User() { Accounts = [ acc ] };
if (inChannel.Users?.Count > 0) inChannel.Users ??= [];
{ inChannel.Users.Add(acc);
Console.WriteLine($"channel has {inChannel.Users.Count} accounts");
}
inChannel.Users ??= [acc];
Rememberer.RememberAccount(acc); Rememberer.RememberAccount(acc);
return acc; return acc;
} }

View File

@ -11,16 +11,6 @@ that's read messages/view channels, send messages, send messages in threads, and
## Data Types ## 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 ### 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 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,14 +2,13 @@ namespace vassago;
using System.Linq.Expressions; using System.Linq.Expressions;
using vassago.Models; using vassago.Models;
using Microsoft.EntityFrameworkCore;
public static class Rememberer public static class Rememberer
{ {
private static readonly ChattingContext db = new(); private static readonly SemaphoreSlim dbAccessSemaphore = new(1, 1);
public static Account SearchAccount(Expression<Func<Account, bool>> predicate) public static Account SearchAccount(Expression<Func<Account, bool>> predicate)
{ {
return (new ChattingContext()).Accounts.Include(a => a.IsUser).FirstOrDefault(predicate); return (new ChattingContext()).Accounts.FirstOrDefault(predicate);
} }
public static List<Account> SearchAccounts(Expression<Func<Account, bool>> predicate) public static List<Account> SearchAccounts(Expression<Func<Account, bool>> predicate)
{ {
@ -29,61 +28,160 @@ public static class Rememberer
} }
public static User SearchUser(Expression<Func<User, bool>> predicate) public static User SearchUser(Expression<Func<User, bool>> predicate)
{ {
return (new ChattingContext()).Users.Include(u => u.Accounts).FirstOrDefault(predicate); return (new ChattingContext()).Users.FirstOrDefault(predicate);
} }
public static void RememberAccount(Account toRemember) public static void RememberAccount(Account toRemember)
{ {
toRemember.IsUser ??= new User{ Accounts = [toRemember]}; dbAccessSemaphore.Wait();
db.Update(toRemember.IsUser); try
{
var db = new ChattingContext();
if (toRemember.Id == Guid.Empty)
{
var parentChannel = toRemember.SeenInChannel;
var isUser = toRemember.IsUser;
toRemember.SeenInChannel = null;
toRemember.IsUser = null;
db.Accounts.Add(toRemember);
db.SaveChanges(); db.SaveChanges();
toRemember.SeenInChannel = parentChannel;
toRemember.IsUser = isUser;
db.SaveChanges();
}
else
{
db.SaveChanges();
}
}
finally
{
dbAccessSemaphore.Release();
}
} }
public static void RememberAttachment(Attachment toRemember) public static void RememberAttachment(Attachment toRemember)
{ {
toRemember.Message ??= new Message() { Attachments = [toRemember]}; dbAccessSemaphore.Wait();
db.Update(toRemember.Message); try
{
var db = new ChattingContext();
if (toRemember.Id == Guid.Empty)
{
var msg = toRemember.Message;
toRemember.Message = null;
db.Attachments.Add(toRemember);
db.SaveChanges(); db.SaveChanges();
toRemember.Message = msg;
db.SaveChanges();
}
else
{
db.SaveChanges();
}
}
finally
{
dbAccessSemaphore.Release();
}
} }
public static Channel RememberChannel(Channel toRemember) public static Channel RememberChannel(Channel toRemember)
{ {
db.Update(toRemember); dbAccessSemaphore.Wait();
try
{
var db = new ChattingContext();
if (toRemember.Id == Guid.Empty)
{
var parent = toRemember.ParentChannel;
var subChannesl = toRemember.SubChannels;
var msgs = toRemember.Messages;
var accounts = toRemember.Users;
toRemember.ParentChannel = null;
toRemember.SubChannels = null;
toRemember.Messages = null;
toRemember.Users = null;
db.Channels.Add(toRemember);
db.SaveChanges(); db.SaveChanges();
toRemember.ParentChannel = parent;
toRemember.SubChannels = subChannesl;
toRemember.Messages = msgs;
toRemember.Users = accounts;
db.SaveChanges();
}
db.SaveChanges();
}
finally
{
dbAccessSemaphore.Release();
}
return toRemember; return toRemember;
} }
public static void RememberMessage(Message toRemember) public static void RememberMessage(Message toRemember)
{ {
toRemember.Channel ??= new (){ Messages = [toRemember] }; dbAccessSemaphore.Wait();
db.Update(toRemember.Channel); try
{
var db = new ChattingContext();
if (toRemember.Id == Guid.Empty)
{
var author = toRemember.Author;
var channel = toRemember.Channel;
var attachments = toRemember.Attachments;
toRemember.Author = null;
toRemember.Channel = null;
toRemember.Attachments = null;
db.Messages.Add(toRemember);
db.SaveChanges(); db.SaveChanges();
toRemember.Author = author;
toRemember.Channel = channel;
toRemember.Attachments = attachments;
db.SaveChanges();
}
db.SaveChanges();
}
finally
{
dbAccessSemaphore.Release();
}
} }
public static void RememberUser(User toRemember) public static void RememberUser(User toRemember)
{ {
db.Users.Update(toRemember); dbAccessSemaphore.Wait();
try
{
var db = new ChattingContext();
if (toRemember.Id == Guid.Empty)
{
var accs = toRemember.Accounts;
toRemember.Accounts = null;
db.Users.Add(toRemember);
db.SaveChanges(); db.SaveChanges();
toRemember.Accounts = accs;
db.SaveChanges();
}
else
{
db.SaveChanges();
}
}
finally
{
dbAccessSemaphore.Release();
}
} }
public static void ForgetUser(User toForget) public static void ForgetUser(User toForget)
{ {
dbAccessSemaphore.Wait();
try
{
var db = new ChattingContext();
db.Users.Remove(toForget); db.Users.Remove(toForget);
db.SaveChanges(); db.SaveChanges();
} }
public static List<Account> AccountsOverview() finally
{ {
return db.Accounts.ToList(); dbAccessSemaphore.Release();
} }
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,26 +8,30 @@ using vassago.WebInterface.Models;
namespace vassago.WebInterface.Controllers; namespace vassago.WebInterface.Controllers;
public class ChannelsController() : Controller public class ChannelsController(ChattingContext db) : Controller
{ {
private ChattingContext Database => db;
public IActionResult Index() public IActionResult Index()
{ {
var channels = Rememberer.ChannelsOverview(); return Database.Channels != null ?
return channels != null ? View(Database.Channels.Include(u => u.ParentChannel).ToList().OrderBy(c => c.LineageSummary)) :
View(channels.OrderBy(c => c.LineageSummary)) :
Problem("Entity set '_db.Channels' is null."); Problem("Entity set '_db.Channels' is null.");
} }
public async Task<IActionResult> Details(Guid id) public async Task<IActionResult> Details(Guid id)
{ {
var allChannels = Rememberer.ChannelsOverview(); if(Database.Channels == null)
if(allChannels == null)
return Problem("Entity set '_db.Channels' is 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." //"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... //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. //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. //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
var channel = allChannels.First(u => u.Id == id); .Include(u => u.SubChannels)
.Include(u => u.Users)
.Include(u => u.ParentChannel)
.ToListAsync();
var channel = AllChannels.First(u => u.Id == id);
var walker = channel; var walker = channel;
while(walker != null) while(walker != null)
{ {

View File

@ -12,23 +12,24 @@ namespace vassago.Controllers;
public class HomeController : Controller public class HomeController : Controller
{ {
private readonly ILogger<HomeController> _logger; private readonly ILogger<HomeController> _logger;
private readonly ChattingContext _db;
public HomeController(ILogger<HomeController> logger) public HomeController(ILogger<HomeController> logger, ChattingContext db)
{ {
_logger = logger; _logger = logger;
_db = db;
} }
public IActionResult Index() public IActionResult Index()
{ {
var allAccounts = Rememberer.AccountsOverview(); var allAccounts = _db.Accounts.ToList();
var allChannels = Rememberer.ChannelsOverview(); var allChannels = _db.Channels.Include(c => c.Users).ToList();
Console.WriteLine($"accounts: {allAccounts?.Count ?? 0}, channels: {allChannels?.Count ?? 0}");
var sb = new StringBuilder(); var sb = new StringBuilder();
sb.Append('['); sb.Append("[");
sb.Append("{text: \"channels\", nodes: ["); sb.Append("{text: \"channels\", nodes: [");
var first = true; var first = true;
var topLevelChannels = Rememberer.ChannelsOverview().Where(x => x.ParentChannel == null); var topLevelChannels = _db.Channels.Where(x => x.ParentChannel == null);
foreach (var topLevelChannel in topLevelChannels) foreach (var topLevelChannel in topLevelChannels)
{ {
if (first) if (first)
@ -84,13 +85,13 @@ public class HomeController : Controller
} }
sb.Append("]}"); sb.Append("]}");
} }
var users = Rememberer.UsersOverview();// _db.Users.ToList(); var users = _db.Users.ToList();
if(users.Any()) if(users.Any())
{ {
sb.Append(",{text: \"users\", nodes: ["); sb.Append(",{text: \"users\", nodes: [");
first=true; first=true;
//refresh list; we'll be knocking them out again in serializeUser //refresh list; we'll be knocking them out again in serializeUser
allAccounts = Rememberer.AccountsOverview(); allAccounts = _db.Accounts.ToList();
foreach(var user in users) foreach(var user in users)
{ {
if (first) if (first)

View File

@ -2,7 +2,6 @@ using System.Diagnostics;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using vassago.Models; using vassago.Models;
using vassago.ProtocolInterfaces.DiscordInterface;
namespace vassago.Controllers.api; namespace vassago.Controllers.api;