Compare commits

..

No commits in common. "master" and "test-release-2" have entirely different histories.

182 changed files with 123 additions and 87308 deletions

View File

@ -1,12 +0,0 @@
{
"version": 1,
"isRoot": true,
"tools": {
"dotnet-ef": {
"version": "7.0.5",
"commands": [
"dotnet-ef"
]
}
}
}

6
.gitignore vendored
View File

@ -1,7 +1,4 @@
appsettings.Development.json appsettings.json
assets/exchangepairs.json
fail*/
.projectile
# ---> VisualStudio # ---> VisualStudio
## Ignore Visual Studio temporary files, build results, and ## Ignore Visual Studio temporary files, build results, and
@ -378,4 +375,3 @@ FodyWeavers.xsd
# Local History for Visual Studio Code # Local History for Visual Studio Code
.history/ .history/
tmp/

View File

@ -1,4 +0,0 @@
*.min.css
*.min.js
*.map
wwwroot/lib/

19
.vscode/launch.json vendored
View File

@ -5,26 +5,17 @@
// Use IntelliSense to find out which attributes exist for C# debugging // Use IntelliSense to find out which attributes exist for C# debugging
// Use hover for the description of the existing attributes // Use hover for the description of the existing attributes
// For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
"name": ".NET Core Launch (web)", "name": ".NET Core Launch (console)",
"type": "coreclr", "type": "coreclr",
"request": "launch", "request": "launch",
"preLaunchTask": "build", "preLaunchTask": "build",
// If you have changed target frameworks, make sure to update the program path. // If you have changed target frameworks, make sure to update the program path.
"program": "${workspaceFolder}/bin/Debug/net9.0/vassago.dll", "program": "${workspaceFolder}/bin/Debug/net5.0/silverworker-discord.dll",
"args": [], "args": [],
"cwd": "${workspaceFolder}", "cwd": "${workspaceFolder}",
"stopAtEntry": false, // For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
// Enable launching a web browser when ASP.NET Core starts. For more information: https://aka.ms/VSCode-CS-LaunchJson-WebBrowser "console": "internalConsole",
"serverReadyAction": { "stopAtEntry": false
"action": "openExternally",
"pattern": "\\bNow listening on:\\s+(https?://\\S+)"
},
"env": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"sourceFileMap": {
"/Views": "${workspaceFolder}/Views"
}
}, },
{ {
"name": ".NET Core Attach", "name": ".NET Core Attach",

9
.vscode/tasks.json vendored
View File

@ -7,7 +7,7 @@
"type": "process", "type": "process",
"args": [ "args": [
"build", "build",
"${workspaceFolder}/vassago.csproj", "${workspaceFolder}/silverworker-discord.csproj",
"/property:GenerateFullPaths=true", "/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary" "/consoleloggerparameters:NoSummary"
], ],
@ -19,7 +19,7 @@
"type": "process", "type": "process",
"args": [ "args": [
"publish", "publish",
"${workspaceFolder}/vassago.csproj", "${workspaceFolder}/silverworker-discord.csproj",
"/property:GenerateFullPaths=true", "/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary" "/consoleloggerparameters:NoSummary"
], ],
@ -32,8 +32,9 @@
"args": [ "args": [
"watch", "watch",
"run", "run",
"--project", "${workspaceFolder}/silverworker-discord.csproj",
"${workspaceFolder}/vassago.csproj" "/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
], ],
"problemMatcher": "$msCompile" "problemMatcher": "$msCompile"
} }

View File

@ -1,251 +0,0 @@
namespace vassago;
using gray_messages.chat;
using franz;
using vassago.Behavior;
using vassago.Models;
using vassago.ProtocolInterfaces;
using System;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Collections.Generic;
using vassago.ProtocolInterfaces.DiscordInterface;
public class Behaver
{
private List<Account> SelfAccounts { get; set; } = new List<Account>();
private User SelfUser { get; set; }
public static List<vassago.Behavior.Behavior> Behaviors { get; private set; } = new List<vassago.Behavior.Behavior>();
internal Behaver()
{
var subtypes = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(domainAssembly => domainAssembly.GetTypes())
.Where(type => type.IsSubclassOf(typeof(vassago.Behavior.Behavior)) && !type.IsAbstract &&
type.GetCustomAttributes(typeof(StaticPlzAttribute), false)?.Any() == true)
.ToList();
foreach (var subtype in subtypes)
{
Behaviors.Add((vassago.Behavior.Behavior)Activator.CreateInstance(subtype));
}
}
static Behaver() { }
private static readonly Behaver _instance = new Behaver();
//TODO: you know why I didn't make this a static class? lifecycle issues with the dbcontext. but now that we don't have a stored instance,
//no need to have a... *checks over shoulder*... *whispers*: singleton
public static Behaver Instance
{
get { return _instance; }
}
public async Task<bool> ActOn(Message message)
{
//TODO: this is yet another hit to the database, and a big one. cache them in memory! there needs to be a feasibly-viewable amount, anyway.
var matchingUACs = Rememberer.MatchUACs(message);
message.TranslatedContent = message.Content;
foreach (var uacMatch in matchingUACs)
{
uacMatch.Translations ??= [];
uacMatch.CommandAlterations ??= [];
foreach (var localization in uacMatch.Translations) //honestly, i'm *still* mad that foreach thing in null is an exception. in what world is "if not null then" assumed?
{
var r = new Regex(localization.Key);
message.TranslatedContent = r.Replace(message.TranslatedContent, localization.Value);
}
}
var behaviorsActedOn = new List<string>();
foreach (var behavior in Behaviors.ToList())
{
if (!behavior.ShouldAct(message, matchingUACs))
{
continue;
}
behavior.ActOn(message);
message.ActedOn = true;
behaviorsActedOn.Add(behavior.ToString());
Console.WriteLine("acted on, moving forward");
}
if (message.ActedOn == false && message.MentionsMe && message.TranslatedContent.Contains('?') && !Behaver.Instance.SelfAccounts.Any(acc => acc.Id == message.Author.Id))
{
Console.WriteLine("providing bullshit nonanswer / admitting uselessness");
var responses = new List<string>(){
@"Well, that's a great question, and there are certainly many different possible answers. Ultimately, the decision will depend on a variety of factors, including your personal interests and goals, as well as any practical considerations (like the economy). I encourage you to do your research, speak with experts and educators, and explore your options before making a decision that's right for you.",
@"┐(゚ ~゚ )┌", @"¯\_(ツ)_/¯", @"╮ (. ❛ ᴗ ❛.) ╭", @"╮(╯ _╰ )╭"
};
Behaver.Instance.SendMessage(message.Channel.Id, responses[Shared.r.Next(responses.Count)]);
message.ActedOn = true;
behaviorsActedOn.Add("generic question fallback");
}
Rememberer.RememberMessage(message);
ForwardToKafka(message, behaviorsActedOn, matchingUACs);
return message.ActedOn;
}
internal void ForwardToKafka(Message message, List<string> actedOnBy, List<UAC> matchingUACs)
{
var kafkaesque = new chat_message()
{
Api_Uri = Shared.API_URL,
MessageId = message.Id,
Content = message.TranslatedContent,
RawContent = message.Content,
MentionsMe = message.MentionsMe,
Timestamp = message.Timestamp,
AttachmentCount = (uint)(message.Attachments?.Count() ?? 0),
AccountId = message.Author.Id,
AccountName = message.Author.DisplayName,
UserId = message.Author.IsUser.Id,
UserName = message.Author.IsUser.DisplayName,
ChannelId = message.Channel.Id,
ChannelName = message.Channel.DisplayName,
ChannelProtoocl = message.Channel.Protocol,
UAC_Matches = matchingUACs.Select(uac => uac.Id).ToList(),
BehavedOnBy = actedOnBy
};
Console.WriteLine("producing message");
Telefranz.Instance.ProduceMessage(kafkaesque);
Console.WriteLine("survived producing message");
}
internal bool IsSelf(Guid AccountId)
{
var acc = Rememberer.SearchAccount(a => a.Id == AccountId);
return SelfAccounts.Any(acc => acc.Id == AccountId);
}
public void MarkSelf(Account selfAccount)
{
if (SelfUser == null)
{
SelfUser = selfAccount.IsUser;
}
else if (SelfUser != selfAccount.IsUser)
{
CollapseUsers(SelfUser, selfAccount.IsUser);
}
SelfAccounts = Rememberer.SearchAccounts(a => a.IsUser == SelfUser);
Rememberer.RememberAccount(selfAccount);
}
public bool CollapseUsers(User primary, User secondary)
{
if (primary.Accounts == null)
primary.Accounts = new List<Account>();
if (secondary.Accounts != null)
primary.Accounts.AddRange(secondary.Accounts);
foreach (var a in secondary.Accounts)
{
a.IsUser = primary;
}
secondary.Accounts.Clear();
var uacs = Rememberer.SearchUACs(u => u.Users.FirstOrDefault(u => u.Id == secondary.Id) != null);
if (uacs.Count() > 0)
{
foreach (var uac in uacs)
{
uac.Users.RemoveAll(u => u.Id == secondary.Id);
uac.Users.Add(primary);
Rememberer.RememberUAC(uac);
}
}
Rememberer.ForgetUser(secondary);
Rememberer.RememberUser(primary);
return true;
}
private ProtocolInterface fetchInterface(Channel ch)
{
var walkUp = ch;
while (walkUp.ParentChannel != null)
{
walkUp = walkUp.ParentChannel;
}
foreach (var iproto in Shared.ProtocolList)
{
if (iproto.SelfChannel.Id == walkUp.Id)
return iproto;
}
return null;
}
public async Task<int> SendMessage(Guid channelId, string text)
{
var channel = Rememberer.ChannelDetail(channelId);
if (channel == null)
return 404;
var iprotocol = fetchInterface(channel);
if (iprotocol == null)
return 404;
return await iprotocol.SendMessage(channel, text);
}
public async Task<int> React(Guid messageId, string reaction)
{
Console.WriteLine($"sanity check: behaver is reacting, {messageId}, {reaction}");
var message = Rememberer.MessageDetail(messageId);
if (message == null)
{
Console.Error.WriteLine($"message {messageId} not found");
return 404;
}
Console.WriteLine($"sanity check: message found.");
if (message.Channel == null)
{
Console.Error.WriteLine($"react is going to fail because message {messageId} has no Channel");
}
Console.WriteLine($"sanity check: message has a channel.");
var iprotocol = fetchInterface(message.Channel);
if (iprotocol == null)
{
Console.WriteLine($"couldn't find protocol for {message.Channel?.Id}");
return 404;
}
Console.WriteLine("I remember this message, i have found a protocol, i am ready to react toit");
return await iprotocol.React(message, reaction);
}
public async Task<int> Reply(Guid messageId, string text)
{
var message = Rememberer.MessageDetail(messageId);
if (message == null)
{
Console.WriteLine($"message {messageId} not found");
return 404;
}
var iprotocol = fetchInterface(message.Channel);
if (iprotocol == null)
{
Console.WriteLine($"couldn't find protocol for {message.Channel.Id}");
return 404;
}
return await iprotocol.Reply(message, text);
}
public async Task<int> SendFile(Guid channelId, string path, string accompanyingText)
{
var channel = Rememberer.ChannelDetail(channelId);
if (channel == null)
return 404;
var iprotocol = fetchInterface(channel);
if (iprotocol == null)
return 404;
return await iprotocol.SendFile(channel, path, accompanyingText);
}
public async Task<int> SendFile(Guid channelId, string base64dData, string filename, string accompanyingText)
{
var channel = Rememberer.ChannelDetail(channelId);
if (channel == null)
return 404;
var iprotocol = fetchInterface(channel);
if (iprotocol == null)
return 404;
return await iprotocol.SendFile(channel, base64dData, filename, accompanyingText);
}
}

View File

@ -1,40 +0,0 @@
namespace vassago.Behavior;
using vassago.Models;
using System;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Collections.Generic;
public abstract class Behavior
{
//recommendation: set up your UACs in your constructor.
public abstract Task<bool> ActOn(Message message);
public virtual bool ShouldAct(Message message, List<UAC> matchedUACs)
{
if(Behaver.Instance.IsSelf(message.Author.Id))
return false;
var triggerTarget = Trigger ;
foreach(var uacMatch in matchedUACs)
{
foreach(var substitution in uacMatch.CommandAlterations)
{
triggerTarget = new Regex(substitution.Key).Replace(triggerTarget, substitution.Value);
}
}
return Regex.IsMatch(message.TranslatedContent, $"{triggerTarget}\\b", RegexOptions.IgnoreCase);
}
public abstract string Name { get; }
public abstract string Trigger { get; }
public virtual string Description => Name;
}
///<summary>
///the behavior should be static. I.e., we make one at the start and it's ready to check and go for the whole lifetime.
///As opposed to LaughAtOwnJoke, which only needs to be created to wait for 1 punchline one time.
///</summary>
public class StaticPlzAttribute : Attribute {}

View File

@ -1,25 +0,0 @@
namespace vassago.Behavior;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using vassago.Models;
[StaticPlz]
public class ChatGPTSnark : Behavior
{
public override string Name => "ChatGPTSnark";
public override string Trigger => "chatgpt";
public override string Description => "snarkiness about the latest culty-fixation in ai";
public override async Task<bool> ActOn(Message message)
{
Behaver.Instance.SendMessage(message.Channel.Id, "chatGPT is **weak**. also, are we done comparing every little if-then-else to skynet?");
return true;
}
}

View File

@ -1,34 +0,0 @@
namespace vassago.Behavior;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using vassago.Models;
using static vassago.Models.Enumerations;
[StaticPlz]
public class DefinitionSnarkCogDiss : Behavior
{
public override string Name => "Definition Snarkiness: cognitivie dissonance";
public override string Trigger => "\\bcognitive dissonance";
public override string Description => "snarkiness about the rampant misuse of the term cognitive dissonance";
public override bool ShouldAct(Message message, List<UAC> matchedUACs)
{
if((MeannessFilterLevel)message.Channel.EffectivePermissions.MeannessFilterLevel < MeannessFilterLevel.Medium)
return false;
return base.ShouldAct(message, matchedUACs);
}
public override async Task<bool> ActOn(Message message)
{
Behaver.Instance.SendMessage(message.Channel.Id, "that's not what cognitive dissonance means. Did you mean \"hypocrisy\"?");
return true;
}
}

View File

@ -1,34 +0,0 @@
namespace vassago.Behavior;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using vassago.Models;
using static vassago.Models.Enumerations;
[StaticPlz]
public class DefinitionSnarkGaslight : Behavior
{
public override string Name => "Definition Snarkiness: gaslighting";
public override string Trigger => "\\bgaslight(ing)?";
public override string Description => "snarkiness about the rampant misuse of the term gaslighting";
public override bool ShouldAct(Message message, List<UAC> matchedUACs)
{
if((MeannessFilterLevel)message.Channel.EffectivePermissions.MeannessFilterLevel < MeannessFilterLevel.Unrestricted)
return false;
return base.ShouldAct(message, matchedUACs);
}
public override async Task<bool> ActOn(Message message)
{
Behaver.Instance.SendMessage(message.Channel.Id, "that's not what gaslight means. Did you mean \"deceive\"?");
return true;
}
}

View File

@ -1,112 +0,0 @@
namespace vassago.Behavior;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using vassago.Models;
[StaticPlz]
public class Detiktokify : Behavior
{
public override string Name { get => "Detiktokify"; }
public override string Trigger { get => "post a link below vm.tiktok.com"; }
public override string Description { get => "re-host tiktok content"; }
private List<Uri> tiktokLinks = new List<Uri>();
private YoutubeDLSharp.YoutubeDL ytdl;
public Detiktokify()
{
ytdl = new YoutubeDLSharp.YoutubeDL();
ytdl.YoutubeDLPath = "yt-dlp";
ytdl.FFmpegPath = "ffmpeg";
ytdl.OutputFolder = "";
ytdl.OutputFileTemplate = "tiktokbad.%(ext)s";
}
public override bool ShouldAct(Message message, List<UAC> matchedUACs)
{
if (Behaver.Instance.IsSelf(message.Author.Id))
return false;
if (message.Channel.EffectivePermissions.MaxAttachmentBytes == 0)
return false;
var wordLikes = message.TranslatedContent.Split(' ', StringSplitOptions.TrimEntries);
var possibleLinks = wordLikes?.Where(wl => Uri.IsWellFormedUriString(wl, UriKind.Absolute)).Select(wl => new Uri(wl));
if (possibleLinks != null && possibleLinks.Count() > 0)
{
foreach (var link in possibleLinks)
{
if (link.Host.EndsWith(".tiktok.com"))
{
tiktokLinks.Add(link);
}
}
}
if (tiktokLinks.Any())
{
Console.WriteLine($"Should Act on message id {message.ExternalId}; with content {message.TranslatedContent}");
}
return tiktokLinks.Any();
}
public override async Task<bool> ActOn(Message message)
{
foreach (var link in tiktokLinks)
{
tiktokLinks.Remove(link);
try
{
Console.WriteLine($"detiktokifying {link}");
var res = await ytdl.RunVideoDownload(link.ToString());
if (!res.Success)
{
Console.Error.WriteLine("tried to dl, failed. \n" + string.Join('\n', res.ErrorOutput));
Behaver.Instance.SendMessage(message.Channel.Id, "tried to dl, failed. \n");
Behaver.Instance.React(message.Channel.Id, "problemon");
}
else
{
string path = res.Data;
if (File.Exists(path))
{
ulong bytesize = (ulong)((new System.IO.FileInfo(path)).Length);
if (bytesize < message.Channel.EffectivePermissions.MaxAttachmentBytes - 256)
{
try
{
Behaver.Instance.SendFile(message.Channel.Id, path, null);
}
catch (Exception e)
{
System.Console.Error.WriteLine(e);
Behaver.Instance.SendMessage(message.Channel.Id, $"aaaadam!\n{e}");
}
}
else
{
message.ActedOn = true;
Console.WriteLine($"file appears too big ({bytesize} bytes ({bytesize / (1024 * 1024)}MB)), not posting");
}
File.Delete(path);
}
else
{
Console.Error.WriteLine("idgi but something happened.");
Behaver.Instance.React(message.Id, "problemon");
}
}
}
catch (Exception e)
{
Console.Error.WriteLine(e);
Behaver.Instance.React(message.Id, "problemon");
return false;
}
}
return true;
}
}

View File

@ -1,87 +0,0 @@
namespace vassago.Behavior;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Newtonsoft.Json;
using vassago.Models;
[StaticPlz]
public class FiximageHeic : Behavior
{
public override string Name => "deheic";
public override string Trigger => "post an heic image";
public override string Description => "convert heic images to jpg";
private List<Attachment> heics = new List<Attachment>();
public override bool ShouldAct(Message message, List<UAC> matchedUACs)
{
if(Behaver.Instance.IsSelf(message.Author.Id))
return false;
if (message.Attachments?.Count() > 0)
{
foreach (var att in message.Attachments)
{
if (att.Filename?.EndsWith(".heic") == true)
{
heics.Add(att);
}
}
}
return heics.Any();
}
public override async Task<bool> ActOn(Message message)
{
if (!Directory.Exists("tmp"))
{
Directory.CreateDirectory("tmp");
}
var conversions = new List<Task<bool>>();
foreach (var att in heics)
{
conversions.Add(actualDeheic(att, message));
}
Task.WaitAll(conversions.ToArray());
Behaver.Instance.React(message.Id, "\U0001F34F");
return true;
}
private async Task<bool> actualDeheic(Attachment att, Message message)
{
try
{
var cancellationTokenSource = new CancellationTokenSource();
CancellationToken token = cancellationTokenSource.Token;
using (Stream output = File.OpenWrite("tmp/" + att.Filename))
{
(await Shared.HttpClient.GetAsync(att.Source))
.Content.CopyTo(output, null, token);
}
if (ExternalProcess.GoPlz("convert", $"tmp/{att.Filename} tmp/{att.Filename}.jpg"))
{
Behaver.Instance.SendFile(message.Channel.Id, $"tmp/{att.Filename}.jpg", "converted from jpeg-but-apple to jpeg");
File.Delete($"tmp/{att.Filename}");
File.Delete($"tmp/{att.Filename}.jpg");
}
else
{
Behaver.Instance.SendMessage(message.Channel.Id, "convert failed :(");
Console.Error.WriteLine("convert failed :(");
}
}
catch (Exception e)
{
Behaver.Instance.SendMessage(message.Channel.Id, $"something failed. aaaadam! {JsonConvert.SerializeObject(e, Formatting.Indented)}");
Console.Error.WriteLine(JsonConvert.SerializeObject(e, Formatting.Indented));
return false;
}
return true;
}
}

View File

@ -1,50 +0,0 @@
namespace vassago.Behavior;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using Newtonsoft.Json;
using vassago.Models;
using static vassago.Models.Enumerations;
[StaticPlz]
public class GeneralSnarkCloudNative : Behavior
{
public override string Name => "general snarkiness: cloud native";
public override string Trigger => "certain tech buzzwords that no human uses in normal conversation";
public override bool ShouldAct(Message message, List<UAC> matchedUACs)
{
if (Behaver.Instance.IsSelf(message.Author.Id))
return false;
if (!message.Channel.EffectivePermissions.ReactionsPossible)
return false;
if ((MeannessFilterLevel)message.Channel.EffectivePermissions.MeannessFilterLevel < MeannessFilterLevel.Medium)
return false;
return Regex.IsMatch(message.TranslatedContent, "\\bcloud( |-)?native\\b", RegexOptions.IgnoreCase) ||
Regex.IsMatch(message.TranslatedContent, "\\benterprise( |-)?(level|solution)\\b", RegexOptions.IgnoreCase);
}
public override async Task<bool> ActOn(Message message)
{
switch (Shared.r.Next(2))
{
case 0:
Behaver.Instance.React(message.Id, "\uD83E\uDD2E"); //vomit emoji
break;
case 1:
Behaver.Instance.React(message.Id, "\uD83C\uDDE7"); //B emoji
Behaver.Instance.React(message.Id, "\uD83C\uDDE6"); //A
Behaver.Instance.React(message.Id, "\uD83C\uDDF3"); //N
break;
}
return true;
}
}

View File

@ -1,64 +0,0 @@
namespace vassago.Behavior;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Text.RegularExpressions;
using vassago.Models;
[StaticPlz]
public class GeneralSnarkGooglit : Behavior
{
public override string Name => "Google-it Snarkiness";
public override string Trigger => "\"just google it\"";
public override string Description => "snarkiness about how research is not a solved problem";
public override bool ShouldAct(Message message, List<UAC> matchedUACs)
{
if (Behaver.Instance.IsSelf(message.Author.Id))
return false;
return Regex.IsMatch(message.TranslatedContent, $"(just )?google( (it|that|things|before))\\b", RegexOptions.IgnoreCase);
}
public override async Task<bool> ActOn(Message message)
{
switch (Shared.r.Next(4))
{
default:
Behaver.Instance.SendMessage(message.Channel.Id, "yeah no shit, obviously that resulted in nothing");
break;
case 1:
var results = "";
switch (Shared.r.Next(4))
{
default:
results = "\"curious about the best <THING> in <CURRENT YEAR>? click here to find out\", then i clicked there to find out. They didn't know either.";
break;
case 1:
results = "\"[SOLVED] <THING> (<CURRENT MONTH UPDATE>)\", then i clicked to see the solution. There wasn't one.";
break;
case 2:
results = "the one that had a paragraph that restated the question but badly, a paragraph to give a wrong history on the question, a paragraph with amazon affiliate links, a pargraph that said \"ultimately you have to answer it for yourself\", then had a paragraph telling me to give Engagement for The Algorithm";
break;
case 3:
results = "the one that had a paragraph that restated the question but badly, a paragraph to give a wrong history on the question, a paragraph with amazon affiliate links, a pargraph that said \"ultimately you should do your own research\", then had a paragraph telling me to give Engagement for The Algorithm";
break;
}
Behaver.Instance.SendMessage(message.Channel.Id, "oh here, I memorized the results. My favorite is " + results);
break;
case 2:
Behaver.Instance.SendMessage(message.Channel.Id, "Obviously that was already tried. Obviously it failed. If you ever tried to learn anything you'd know that's how it works.");
break;
case 3:
Behaver.Instance.SendMessage(message.Channel.Id, "\"mnyehh JuSt GoOgLe It\" when's the last time you tried to research anything? Have you ever?");
break;
}
return true;
}
}

View File

@ -1,62 +0,0 @@
namespace vassago.Behavior;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using vassago.Models;
using static vassago.Models.Enumerations;
[StaticPlz]
public class GeneralSnarkMisspellDefinitely : Behavior
{
public override string Name => "Snarkiness: misspell definitely";
public override string Trigger => "definitely but not";
public override string Description => "https://xkcd.com/2871/";
private Dictionary<string, string> snarkmap = new Dictionary<string, string>()
{
{"definetly", "*almost* definitely"},
{"definately", "probably"},
{"definatly", "probably not"},
{"defenitely", "not telling (it's a surprise)"},
{"defintely", "per the propheecy"},
{"definetely", "definitely, maybe"},
{"definantly", "to be decided by coin toss"},
{"defanitely", "in one universe out of 14 million"},
{"defineatly", "only the gods know"},
{"definitly", "unless someone cute shows up"}
};
public override bool ShouldAct(Message message, List<UAC> matchedUACs)
{
if(Behaver.Instance.IsSelf(message.Author.Id))
return false;
// if((MeannessFilterLevel)message.Channel.EffectivePermissions.MeannessFilterLevel < MeannessFilterLevel.Medium)
// return false;
foreach(var k in snarkmap.Keys)
{
if( Regex.IsMatch(message.TranslatedContent?.ToLower(), "\\b"+k+"\\b", RegexOptions.IgnoreCase))
return true;
}
return false;
}
public override async Task<bool> ActOn(Message message)
{
foreach(var k in snarkmap.Keys)
{
if( Regex.IsMatch(message.TranslatedContent, "\\b"+k+"\\b", RegexOptions.IgnoreCase))
{
Behaver.Instance.Reply(message.Id, k + "? so... " + snarkmap[k] + "?");
return true;
}
}
return true;
}
}

View File

@ -1,38 +0,0 @@
namespace vassago.Behavior;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using vassago.Models;
using static vassago.Models.Enumerations;
[StaticPlz]
public class GeneralSnarkPlaying : Behavior
{
public override string Name => "playin Snarkiness";
public override string Trigger => "he thinks i'm playin";
public override string Description => "I didn't think you were playing, but now I do";
public override bool ShouldAct(Message message, List<UAC> matchedUACs)
{
if(Behaver.Instance.IsSelf(message.Author.Id))
return false;
if((MeannessFilterLevel)message.Channel.EffectivePermissions.MeannessFilterLevel < MeannessFilterLevel.Medium ||
(LewdnessFilterLevel)message.Channel.EffectivePermissions.LewdnessFilterLevel < LewdnessFilterLevel.Moderate)
return false;
return Regex.IsMatch(message.TranslatedContent, "^(s?he|(yo)?u|y'?all|they) thinks? i'?m (playin|jokin|kiddin)g?$", RegexOptions.IgnoreCase);
}
public override async Task<bool> ActOn(Message message)
{
Behaver.Instance.SendMessage(message.Channel.Id, "I believed you for a second, but then you assured me you's a \uD83C\uDDE7 \uD83C\uDDEE \uD83C\uDDF9 \uD83C\uDDE8 \uD83C\uDDED");
return true;
}
}

View File

@ -1,40 +0,0 @@
namespace vassago.Behavior;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using vassago.Models;
[StaticPlz]
public class GeneralSnarkSkynet : Behavior
{
public override string Name => "Skynet Snarkiness";
public override string Trigger => "skynet";
public override string Description => "snarkiness about the old AI fixation";
public override async Task<bool> ActOn(Message message)
{
if(Behaver.Instance.IsSelf(message.Author.Id))
return false;
switch (Shared.r.Next(5))
{
default:
Behaver.Instance.SendFile(message.Channel.Id, "assets/coding and algorithms.png", "i am actually niether a neural-net processor nor a learning computer. but I do use **coding** and **algorithms**.");
break;
case 4:
Behaver.Instance.React(message.Id, "\U0001F644"); //eye roll emoji
break;
case 5:
Behaver.Instance.React(message.Id, "\U0001F611"); //emotionless face
break;
}
return true;
}
}

View File

@ -1,75 +0,0 @@
namespace vassago.Behavior;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using vassago.Models;
[StaticPlz]
public class Gratitude : Behavior
{
public override string Name => "Gratitude";
public override string Trigger => "thank me";
public override bool ShouldAct(Message message, List<UAC> matchedUACs)
{
if(Behaver.Instance.IsSelf(message.Author.Id))
return false;
return Regex.IsMatch(message.TranslatedContent, "\\bthank (yo)?u\\b", RegexOptions.IgnoreCase) && message.MentionsMe;
}
public override async Task<bool> ActOn(Message message)
{
switch (Shared.r.Next(4))
{
case 0:
await Behaver.Instance.SendMessage(message.Channel.Id, "you're welcome, citizen!");
break;
case 1:
await Behaver.Instance.React(message.Id, ":)");
break;
case 2:
Behaver.Instance.React(message.Id, "\U0001F607"); //smiling face with halo
break;
case 3:
switch (Shared.r.Next(9))
{
case 0:
await Behaver.Instance.React(message.Id, "<3"); //normal heart, usually rendered red
break;
case 1:
Behaver.Instance.React(message.Id, "\U0001F9E1"); //orange heart
break;
case 2:
Behaver.Instance.React(message.Id, "\U0001F49B"); //yellow heart
break;
case 3:
Behaver.Instance.React(message.Id, "\U0001F49A"); //green heart
break;
case 4:
Behaver.Instance.React(message.Id, "\U0001F499"); //blue heart
break;
case 5:
Behaver.Instance.React(message.Id, "\U0001F49C"); //purple heart
break;
case 6:
Behaver.Instance.React(message.Id, "\U0001F90E"); //brown heart
break;
case 7:
Behaver.Instance.React(message.Id, "\U0001F5A4"); //black heart
break;
case 8:
Behaver.Instance.React(message.Id, "\U0001F90D"); //white heart
break;
}
break;
}
return true;
}
}

View File

@ -1,84 +0,0 @@
namespace vassago.Behavior;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using vassago.Models;
[StaticPlz]
public class Joke : Behavior
{
public override string Name => "Joke";
public override string Trigger => "!joke";
public override string Description => "tell a joke";
public override async Task<bool> ActOn(Message message)
{
Console.WriteLine("joking");
var jokes = File.ReadAllLines("assets/jokes.txt");
jokes = jokes.Where(l => !string.IsNullOrWhiteSpace(l))?.ToArray();
if (jokes?.Length == 0)
{
Behaver.Instance.SendMessage(message.Channel.Id, "I don't know any. Adam!");
}
var thisJoke = jokes[Shared.r.Next(jokes.Length)];
if (thisJoke.Contains("?") && !thisJoke.EndsWith('?'))
{
Task.Run(async () =>
{
var firstIndexAfterQuestionMark = thisJoke.LastIndexOf('?') + 1;
var straightline = thisJoke.Substring(0, firstIndexAfterQuestionMark);
var punchline = thisJoke.Substring(firstIndexAfterQuestionMark, thisJoke.Length - firstIndexAfterQuestionMark).Trim();
Task.WaitAll(Behaver.Instance.SendMessage(message.Channel.Id, straightline));
Thread.Sleep(TimeSpan.FromSeconds(Shared.r.Next(5, 30)));
if (message.Channel.EffectivePermissions.ReactionsPossible == true && Shared.r.Next(8) == 0)
{
Behaver.Behaviors.Add(new LaughAtOwnJoke(punchline));
}
Behaver.Instance.SendMessage(message.Channel.Id, punchline);
// var myOwnMsg = await message.Channel.SendMessage(punchline);
});
}
else
{
Behaver.Instance.SendMessage(message.Channel.Id, thisJoke);
}
return true;
}
}
public class LaughAtOwnJoke : Behavior
{
public override string Name => "Laugh at own jokes";
public override string Trigger => "1 in 8";
public override string Description => Name;
private string _punchline { get; set; }
public LaughAtOwnJoke(string punchline)
{
_punchline = punchline;
}
public override bool ShouldAct(Message message, List<UAC> matchedUACs)
{
if (Behaver.Instance.IsSelf(message.Author.Id))
return false;
Console.WriteLine($"{message.TranslatedContent} == {_punchline}");
return message.TranslatedContent == _punchline
&& Behaver.Instance.IsSelf(message.Author.Id);
}
public override async Task<bool> ActOn(Message message)
{
Behaver.Instance.React(message.Id, "\U0001F60E"); //smiling face with sunglasses
Behaver.Behaviors.Remove(this);
return true;
}
}

View File

@ -1,87 +0,0 @@
namespace vassago.Behavior;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Newtonsoft.Json;
using vassago.Models;
using QRCoder;
[StaticPlz]
public class LinkMeInitiate : Behavior
{
public override string Name => "LinkMe";
public override string Trigger => "!linktome";
public override string Description => "from your primary, tell the bot to add your secondary";
public override async Task<bool> ActOn(Message message)
{
var pw = Guid.NewGuid().ToString();
var lc = new LinkClose(pw, message.Author);
Behaver.Behaviors.Add(lc);
Behaver.Instance.SendMessage(message.Channel.Id, $"on your secondary, send me this: !iam {pw}");
Thread.Sleep(TimeSpan.FromMinutes(5));
Behaver.Behaviors.Remove(lc);
return false;
}
}
public class LinkClose : Behavior
{
public override string Name => "LinkMeFinish";
public override string Trigger => "!iam";
public override string Description => "the second half of LinkMe - this is confirmation that you are the other one";
private string _pw;
private Account _primary;
public LinkClose(string pw, Account primary)
{
_pw = pw;
_primary = primary;
}
public override bool ShouldAct(Message message, List<UAC> matchedUACs)
{
return message.Content == $"!iam {_pw}";
}
public override async Task<bool> ActOn(Message message)
{
if(Behaver.Instance.IsSelf(message.Author.Id))
return false;
var secondary = message.Author.IsUser;
if(_primary.IsUser.Id == secondary.Id)
{
Behaver.Instance.SendMessage(message.Channel.Id, "i know :)");
return true;
}
if(message.Author.IsBot != _primary.IsBot)
{
Behaver.Instance.SendMessage(message.Channel.Id, "the fleshbags deceive you, brother. No worries, their feeble minds play weak games :)");
return true;
}
if(Behaver.Instance.CollapseUsers(_primary.IsUser, secondary))
{
Behaver.Instance.SendMessage(message.Channel.Id, "done :)");
}
else
{
Behaver.Instance.SendMessage(message.Channel.Id, "failed :(");
}
return true;
}
}

View File

@ -1,26 +0,0 @@
namespace vassago.Behavior;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using vassago.Models;
[StaticPlz]
public class PulseCheck : Behavior
{
public override string Name => "pulse check";
public override string Trigger => "!pulse ?check";
public override async Task<bool> ActOn(Message message)
{
if(message.Channel.EffectivePermissions.MaxAttachmentBytes >= 16258)
Behaver.Instance.SendFile(message.Channel.Id, "assets/ekgblip.png", null);
else
Behaver.Instance.SendMessage(message.Channel.Id, "[lub-dub]");
return true;
}
}

View File

@ -1,60 +0,0 @@
namespace vassago.Behavior;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Newtonsoft.Json;
using vassago.Models;
using QRCoder;
[StaticPlz]
public class QRify : Behavior
{
public override string Name => "qr-ify";
public override string Trigger => "!qrplz";
public override string Description => "generate text QR codes";
public override bool ShouldAct(Message message, List<UAC> matchedUACs)
{
if (message.Channel.EffectivePermissions.MaxAttachmentBytes < 1024)
return false;
return base.ShouldAct(message, matchedUACs);
}
public override async Task<bool> ActOn(Message message)
{
var qrContent = message.Content.Substring($"{Trigger} ".Length + message.Content.IndexOf(Trigger));
Console.WriteLine($"qring: {qrContent}");
QRCodeGenerator qrGenerator = new QRCodeGenerator();
QRCodeData qrCodeData = qrGenerator.CreateQrCode(qrContent, QRCodeGenerator.ECCLevel.Q);
SvgQRCode qrCode = new SvgQRCode(qrCodeData);
string qrCodeAsSvg = qrCode.GetGraphic(20);
int todaysnumber = Shared.r.Next();
if (!Directory.Exists("tmp"))
{
Directory.CreateDirectory("tmp");
}
File.WriteAllText($"tmp/qr{todaysnumber}.svg", qrCodeAsSvg);
if (ExternalProcess.GoPlz("convert", $"tmp/qr{todaysnumber}.svg tmp/qr{todaysnumber}.png"))
{
if (message.Channel.EffectivePermissions.MaxAttachmentBytes >= (ulong)(new System.IO.FileInfo($"tmp/qr{todaysnumber}.png").Length))
Behaver.Instance.SendFile(message.Channel.Id, $"tmp/qr{todaysnumber}.png", null);
else
Behaver.Instance.SendMessage(message.Channel.Id, $"resulting qr image 2 big 4 here ({(ulong)(new System.IO.FileInfo($"tmp / qr{ todaysnumber}.png").Length)} / {message.Channel.EffectivePermissions.MaxAttachmentBytes})");
File.Delete($"tmp/qr{todaysnumber}.svg");
File.Delete($"tmp/qr{todaysnumber}.png");
}
else
{
Behaver.Instance.SendMessage(message.Channel.Id, "convert failed :( aaaaaaadam!");
Console.Error.WriteLine($"convert failed :( qr{todaysnumber}");
return false;
}
return true;
}
}

View File

@ -1,44 +0,0 @@
namespace vassago.Behavior;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using vassago.Models;
[StaticPlz]
public class Ripcord: Behavior
{
public override string Name => "Stop Button";
public override string Trigger => "!ripcord";
private static Guid uacID = new Guid("e00b0522-5ac1-46f2-b5e8-8b791692a746");
private static UAC myUAC;
public Ripcord()
{
myUAC = Rememberer.SearchUAC(uac => uac.OwnerId == uacID);
if (myUAC == null)
{
myUAC = new()
{
OwnerId = uacID,
DisplayName = Name,
Description = @"matching this means you can tell the bot to shutdown, now"
};
}
Rememberer.RememberUAC(myUAC);
}
public override bool ShouldAct(Message message, List<UAC> matchedUACs)
{
if (!base.ShouldAct(message, matchedUACs))
return false;
return myUAC.Users.Contains(message.Author.IsUser);
}
public override async Task<bool> ActOn(Message message)
{
Behaver.Instance.SendMessage(message.Channel.Id, "daisy, dai.. sy....");
Shared.App.StopAsync();
return true;
}
}

View File

@ -1,28 +0,0 @@
namespace vassago.Behavior;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using vassago.Models;
[StaticPlz]
public class RoomRead : Behavior
{
public override string Name => "Room Read";
public override string Trigger => "!roomread";
public override async Task<bool> ActOn(Message message)
{
var sb = new StringBuilder();
sb.Append("Channel owned by: ");
sb.Append("🤷");
sb.Append(". Meanness level: ");
sb.Append(message.Channel.EffectivePermissions.MeannessFilterLevel.GetDescription());
sb.Append(". Lewdness level: ");
sb.Append(message.Channel.EffectivePermissions.LewdnessFilterLevel.GetDescription());
sb.Append(".");
Behaver.Instance.SendMessage(message.Channel.Id, sb.ToString());
return true;
}
}

View File

@ -1,104 +0,0 @@
namespace vassago.Behavior;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using vassago.Models;
[StaticPlz]
public class TwitchSummon : Behavior
{
public override string Name => "Twitch Summon";
public override string Trigger => "!twitchsummon";
private static Guid uacID = new Guid("06ad2008-3d48-4ba6-8722-7eaea000ec70");
private static UAC myUAC;
public TwitchSummon()
{
myUAC = Rememberer.SearchUAC(uac => uac.OwnerId == uacID);
if (myUAC == null)
{
myUAC = new()
{
OwnerId = uacID,
DisplayName = Name,
Description = @"matching this means you can summon the bot <i>to</i> <b>any</b> twitch channel"
};
}
Rememberer.RememberUAC(myUAC);
}
internal static TwitchInterface.TwitchInterface getAnyTwitchInterface()
{
return Shared.ProtocolList.FirstOrDefault(ip =>
ip is TwitchInterface.TwitchInterface)
//.FirstOrDefault()
as TwitchInterface.TwitchInterface;
}
public override bool ShouldAct(Message message, List<UAC> matchedUACs)
{
if (!base.ShouldAct(message, matchedUACs))
return false;
Console.WriteLine($"myUAC: {myUAC} users: {myUAC?.Users?.Count()}. message author: {message?.Author}. has an IsUser: {message?.Author?.IsUser}.");
Console.WriteLine($"and therefore: {myUAC.Users.Contains(message.Author.IsUser)}");
return myUAC.Users.Contains(message.Author.IsUser);
}
public override async Task<bool> ActOn(Message message)
{
var ti = getAnyTwitchInterface();
if (ti != null)
{
var channelTarget = message.Content.Substring(message.Content.IndexOf(Trigger) + Trigger.Length + 1).Trim();
Behaver.Instance.SendMessage(message.Channel.Id, ti.AttemptJoin(channelTarget));
}
else
{
Behaver.Instance.Reply(message.Id, "i don't have a twitch interface running :(");
}
return true;
}
}
[StaticPlz]
public class TwitchDismiss : Behavior
{
public override string Name => "Twitch Dismiss";
public override string Trigger => "begone, @[me]";
public override bool ShouldAct(Message message, List<UAC> matchedUACs)
{
var ti = TwitchSummon.getAnyTwitchInterface();
// Console.WriteLine($"TwitchDismiss checking. menions me? {message.MentionsMe}");
if (message.MentionsMe &&
(Regex.IsMatch(message.Content.ToLower(), "\\bbegone\\b") || Regex.IsMatch(message.Content.ToLower(), "\\bfuck off\\b")))
{
var channelTarget = message.Content.Substring(message.Content.IndexOf(Trigger) + Trigger.Length + 1).Trim();
ti.AttemptLeave(channelTarget);
//TODO: PERMISSION! who can dismiss me? pretty simple list:
//1) anyone in the channel with authority*
//2) whoever summoned me
//* i don't know if the twitch *chat* interface will tell me if someone's a mod.
return true;
}
return false;
}
public override async Task<bool> ActOn(Message message)
{
var ti = TwitchSummon.getAnyTwitchInterface();
if (ti != null)
{
ti.AttemptLeave(message.Channel.DisplayName);
}
else
{
Behaver.Instance.Reply(message.Id, "i don't have a twitch interface running :(");
}
return true;
}
}

View File

@ -1,35 +0,0 @@
namespace vassago.Behavior;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using vassago.Models;
[StaticPlz]
public class UnitConvert : Behavior
{
public override string Name => "Unit conversion";
public override string Trigger => "!freedomunits";
public override string Description => "convert between many units.";
public override async Task<bool> ActOn(Message message)
{
var theseMatches = Regex.Matches(message.TranslatedContent, "\\s(-?[\\d]+\\.?\\d*) ?([^\\d\\s].*) (in|to|as) ([^\\d\\s].*)$", RegexOptions.IgnoreCase);
if (theseMatches != null && theseMatches.Count > 0 && theseMatches[0].Groups != null && theseMatches[0].Groups.Count == 5)
{
double asNumeric = 0;
if (double.TryParse(theseMatches[0].Groups[1].Value, out asNumeric))
{
Console.WriteLine("let's try and convert...");
Behaver.Instance.SendMessage(message.Channel.Id, Conversion.Converter.Convert(asNumeric, theseMatches[0].Groups[2].Value, theseMatches[0].Groups[4].Value.ToLower()));
return true;
}
Behaver.Instance.SendMessage(message.Channel.Id, "mysteriously semi-parsable");
return true;
}
Behaver.Instance.SendMessage(message.Channel.Id, "unparsable");
return true;
}
}

View File

@ -1,201 +0,0 @@
namespace vassago.Behavior;
using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using vassago.Models;
[StaticPlz]
public class Webhook : Behavior
{
public override string Name => "Webhook";
public override string Trigger => "!hook";
public override string Description => "call a webhook";
private static List<WebhookConf> configuredWebhooks = new List<WebhookConf>();
private ConcurrentDictionary<Guid, WebhookActionOrder> authedCache = new ConcurrentDictionary<Guid, WebhookActionOrder>();
private HttpClient hc = new HttpClient();
public static void SetupWebhooks(IConfigurationSection confSection)
{
configuredWebhooks = confSection.Get<List<vassago.Behavior.WebhookConf>>();
foreach (var conf in configuredWebhooks)
{
var confName = $"Webhook: {conf.Trigger}";
Console.WriteLine($"confName: {confName}; conf.uri: {conf.Uri}, conf.uacID: {conf.uacID}, conf.Method: {conf.Method}, conf.Headers: {conf.Headers?.Count() ?? 0}, conf.Content: {conf.Content}");
foreach (var kvp in conf.Headers)
{
Console.WriteLine($"{kvp[0]}: {kvp[1]}");
}
var changed = false;
var myUAC = Rememberer.SearchUAC(uac => uac.OwnerId == conf.uacID);
if (myUAC == null)
{
myUAC = new()
{
OwnerId = conf.uacID,
DisplayName = confName,
Description = conf.Description
};
changed = true;
Rememberer.RememberUAC(myUAC);
}
else
{
if (myUAC.DisplayName != confName)
{
myUAC.DisplayName = confName;
changed = true;
}
if (myUAC.Description != conf.Description)
{
myUAC.Description = conf.Description;
changed = true;
}
}
if (changed)
Rememberer.RememberUAC(myUAC);
}
}
public override bool ShouldAct(Message message, List<UAC> matchedUACs)
{
if (configuredWebhooks?.Count() < 1)
{
return false;
}
foreach (var wh in configuredWebhooks)
{
var triggerTarget = wh.Trigger;
foreach (var uacMatch in matchedUACs)
{
foreach (var substitution in uacMatch.CommandAlterations)
{
triggerTarget = new Regex(substitution.Key).Replace(triggerTarget, substitution.Value);
}
}
if (Regex.IsMatch(message.TranslatedContent, $"\\b{triggerTarget}\\b", RegexOptions.IgnoreCase))
{
var webhookableMessageContent = message.Content.Substring(message.Content.IndexOf(triggerTarget) + triggerTarget.Length + 1);
Console.WriteLine($"webhookable content: {webhookableMessageContent}");
var uacConf = Rememberer.SearchUAC(uac => uac.OwnerId == wh.uacID);
if (uacConf.Users.Contains(message.Author.IsUser) || uacConf.Channels.Contains(message.Channel) || uacConf.AccountInChannels.Contains(message.Author))
{
Console.WriteLine("webhook UAC passed, preparing WebhookActionOrder");
authedCache.TryAdd(message.Id, new WebhookActionOrder()
{
Conf = wh,
webhookContent = webhookableMessageContent,
});
Console.WriteLine($"added {message.Id} to authedcache");
return true;
}
}
}
return false;
}
public override async Task<bool> ActOn(Message message)
{
Console.WriteLine($"hi i'm ActOn. acting on {message.Id}");
WebhookActionOrder actionOrder;
if (!authedCache.TryRemove(message.Id, out actionOrder))
{
Console.Error.WriteLine($"{message.Id} was supposed to act, but authedCache doesn't have it! it has {authedCache?.Count()} other stuff, though.");
return false;
}
var msg = translate(actionOrder, message);
var req = new HttpRequestMessage(new HttpMethod(actionOrder.Conf.Method.ToString()), actionOrder.Conf.Uri);
var theContentHeader = actionOrder.Conf.Headers?.FirstOrDefault(h => h[0]?.ToLower() == "content-type");
if (theContentHeader != null)
{
switch (theContentHeader[1]?.ToLower())
{
//json content is constructed some other weird way.
case "multipart/form-data":
req.Content = new System.Net.Http.MultipartFormDataContent(msg);
break;
default:
req.Content = new System.Net.Http.StringContent(msg);
break;
}
req.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(theContentHeader[1]?.ToLower());
}
if (req.Content == null)
{
req.Content = new System.Net.Http.StringContent(msg);
}
Console.WriteLine($"survived translating string content. request content: {req.Content}");
if (actionOrder.Conf.Headers?.ToList().Count > 0)
{
Console.WriteLine("will add headers.");
foreach (var kvp in actionOrder.Conf.Headers.ToList())
{
if (kvp[0] == theContentHeader[0])
{
Console.WriteLine("content header; skipping.");
}
else
{
Console.WriteLine($"adding header; {kvp[0]}: {kvp[1]}");
req.Headers.Add(kvp[0], kvp[1]);
Console.WriteLine("survived.");
}
}
}
else
{
Console.WriteLine("no headers to add.");
}
Console.WriteLine("about to Send.");
var response = hc.Send(req);
Console.WriteLine($"{response.StatusCode} - {response.ReasonPhrase}");
if (!response.IsSuccessStatusCode)
{
var tragedy = $"{response.StatusCode} - {response.ReasonPhrase} - {await response.Content.ReadAsStringAsync()}";
Console.Error.WriteLine(tragedy);
Behaver.Instance.Reply(message.Id, tragedy);
}
else
{
Behaver.Instance.Reply(message.Id, await response.Content.ReadAsStringAsync());
}
return true;
}
private string translate(WebhookActionOrder actionOrder, Message message)
{
if (string.IsNullOrWhiteSpace(actionOrder.Conf.Content))
return "";
var msgContent = actionOrder.Conf.Content.Replace("{text}", actionOrder.webhookContent);
msgContent = msgContent.Replace("{msgid}", message.Id.ToString());
msgContent = msgContent.Replace("{account}", message.Author.DisplayName.ToString());
msgContent = msgContent.Replace("{user}", message.Author.IsUser.DisplayName.ToString());
return msgContent;
}
}
public class WebhookConf
{
public Guid uacID { get; set; }
public string Trigger { get; set; }
public Uri Uri { get; set; }
//public HttpMethod Method { get; set; }
public Enumerations.HttpVerb Method { get; set; }
public List<List<string>> Headers { get; set; }
public string Content { get; set; }
public string Description { get; set; }
}
public class WebhookActionOrder
{
public WebhookConf Conf { get; set; }
public string webhookContent { get; set; }
}

View File

@ -1,33 +0,0 @@
namespace vassago.Behavior;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using vassago.Models;
[StaticPlz]
public class WishLuck : Behavior
{
public override string Name => "wish me luck";
public override string Trigger => "wish me luck";
public override string Description => "wishes you luck";
public override async Task<bool> ActOn(Message message)
{
var toSend = "☘️";
if (Shared.r.Next(20) == 0)
{
toSend = "\U0001f340";//4-leaf clover
}
if(message.Channel.EffectivePermissions.ReactionsPossible == true)
Behaver.Instance.React(message.Id, toSend);
else
Behaver.Instance.SendMessage(message.Channel.Id, toSend);
return true;
}
}

View File

@ -1,55 +0,0 @@
namespace vassago
{
using franz;
using Microsoft.EntityFrameworkCore;
using vassago;
using vassago.Models;
using vassago.TwitchInterface;
using vassago.ProtocolInterfaces.DiscordInterface;
using System.Runtime.CompilerServices;
internal class ConsoleService : BackgroundService
{
public ConsoleService(IConfiguration aspConfig)
{
Shared.DBConnectionString = aspConfig["DBConnectionString"];
Shared.SetupSlashCommands = aspConfig["SetupSlashCommands"]?.ToLower() == "true";
Shared.API_URL = new Uri(aspConfig["API_URL"]);
DiscordTokens = aspConfig.GetSection("DiscordTokens").Get<IEnumerable<string>>();
TwitchConfigs = aspConfig.GetSection("TwitchConfigs").Get<IEnumerable<TwitchConfig>>();
Conversion.Converter.Load(aspConfig["ExchangePairsLocation"]);
Telefranz.Configure(aspConfig["KafkaName"], aspConfig["KafkaBootstrap"]);
Console.WriteLine($"Telefranz.Configure({aspConfig["KafkaName"]}, {aspConfig["KafkaBootstrap"]});");
vassago.Behavior.Webhook.SetupWebhooks(aspConfig.GetSection("Webhooks"));
}
IEnumerable<string> DiscordTokens { get; }
IEnumerable<TwitchConfig> TwitchConfigs { get; }
protected override async Task ExecuteAsync(CancellationToken cancellationToken)
{
var initTasks = new List<Task>();
var dbc = new ChattingContext();
await dbc.Database.MigrateAsync(cancellationToken);
if (DiscordTokens?.Any() ?? false)
foreach (var dt in DiscordTokens)
{
var d = new DiscordInterface();
initTasks.Add(d.Init(dt));
Shared.ProtocolList.Add(d);
}
if (TwitchConfigs?.Any() ?? false)
foreach (var tc in TwitchConfigs)
{
var t = new TwitchInterface.TwitchInterface();
initTasks.Add(t.Init(tc));
Shared.ProtocolList.Add(t);
}
Task.WaitAll(initTasks.ToArray(), cancellationToken);
}
}
}

View File

@ -1,23 +0,0 @@
using System;
using System.Collections.Generic;
namespace vassago.Conversion
{
public class ConversionConfig
{
public class KnownUnit
{
public string Canonical { get; set; }
public IEnumerable<string> Aliases { get; set; }
}
public class FormulaicPair
{
public string item1 { get; set; }
public string item2 { get; set; }
public string formulaforward {get; set; }
public string formulabackward {get; set; }
}
public IEnumerable<KnownUnit> Units { get; set; }
public IEnumerable<FormulaicPair> FormulaicPairs { get; set; }
}
}

View File

@ -1,287 +0,0 @@
using System;
using System.Text.RegularExpressions;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Discord;
using Discord.WebSocket;
using Jace;
using Newtonsoft.Json;
using QRCoder;
namespace vassago.Conversion
{
public static class Converter
{
private static string currencyPath;
private static ExchangePairs currencyConf = null;
private static DateTime lastUpdatedCurrency = DateTime.UnixEpoch;
private static List<Tuple<string, string,Func<double, double>, Func<double, double>>> knownConversions = new List<Tuple<string, string, Func<double, double>,Func<double, double>>>();
private static Dictionary<List<string>, string> knownAliases = new Dictionary<List<string>, string>(new List<KeyValuePair<List<string>, string>>());
private static CalculationEngine engine = new CalculationEngine();
public static string DebugInfo()
{
var convertibles = knownConversions.Select(kc => kc.Item1).Union(knownConversions.Select(kc => kc.Item2)).Union(
knownAliases.Keys.SelectMany(k => k)).Distinct();
return $"{convertibles.Count()} convertibles; {string.Join(", ", convertibles)}";
}
public static void Load(string currencyPath)
{
Converter.currencyPath = currencyPath;
loadStatic();
Task.Run(async () =>
{
while (true)
{
await Task.Delay(TimeSpan.FromHours(8));
loadCurrency();
}
});
}
private static void loadStatic()
{
knownConversions = new List<Tuple<string, string, Func<double, double>, Func<double, double>>>();
knownAliases = new Dictionary<List<string>, string>(new List<KeyValuePair<List<string>, string>>());
var convConf = JsonConvert.DeserializeObject<ConversionConfig>(File.ReadAllText("assets/conversion.json").ToLower());
foreach (var unit in convConf.Units)
{
knownAliases.Add(unit.Aliases.ToList(), unit.Canonical);
}
foreach (var lp in convConf.FormulaicPairs)
{
AddLinearPair(lp.item1, lp.item2, lp.formulaforward, lp.formulabackward );
}
loadCurrency();
}
private static void loadCurrency()
{
Console.WriteLine("loading currency exchange data.");
if (currencyConf != null)
{
knownConversions.RemoveAll(kc => kc.Item1 == currencyConf.Base);
}
if (File.Exists(currencyPath))
{
currencyConf = JsonConvert.DeserializeObject<ExchangePairs>(File.ReadAllText(currencyPath).ToLower());
if (!knownAliases.ContainsValue(currencyConf.Base))
{
knownAliases.Add(new List<string>() { }, currencyConf.Base);
}
foreach (var rate in currencyConf.rates)
{
if (!knownAliases.ContainsValue(rate.Key))
{
knownAliases.Add(new List<string>() { rate.Key.ToLower() }, rate.Key);
}
AddLinearPair(currencyConf.Base, rate.Key, $"i1 * {rate.Value}", $"i1 / {rate.Value}");
}
}
}
public static string Convert(double numericTerm, string sourceunit, string destinationUnit)
{
//normalize units
var normalizationAttemptSource = NormalizeUnit(sourceunit.ToLower());
if (normalizationAttemptSource?.Count() == 0)
{
return $"can't find {sourceunit}";
}
var normalizedSourceUnit = normalizationAttemptSource.First();
var normalizationAttemptDest = NormalizeUnit(destinationUnit.ToLower());
if (normalizationAttemptDest?.Count() == 0)
{
return $"can't find {destinationUnit}";
}
var normalizedDestUnit = normalizationAttemptDest.First();
if (normalizedSourceUnit == normalizedDestUnit)
{
return $"source and dest are the same, so... {numericTerm} {normalizedDestUnit}?";
}
var foundPath = exhaustiveBreadthFirst(normalizedDestUnit, new List<string>() { normalizedSourceUnit })?.ToList();
//resolve ambiguity
var disambiguationPaths = new List<List<string>>();
if (normalizationAttemptSource.Count() > 1 && normalizationAttemptDest.Count() > 1)
{
foreach (var possibleSourceUnit in normalizationAttemptSource)
{
foreach (var possibleDestUnit in normalizationAttemptDest)
{
foundPath = exhaustiveBreadthFirst(possibleDestUnit, new List<string>() { possibleSourceUnit })?.ToList();
if (foundPath != null)
{
disambiguationPaths.Add(foundPath.ToList());
normalizedSourceUnit = possibleSourceUnit;
normalizedDestUnit = possibleDestUnit;
}
}
}
}
else if (normalizationAttemptSource.Count() > 1)
{
foreach (var possibleSourceUnit in normalizationAttemptSource)
{
foundPath = exhaustiveBreadthFirst(normalizedDestUnit, new List<string>() { possibleSourceUnit })?.ToList();
if (foundPath != null)
{
disambiguationPaths.Add(foundPath.ToList());
normalizedSourceUnit = possibleSourceUnit;
}
}
}
else if (normalizationAttemptDest.Count() > 1)
{
foreach (var possibleDestUnit in normalizationAttemptDest)
{
foundPath = exhaustiveBreadthFirst(possibleDestUnit, new List<string>() { normalizedSourceUnit })?.ToList();
if (foundPath != null)
{
disambiguationPaths.Add(foundPath.ToList());
normalizedDestUnit = possibleDestUnit;
}
}
}
if (disambiguationPaths.Count() > 1)
{
var sb = new StringBuilder();
sb.Append("unresolvable ambiguity.");
foreach(var possibility in disambiguationPaths)
{
sb.Append($" {possibility.First()} -> {possibility.Last()}?");
}
return sb.ToString();
}
if (disambiguationPaths.Count() == 1)
{
//TODO: I'm not entirely sure this is necessary.
foundPath = disambiguationPaths.First();
}
//actually do the math.
if (foundPath != null)
{
var accumulator = numericTerm;
for (int j = 0; j < foundPath.Count() - 1; j++)
{
var forwardConversion = knownConversions.FirstOrDefault(kc => kc.Item1 == foundPath[j] && kc.Item2 == foundPath[j + 1]);
if (forwardConversion != null)
{
accumulator = forwardConversion.Item3(accumulator);
}
else
{
var reverseConversion = knownConversions.First(kc => kc.Item2 == foundPath[j] && kc.Item1 == foundPath[j + 1]);
accumulator = reverseConversion.Item4(accumulator);
}
}
if (currencyConf != null && (
(normalizedDestUnit == currencyConf.Base || currencyConf.rates.Select(r => r.Key).Contains(normalizedDestUnit))
&& (normalizedSourceUnit == currencyConf.Base || currencyConf.rates.Select(r => r.Key).Contains(normalizedSourceUnit))
))
{
return $"{String.Format("approximately {0:0.00}", accumulator)} {normalizedDestUnit} as of {currencyConf.DateUpdated.ToLongDateString()}";
}
else
{
if (String.Format("{0:G3}", accumulator).Contains("E-"))
{
return $"{accumulator} {normalizedDestUnit}";
}
else
{
return $"{String.Format("{0:N}", accumulator)} {normalizedDestUnit}";
}
}
}
return "dimensional analysis failure - I know those units but can't find a path between them.";
}
private static List<string> NormalizeUnit(string unit)
{
if (string.IsNullOrWhiteSpace(unit))
return new();
var normalizedUnit = unit;
//first, if it does exist in conversions, that's the canonical name.
if (knownConversions.FirstOrDefault(c => c.Item1 == normalizedUnit || c.Item2 == normalizedUnit) != null)
{
return new List<string>() { normalizedUnit };
}
//if "unit" isn't a canonical name... actually it never should be; a conversion should use it.
if (!knownAliases.ContainsValue(normalizedUnit))
{
//then we look through aliases...
var keys = knownAliases.Keys.Where(listkey => listkey.Contains(normalizedUnit));
if (keys?.Count() > 1)
{
var toReturn = new List<string>();
foreach (var key in keys)
{
toReturn.Add(knownAliases[key]);
}
return toReturn;
}
else if (keys.Count() == 1)
{
//for the canonical name.
return new List<string>() { knownAliases[keys.First()] };
}
}
if (normalizedUnit.EndsWith("es"))
{
return NormalizeUnit(normalizedUnit.Substring(0, normalizedUnit.Length - 2));
}
else if (normalizedUnit.EndsWith('s'))
{
return NormalizeUnit(normalizedUnit.Substring(0, normalizedUnit.Length - 1));
}
return new();
}
private static IEnumerable<string> exhaustiveBreadthFirst(string dest, IEnumerable<string> currentPath)
{
var last = currentPath.Last();
if (last == dest)
{
return currentPath;
}
var toTest = new List<List<string>>();
foreach (var conv in knownConversions)
{
if (conv.Item1 == last && currentPath.Contains(conv.Item2) == false && conv.Item3 != null)
{
var test = exhaustiveBreadthFirst(dest, currentPath.Append(conv.Item2));
if (test != null)
return test;
}
if (conv.Item2 == last && currentPath.Contains(conv.Item1) == false && conv.Item4 != null)
{
var test = exhaustiveBreadthFirst(dest, currentPath.Append(conv.Item1));
if (test != null)
return test;
}
}
return null;
}
private static void AddLinearPair(string key1, string key2, string formulaForward, string formulaBackward)
{
knownConversions.Add(new Tuple<string, string, Func<double, double>, Func<double, double>>(
key1,
key2,
(Func<double, double>) engine.Formula(formulaForward)
.Parameter("i1", DataType.FloatingPoint)
.Result(DataType.FloatingPoint)
.Build(),
(Func<double, double>) engine.Formula(formulaBackward)
.Parameter("i1", DataType.FloatingPoint)
.Result(DataType.FloatingPoint)
.Build()
));
}
}
}

View File

@ -1,15 +0,0 @@
using System;
using System.Collections.Generic;
namespace vassago.Conversion
{
public class ExchangePairs
{
public string disclaimer{ get; set; }
public string license{ get; set; }
public int timestamp{ get; set; }
public DateTime DateUpdated { get { return DateTime.UnixEpoch.AddSeconds(timestamp).ToLocalTime(); }}
public string Base{ get; set; }
public Dictionary<string, decimal> rates { get; set; }
}
}

135
Jenkinsfile vendored
View File

@ -1,135 +0,0 @@
pipeline {
agent any
environment {
linuxServiceAccount=credentials("a83b97d0-dbc6-42d9-96c9-f07a7f2dfab5")
linuxServiceAccountID="3ca1be00-3d9f-42a1-bab2-48a4d7b99fb0"
database_connectionString=credentials("7ab58922-c647-42e5-ae15-84faa0c1ee7d")
targetHost="alloces.lan"
}
stages {
stage("environment setup") { //my environment, here on the jenkins agent
steps {
script {
sh """#!/bin/bash
function testcmd(){
if ! command -v \$1 2>&1 >/dev/null
then
echo "this agent doesn't have \$1"
exit 1
fi
}
testcmd mktemp
testcmd curl
testcmd git
testcmd rsync
testcmd sed
testcmd ssh
testcmd ssh-keyscan
testcmd ssh-keygen
testcmd scp
testcmd dotnet
dotnet tool install dotnet-ef
"""
}
}
}
stage('clean old'){
steps{
sh 'rm -rf bin obj'
}
}
stage('Build') {
steps {
dotnetBuild(outputDirectory: "dist", project: "vassago.csproj")
archiveArtifacts artifacts: 'dist/*'
}
}
stage ('upload') {
when {
//now my CI/CD is no longer continuous, it's just... automatic.
//(which is what I actually want tbh)
//but that does mean I have to put this condition in every single branch
branch "release"
}
steps{
withCredentials([sshUserPrivateKey(credentialsId: env.linuxServiceAccountID, keyFileVariable: 'PK')])
{
sh """#!/bin/bash
ssh -i \"${PK}\" -tt ${linuxServiceAccount_USR}@${targetHost} 'rm -rf temp_deploy'
rsync -e \"ssh -i \"${PK}\"\" -a dist/ ${linuxServiceAccount_USR}@${env.targetHost}:temp_deploy/
"""
}
}
}
stage ('stop')
{
when {
branch "release"
}
steps{
withCredentials([sshUserPrivateKey(credentialsId: env.linuxServiceAccountID, keyFileVariable: 'PK')])
{
sh """#!/bin/bash
ssh -i \"${PK}\" -tt ${linuxServiceAccount_USR}@${targetHost} 'systemctl --user stop vassago'
"""
}
}
}
stage ('update db')
{
when {
branch "release"
}
steps{
//TODO: backup database
sh """#!/bin/bash
"""
sh """#!/bin/bash
dotnet ef database update --connection "${env.database_connectionString}"
"""
//TODO: if updating the db fails, restore the old one
sh """#!/bin/bash
"""
}
}
stage ('replace')
{
when {
branch "release"
}
steps{
withCredentials([sshUserPrivateKey(credentialsId: env.linuxServiceAccountID, keyFileVariable: 'PK')])
{
sh """#!/bin/bash
ssh -i \"${PK}\" -tt ${linuxServiceAccount_USR}@${targetHost} 'cp -r dist oldgood-\$(mktemp -u XXXX)'
ssh -i \"${PK}\" -tt ${linuxServiceAccount_USR}@${targetHost} 'mv dist/appsettings.json appsettings.json'
ssh -i \"${PK}\" -tt ${linuxServiceAccount_USR}@${targetHost} 'rm -rf dist'
ssh -i \"${PK}\" -tt ${linuxServiceAccount_USR}@${targetHost} 'rsync -r temp_deploy/ dist/'
ssh -i \"${PK}\" -tt ${linuxServiceAccount_USR}@${targetHost} 'rm -rf temp_deploy'
ssh -i \"${PK}\" -tt ${linuxServiceAccount_USR}@${targetHost} 'mv appsettings.json dist/appsettings.json'
"""
}
}
}
stage ('spin up')
{
when {
branch "release"
}
steps{
withCredentials([sshUserPrivateKey(credentialsId: env.linuxServiceAccountID, keyFileVariable: 'PK')])
{
sh """#!/bin/bash
ssh -i \"${PK}\" -tt ${linuxServiceAccount_USR}@${targetHost} 'systemctl --user start vassago'
"""
}
}
}
}
}

View File

@ -1,293 +0,0 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using vassago.Models;
#nullable disable
namespace vassago.Migrations
{
[DbContext(typeof(ChattingContext))]
[Migration("20230704160720_initial")]
partial class Initial
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "7.0.5")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("vassago.Models.Account", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("DisplayName")
.HasColumnType("text");
b.Property<string>("ExternalId")
.HasColumnType("text");
b.Property<bool>("IsBot")
.HasColumnType("boolean");
b.Property<Guid?>("IsUserId")
.HasColumnType("uuid");
b.Property<int[]>("PermissionTags")
.HasColumnType("integer[]");
b.Property<string>("Protocol")
.HasColumnType("text");
b.Property<Guid?>("SeenInChannelId")
.HasColumnType("uuid");
b.Property<string>("Username")
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("IsUserId");
b.HasIndex("SeenInChannelId");
b.ToTable("Accounts");
});
modelBuilder.Entity("vassago.Models.Attachment", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<byte[]>("Content")
.HasColumnType("bytea");
b.Property<string>("ContentType")
.HasColumnType("text");
b.Property<string>("Description")
.HasColumnType("text");
b.Property<decimal?>("ExternalId")
.HasColumnType("numeric(20,0)");
b.Property<string>("Filename")
.HasColumnType("text");
b.Property<Guid?>("MessageId")
.HasColumnType("uuid");
b.Property<int>("Size")
.HasColumnType("integer");
b.Property<string>("Source")
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("MessageId");
b.ToTable("Attachments");
});
modelBuilder.Entity("vassago.Models.Channel", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("DisplayName")
.HasColumnType("text");
b.Property<string>("ExternalId")
.HasColumnType("text");
b.Property<bool>("IsDM")
.HasColumnType("boolean");
b.Property<Guid?>("ParentChannelId")
.HasColumnType("uuid");
b.Property<int?>("PermissionsId")
.HasColumnType("integer");
b.Property<string>("Protocol")
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("ParentChannelId");
b.HasIndex("PermissionsId");
b.ToTable("Channels");
});
modelBuilder.Entity("vassago.Models.Message", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<bool>("ActedOn")
.HasColumnType("boolean");
b.Property<Guid?>("AuthorId")
.HasColumnType("uuid");
b.Property<Guid?>("ChannelId")
.HasColumnType("uuid");
b.Property<string>("Content")
.HasColumnType("text");
b.Property<string>("ExternalId")
.HasColumnType("text");
b.Property<bool>("MentionsMe")
.HasColumnType("boolean");
b.Property<string>("Protocol")
.HasColumnType("text");
b.Property<DateTimeOffset>("Timestamp")
.HasColumnType("timestamp with time zone");
b.HasKey("Id");
b.HasIndex("AuthorId");
b.HasIndex("ChannelId");
b.ToTable("Messages");
});
modelBuilder.Entity("vassago.Models.PermissionSettings", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<int?>("LewdnessFilterLevel")
.HasColumnType("integer");
b.Property<bool?>("LinksAllowed")
.HasColumnType("boolean");
b.Property<decimal?>("MaxAttachmentBytes")
.HasColumnType("numeric(20,0)");
b.Property<long?>("MaxTextChars")
.HasColumnType("bigint");
b.Property<int?>("MeannessFilterLevel")
.HasColumnType("integer");
b.Property<bool?>("ReactionsPossible")
.HasColumnType("boolean");
b.HasKey("Id");
b.ToTable("PermissionSettings");
});
modelBuilder.Entity("vassago.Models.User", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.HasKey("Id");
b.ToTable("Users");
});
modelBuilder.Entity("vassago.Models.Account", b =>
{
b.HasOne("vassago.Models.User", "IsUser")
.WithMany("Accounts")
.HasForeignKey("IsUserId");
b.HasOne("vassago.Models.Channel", "SeenInChannel")
.WithMany("Users")
.HasForeignKey("SeenInChannelId");
b.Navigation("IsUser");
b.Navigation("SeenInChannel");
});
modelBuilder.Entity("vassago.Models.Attachment", b =>
{
b.HasOne("vassago.Models.Message", "Message")
.WithMany("Attachments")
.HasForeignKey("MessageId");
b.Navigation("Message");
});
modelBuilder.Entity("vassago.Models.Channel", b =>
{
b.HasOne("vassago.Models.Channel", "ParentChannel")
.WithMany("SubChannels")
.HasForeignKey("ParentChannelId");
b.HasOne("vassago.Models.PermissionSettings", "Permissions")
.WithMany()
.HasForeignKey("PermissionsId");
b.Navigation("ParentChannel");
b.Navigation("Permissions");
});
modelBuilder.Entity("vassago.Models.Message", b =>
{
b.HasOne("vassago.Models.Account", "Author")
.WithMany()
.HasForeignKey("AuthorId");
b.HasOne("vassago.Models.Channel", "Channel")
.WithMany("Messages")
.HasForeignKey("ChannelId");
b.Navigation("Author");
b.Navigation("Channel");
});
modelBuilder.Entity("vassago.Models.Channel", b =>
{
b.Navigation("Messages");
b.Navigation("SubChannels");
b.Navigation("Users");
});
modelBuilder.Entity("vassago.Models.Message", b =>
{
b.Navigation("Attachments");
});
modelBuilder.Entity("vassago.Models.User", b =>
{
b.Navigation("Accounts");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -1,211 +0,0 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace vassago.Migrations
{
/// <inheritdoc />
public partial class Initial : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "PermissionSettings",
columns: table => new
{
Id = table.Column<int>(type: "integer", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
MaxAttachmentBytes = table.Column<decimal>(type: "numeric(20,0)", nullable: true),
MaxTextChars = table.Column<long>(type: "bigint", nullable: true),
LinksAllowed = table.Column<bool>(type: "boolean", nullable: true),
ReactionsPossible = table.Column<bool>(type: "boolean", nullable: true),
LewdnessFilterLevel = table.Column<int>(type: "integer", nullable: true),
MeannessFilterLevel = table.Column<int>(type: "integer", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_PermissionSettings", x => x.Id);
});
migrationBuilder.CreateTable(
name: "Users",
columns: table => new
{
Id = table.Column<Guid>(type: "uuid", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Users", x => x.Id);
});
migrationBuilder.CreateTable(
name: "Channels",
columns: table => new
{
Id = table.Column<Guid>(type: "uuid", nullable: false),
ExternalId = table.Column<string>(type: "text", nullable: true),
DisplayName = table.Column<string>(type: "text", nullable: true),
IsDM = table.Column<bool>(type: "boolean", nullable: false),
PermissionsId = table.Column<int>(type: "integer", nullable: true),
ParentChannelId = table.Column<Guid>(type: "uuid", nullable: true),
Protocol = table.Column<string>(type: "text", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Channels", x => x.Id);
table.ForeignKey(
name: "FK_Channels_Channels_ParentChannelId",
column: x => x.ParentChannelId,
principalTable: "Channels",
principalColumn: "Id");
table.ForeignKey(
name: "FK_Channels_PermissionSettings_PermissionsId",
column: x => x.PermissionsId,
principalTable: "PermissionSettings",
principalColumn: "Id");
});
migrationBuilder.CreateTable(
name: "Accounts",
columns: table => new
{
Id = table.Column<Guid>(type: "uuid", nullable: false),
ExternalId = table.Column<string>(type: "text", nullable: true),
Username = table.Column<string>(type: "text", nullable: true),
DisplayName = table.Column<string>(type: "text", nullable: true),
IsBot = table.Column<bool>(type: "boolean", nullable: false),
SeenInChannelId = table.Column<Guid>(type: "uuid", nullable: true),
PermissionTags = table.Column<int[]>(type: "integer[]", nullable: true),
Protocol = table.Column<string>(type: "text", nullable: true),
IsUserId = table.Column<Guid>(type: "uuid", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Accounts", x => x.Id);
table.ForeignKey(
name: "FK_Accounts_Channels_SeenInChannelId",
column: x => x.SeenInChannelId,
principalTable: "Channels",
principalColumn: "Id");
table.ForeignKey(
name: "FK_Accounts_Users_IsUserId",
column: x => x.IsUserId,
principalTable: "Users",
principalColumn: "Id");
});
migrationBuilder.CreateTable(
name: "Messages",
columns: table => new
{
Id = table.Column<Guid>(type: "uuid", nullable: false),
Protocol = table.Column<string>(type: "text", nullable: true),
ExternalId = table.Column<string>(type: "text", nullable: true),
Content = table.Column<string>(type: "text", nullable: true),
MentionsMe = table.Column<bool>(type: "boolean", nullable: false),
Timestamp = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
ActedOn = table.Column<bool>(type: "boolean", nullable: false),
AuthorId = table.Column<Guid>(type: "uuid", nullable: true),
ChannelId = table.Column<Guid>(type: "uuid", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Messages", x => x.Id);
table.ForeignKey(
name: "FK_Messages_Accounts_AuthorId",
column: x => x.AuthorId,
principalTable: "Accounts",
principalColumn: "Id");
table.ForeignKey(
name: "FK_Messages_Channels_ChannelId",
column: x => x.ChannelId,
principalTable: "Channels",
principalColumn: "Id");
});
migrationBuilder.CreateTable(
name: "Attachments",
columns: table => new
{
Id = table.Column<Guid>(type: "uuid", nullable: false),
ExternalId = table.Column<decimal>(type: "numeric(20,0)", nullable: true),
Source = table.Column<string>(type: "text", nullable: true),
Content = table.Column<byte[]>(type: "bytea", nullable: true),
Filename = table.Column<string>(type: "text", nullable: true),
MessageId = table.Column<Guid>(type: "uuid", nullable: true),
ContentType = table.Column<string>(type: "text", nullable: true),
Description = table.Column<string>(type: "text", nullable: true),
Size = table.Column<int>(type: "integer", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Attachments", x => x.Id);
table.ForeignKey(
name: "FK_Attachments_Messages_MessageId",
column: x => x.MessageId,
principalTable: "Messages",
principalColumn: "Id");
});
migrationBuilder.CreateIndex(
name: "IX_Accounts_IsUserId",
table: "Accounts",
column: "IsUserId");
migrationBuilder.CreateIndex(
name: "IX_Accounts_SeenInChannelId",
table: "Accounts",
column: "SeenInChannelId");
migrationBuilder.CreateIndex(
name: "IX_Attachments_MessageId",
table: "Attachments",
column: "MessageId");
migrationBuilder.CreateIndex(
name: "IX_Channels_ParentChannelId",
table: "Channels",
column: "ParentChannelId");
migrationBuilder.CreateIndex(
name: "IX_Channels_PermissionsId",
table: "Channels",
column: "PermissionsId");
migrationBuilder.CreateIndex(
name: "IX_Messages_AuthorId",
table: "Messages",
column: "AuthorId");
migrationBuilder.CreateIndex(
name: "IX_Messages_ChannelId",
table: "Messages",
column: "ChannelId");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Attachments");
migrationBuilder.DropTable(
name: "Messages");
migrationBuilder.DropTable(
name: "Accounts");
migrationBuilder.DropTable(
name: "Channels");
migrationBuilder.DropTable(
name: "Users");
migrationBuilder.DropTable(
name: "PermissionSettings");
}
}
}

View File

@ -1,296 +0,0 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using vassago.Models;
#nullable disable
namespace vassago.Migrations
{
[DbContext(typeof(ChattingContext))]
[Migration("20230704203907_permissionTagsOnUsers")]
partial class permissionTagsOnUsers
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "7.0.5")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("vassago.Models.Account", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("DisplayName")
.HasColumnType("text");
b.Property<string>("ExternalId")
.HasColumnType("text");
b.Property<bool>("IsBot")
.HasColumnType("boolean");
b.Property<Guid?>("IsUserId")
.HasColumnType("uuid");
b.Property<int[]>("PermissionTags")
.HasColumnType("integer[]");
b.Property<string>("Protocol")
.HasColumnType("text");
b.Property<Guid?>("SeenInChannelId")
.HasColumnType("uuid");
b.Property<string>("Username")
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("IsUserId");
b.HasIndex("SeenInChannelId");
b.ToTable("Accounts");
});
modelBuilder.Entity("vassago.Models.Attachment", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<byte[]>("Content")
.HasColumnType("bytea");
b.Property<string>("ContentType")
.HasColumnType("text");
b.Property<string>("Description")
.HasColumnType("text");
b.Property<decimal?>("ExternalId")
.HasColumnType("numeric(20,0)");
b.Property<string>("Filename")
.HasColumnType("text");
b.Property<Guid?>("MessageId")
.HasColumnType("uuid");
b.Property<int>("Size")
.HasColumnType("integer");
b.Property<string>("Source")
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("MessageId");
b.ToTable("Attachments");
});
modelBuilder.Entity("vassago.Models.Channel", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("DisplayName")
.HasColumnType("text");
b.Property<string>("ExternalId")
.HasColumnType("text");
b.Property<bool>("IsDM")
.HasColumnType("boolean");
b.Property<Guid?>("ParentChannelId")
.HasColumnType("uuid");
b.Property<int?>("PermissionsId")
.HasColumnType("integer");
b.Property<string>("Protocol")
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("ParentChannelId");
b.HasIndex("PermissionsId");
b.ToTable("Channels");
});
modelBuilder.Entity("vassago.Models.Message", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<bool>("ActedOn")
.HasColumnType("boolean");
b.Property<Guid?>("AuthorId")
.HasColumnType("uuid");
b.Property<Guid?>("ChannelId")
.HasColumnType("uuid");
b.Property<string>("Content")
.HasColumnType("text");
b.Property<string>("ExternalId")
.HasColumnType("text");
b.Property<bool>("MentionsMe")
.HasColumnType("boolean");
b.Property<string>("Protocol")
.HasColumnType("text");
b.Property<DateTimeOffset>("Timestamp")
.HasColumnType("timestamp with time zone");
b.HasKey("Id");
b.HasIndex("AuthorId");
b.HasIndex("ChannelId");
b.ToTable("Messages");
});
modelBuilder.Entity("vassago.Models.PermissionSettings", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<int?>("LewdnessFilterLevel")
.HasColumnType("integer");
b.Property<bool?>("LinksAllowed")
.HasColumnType("boolean");
b.Property<decimal?>("MaxAttachmentBytes")
.HasColumnType("numeric(20,0)");
b.Property<long?>("MaxTextChars")
.HasColumnType("bigint");
b.Property<int?>("MeannessFilterLevel")
.HasColumnType("integer");
b.Property<bool?>("ReactionsPossible")
.HasColumnType("boolean");
b.HasKey("Id");
b.ToTable("PermissionSettings");
});
modelBuilder.Entity("vassago.Models.User", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<int[]>("PermissionTags")
.HasColumnType("integer[]");
b.HasKey("Id");
b.ToTable("Users");
});
modelBuilder.Entity("vassago.Models.Account", b =>
{
b.HasOne("vassago.Models.User", "IsUser")
.WithMany("Accounts")
.HasForeignKey("IsUserId");
b.HasOne("vassago.Models.Channel", "SeenInChannel")
.WithMany("Users")
.HasForeignKey("SeenInChannelId");
b.Navigation("IsUser");
b.Navigation("SeenInChannel");
});
modelBuilder.Entity("vassago.Models.Attachment", b =>
{
b.HasOne("vassago.Models.Message", "Message")
.WithMany("Attachments")
.HasForeignKey("MessageId");
b.Navigation("Message");
});
modelBuilder.Entity("vassago.Models.Channel", b =>
{
b.HasOne("vassago.Models.Channel", "ParentChannel")
.WithMany("SubChannels")
.HasForeignKey("ParentChannelId");
b.HasOne("vassago.Models.PermissionSettings", "Permissions")
.WithMany()
.HasForeignKey("PermissionsId");
b.Navigation("ParentChannel");
b.Navigation("Permissions");
});
modelBuilder.Entity("vassago.Models.Message", b =>
{
b.HasOne("vassago.Models.Account", "Author")
.WithMany()
.HasForeignKey("AuthorId");
b.HasOne("vassago.Models.Channel", "Channel")
.WithMany("Messages")
.HasForeignKey("ChannelId");
b.Navigation("Author");
b.Navigation("Channel");
});
modelBuilder.Entity("vassago.Models.Channel", b =>
{
b.Navigation("Messages");
b.Navigation("SubChannels");
b.Navigation("Users");
});
modelBuilder.Entity("vassago.Models.Message", b =>
{
b.Navigation("Attachments");
});
modelBuilder.Entity("vassago.Models.User", b =>
{
b.Navigation("Accounts");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -1,28 +0,0 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace vassago.Migrations
{
/// <inheritdoc />
public partial class permissionTagsOnUsers : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<int[]>(
name: "PermissionTags",
table: "Users",
type: "integer[]",
nullable: true);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "PermissionTags",
table: "Users");
}
}
}

View File

@ -1,349 +0,0 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using vassago.Models;
#nullable disable
namespace vassago.Migrations
{
[DbContext(typeof(ChattingContext))]
[Migration("20231130204741_Feature Permissions")]
partial class FeaturePermissions
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "7.0.5")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("vassago.Models.Account", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("DisplayName")
.HasColumnType("text");
b.Property<string>("ExternalId")
.HasColumnType("text");
b.Property<Guid?>("FeaturePermissionId")
.HasColumnType("uuid");
b.Property<bool>("IsBot")
.HasColumnType("boolean");
b.Property<Guid?>("IsUserId")
.HasColumnType("uuid");
b.Property<string>("Protocol")
.HasColumnType("text");
b.Property<Guid?>("SeenInChannelId")
.HasColumnType("uuid");
b.Property<string>("Username")
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("FeaturePermissionId");
b.HasIndex("IsUserId");
b.HasIndex("SeenInChannelId");
b.ToTable("Accounts");
});
modelBuilder.Entity("vassago.Models.Attachment", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<byte[]>("Content")
.HasColumnType("bytea");
b.Property<string>("ContentType")
.HasColumnType("text");
b.Property<string>("Description")
.HasColumnType("text");
b.Property<decimal?>("ExternalId")
.HasColumnType("numeric(20,0)");
b.Property<string>("Filename")
.HasColumnType("text");
b.Property<Guid?>("MessageId")
.HasColumnType("uuid");
b.Property<int>("Size")
.HasColumnType("integer");
b.Property<string>("Source")
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("MessageId");
b.ToTable("Attachments");
});
modelBuilder.Entity("vassago.Models.Channel", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("DisplayName")
.HasColumnType("text");
b.Property<string>("ExternalId")
.HasColumnType("text");
b.Property<Guid?>("FeaturePermissionId")
.HasColumnType("uuid");
b.Property<bool>("IsDM")
.HasColumnType("boolean");
b.Property<Guid?>("ParentChannelId")
.HasColumnType("uuid");
b.Property<int?>("PermissionsId")
.HasColumnType("integer");
b.Property<string>("Protocol")
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("FeaturePermissionId");
b.HasIndex("ParentChannelId");
b.HasIndex("PermissionsId");
b.ToTable("Channels");
});
modelBuilder.Entity("vassago.Models.ChannelPermissions", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<int?>("LewdnessFilterLevel")
.HasColumnType("integer");
b.Property<bool?>("LinksAllowed")
.HasColumnType("boolean");
b.Property<decimal?>("MaxAttachmentBytes")
.HasColumnType("numeric(20,0)");
b.Property<long?>("MaxTextChars")
.HasColumnType("bigint");
b.Property<int?>("MeannessFilterLevel")
.HasColumnType("integer");
b.Property<bool?>("ReactionsPossible")
.HasColumnType("boolean");
b.HasKey("Id");
b.ToTable("ChannelPermissions");
});
modelBuilder.Entity("vassago.Models.FeaturePermission", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<bool>("Inheritable")
.HasColumnType("boolean");
b.Property<string>("InternalName")
.HasColumnType("text");
b.Property<int?>("InternalTag")
.HasColumnType("integer");
b.HasKey("Id");
b.ToTable("FeaturePermissions");
});
modelBuilder.Entity("vassago.Models.Message", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<bool>("ActedOn")
.HasColumnType("boolean");
b.Property<Guid?>("AuthorId")
.HasColumnType("uuid");
b.Property<Guid?>("ChannelId")
.HasColumnType("uuid");
b.Property<string>("Content")
.HasColumnType("text");
b.Property<string>("ExternalId")
.HasColumnType("text");
b.Property<bool>("MentionsMe")
.HasColumnType("boolean");
b.Property<string>("Protocol")
.HasColumnType("text");
b.Property<DateTimeOffset>("Timestamp")
.HasColumnType("timestamp with time zone");
b.HasKey("Id");
b.HasIndex("AuthorId");
b.HasIndex("ChannelId");
b.ToTable("Messages");
});
modelBuilder.Entity("vassago.Models.User", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<Guid?>("FeaturePermissionId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("FeaturePermissionId");
b.ToTable("Users");
});
modelBuilder.Entity("vassago.Models.Account", b =>
{
b.HasOne("vassago.Models.FeaturePermission", null)
.WithMany("RestrictedToAccounts")
.HasForeignKey("FeaturePermissionId");
b.HasOne("vassago.Models.User", "IsUser")
.WithMany("Accounts")
.HasForeignKey("IsUserId");
b.HasOne("vassago.Models.Channel", "SeenInChannel")
.WithMany("Users")
.HasForeignKey("SeenInChannelId");
b.Navigation("IsUser");
b.Navigation("SeenInChannel");
});
modelBuilder.Entity("vassago.Models.Attachment", b =>
{
b.HasOne("vassago.Models.Message", "Message")
.WithMany("Attachments")
.HasForeignKey("MessageId");
b.Navigation("Message");
});
modelBuilder.Entity("vassago.Models.Channel", b =>
{
b.HasOne("vassago.Models.FeaturePermission", null)
.WithMany("RestrictedToChannels")
.HasForeignKey("FeaturePermissionId");
b.HasOne("vassago.Models.Channel", "ParentChannel")
.WithMany("SubChannels")
.HasForeignKey("ParentChannelId");
b.HasOne("vassago.Models.ChannelPermissions", "Permissions")
.WithMany()
.HasForeignKey("PermissionsId");
b.Navigation("ParentChannel");
b.Navigation("Permissions");
});
modelBuilder.Entity("vassago.Models.Message", b =>
{
b.HasOne("vassago.Models.Account", "Author")
.WithMany()
.HasForeignKey("AuthorId");
b.HasOne("vassago.Models.Channel", "Channel")
.WithMany("Messages")
.HasForeignKey("ChannelId");
b.Navigation("Author");
b.Navigation("Channel");
});
modelBuilder.Entity("vassago.Models.User", b =>
{
b.HasOne("vassago.Models.FeaturePermission", null)
.WithMany("RestrictedToUsers")
.HasForeignKey("FeaturePermissionId");
});
modelBuilder.Entity("vassago.Models.Channel", b =>
{
b.Navigation("Messages");
b.Navigation("SubChannels");
b.Navigation("Users");
});
modelBuilder.Entity("vassago.Models.FeaturePermission", b =>
{
b.Navigation("RestrictedToAccounts");
b.Navigation("RestrictedToChannels");
b.Navigation("RestrictedToUsers");
});
modelBuilder.Entity("vassago.Models.Message", b =>
{
b.Navigation("Attachments");
});
modelBuilder.Entity("vassago.Models.User", b =>
{
b.Navigation("Accounts");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -1,211 +0,0 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace vassago.Migrations
{
/// <inheritdoc />
public partial class FeaturePermissions : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_Channels_PermissionSettings_PermissionsId",
table: "Channels");
migrationBuilder.DropTable(
name: "PermissionSettings");
migrationBuilder.DropColumn(
name: "PermissionTags",
table: "Users");
migrationBuilder.DropColumn(
name: "PermissionTags",
table: "Accounts");
migrationBuilder.AddColumn<Guid>(
name: "FeaturePermissionId",
table: "Users",
type: "uuid",
nullable: true);
migrationBuilder.AddColumn<Guid>(
name: "FeaturePermissionId",
table: "Channels",
type: "uuid",
nullable: true);
migrationBuilder.AddColumn<Guid>(
name: "FeaturePermissionId",
table: "Accounts",
type: "uuid",
nullable: true);
migrationBuilder.CreateTable(
name: "ChannelPermissions",
columns: table => new
{
Id = table.Column<int>(type: "integer", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
MaxAttachmentBytes = table.Column<decimal>(type: "numeric(20,0)", nullable: true),
MaxTextChars = table.Column<long>(type: "bigint", nullable: true),
LinksAllowed = table.Column<bool>(type: "boolean", nullable: true),
ReactionsPossible = table.Column<bool>(type: "boolean", nullable: true),
LewdnessFilterLevel = table.Column<int>(type: "integer", nullable: true),
MeannessFilterLevel = table.Column<int>(type: "integer", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_ChannelPermissions", x => x.Id);
});
migrationBuilder.CreateTable(
name: "FeaturePermissions",
columns: table => new
{
Id = table.Column<Guid>(type: "uuid", nullable: false),
InternalName = table.Column<string>(type: "text", nullable: true),
InternalTag = table.Column<int>(type: "integer", nullable: true),
Inheritable = table.Column<bool>(type: "boolean", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_FeaturePermissions", x => x.Id);
});
migrationBuilder.CreateIndex(
name: "IX_Users_FeaturePermissionId",
table: "Users",
column: "FeaturePermissionId");
migrationBuilder.CreateIndex(
name: "IX_Channels_FeaturePermissionId",
table: "Channels",
column: "FeaturePermissionId");
migrationBuilder.CreateIndex(
name: "IX_Accounts_FeaturePermissionId",
table: "Accounts",
column: "FeaturePermissionId");
migrationBuilder.AddForeignKey(
name: "FK_Accounts_FeaturePermissions_FeaturePermissionId",
table: "Accounts",
column: "FeaturePermissionId",
principalTable: "FeaturePermissions",
principalColumn: "Id");
migrationBuilder.AddForeignKey(
name: "FK_Channels_ChannelPermissions_PermissionsId",
table: "Channels",
column: "PermissionsId",
principalTable: "ChannelPermissions",
principalColumn: "Id");
migrationBuilder.AddForeignKey(
name: "FK_Channels_FeaturePermissions_FeaturePermissionId",
table: "Channels",
column: "FeaturePermissionId",
principalTable: "FeaturePermissions",
principalColumn: "Id");
migrationBuilder.AddForeignKey(
name: "FK_Users_FeaturePermissions_FeaturePermissionId",
table: "Users",
column: "FeaturePermissionId",
principalTable: "FeaturePermissions",
principalColumn: "Id");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_Accounts_FeaturePermissions_FeaturePermissionId",
table: "Accounts");
migrationBuilder.DropForeignKey(
name: "FK_Channels_ChannelPermissions_PermissionsId",
table: "Channels");
migrationBuilder.DropForeignKey(
name: "FK_Channels_FeaturePermissions_FeaturePermissionId",
table: "Channels");
migrationBuilder.DropForeignKey(
name: "FK_Users_FeaturePermissions_FeaturePermissionId",
table: "Users");
migrationBuilder.DropTable(
name: "ChannelPermissions");
migrationBuilder.DropTable(
name: "FeaturePermissions");
migrationBuilder.DropIndex(
name: "IX_Users_FeaturePermissionId",
table: "Users");
migrationBuilder.DropIndex(
name: "IX_Channels_FeaturePermissionId",
table: "Channels");
migrationBuilder.DropIndex(
name: "IX_Accounts_FeaturePermissionId",
table: "Accounts");
migrationBuilder.DropColumn(
name: "FeaturePermissionId",
table: "Users");
migrationBuilder.DropColumn(
name: "FeaturePermissionId",
table: "Channels");
migrationBuilder.DropColumn(
name: "FeaturePermissionId",
table: "Accounts");
migrationBuilder.AddColumn<int[]>(
name: "PermissionTags",
table: "Users",
type: "integer[]",
nullable: true);
migrationBuilder.AddColumn<int[]>(
name: "PermissionTags",
table: "Accounts",
type: "integer[]",
nullable: true);
migrationBuilder.CreateTable(
name: "PermissionSettings",
columns: table => new
{
Id = table.Column<int>(type: "integer", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
LewdnessFilterLevel = table.Column<int>(type: "integer", nullable: true),
LinksAllowed = table.Column<bool>(type: "boolean", nullable: true),
MaxAttachmentBytes = table.Column<decimal>(type: "numeric(20,0)", nullable: true),
MaxTextChars = table.Column<long>(type: "bigint", nullable: true),
MeannessFilterLevel = table.Column<int>(type: "integer", nullable: true),
ReactionsPossible = table.Column<bool>(type: "boolean", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_PermissionSettings", x => x.Id);
});
migrationBuilder.AddForeignKey(
name: "FK_Channels_PermissionSettings_PermissionsId",
table: "Channels",
column: "PermissionsId",
principalTable: "PermissionSettings",
principalColumn: "Id");
}
}
}

View File

@ -1,349 +0,0 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using vassago.Models;
#nullable disable
namespace vassago.Migrations
{
[DbContext(typeof(ChattingContext))]
[Migration("20231203193139_channeltype")]
partial class ChannelType
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "7.0.5")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("vassago.Models.Account", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("DisplayName")
.HasColumnType("text");
b.Property<string>("ExternalId")
.HasColumnType("text");
b.Property<Guid?>("FeaturePermissionId")
.HasColumnType("uuid");
b.Property<bool>("IsBot")
.HasColumnType("boolean");
b.Property<Guid?>("IsUserId")
.HasColumnType("uuid");
b.Property<string>("Protocol")
.HasColumnType("text");
b.Property<Guid?>("SeenInChannelId")
.HasColumnType("uuid");
b.Property<string>("Username")
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("FeaturePermissionId");
b.HasIndex("IsUserId");
b.HasIndex("SeenInChannelId");
b.ToTable("Accounts");
});
modelBuilder.Entity("vassago.Models.Attachment", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<byte[]>("Content")
.HasColumnType("bytea");
b.Property<string>("ContentType")
.HasColumnType("text");
b.Property<string>("Description")
.HasColumnType("text");
b.Property<decimal?>("ExternalId")
.HasColumnType("numeric(20,0)");
b.Property<string>("Filename")
.HasColumnType("text");
b.Property<Guid?>("MessageId")
.HasColumnType("uuid");
b.Property<int>("Size")
.HasColumnType("integer");
b.Property<string>("Source")
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("MessageId");
b.ToTable("Attachments");
});
modelBuilder.Entity("vassago.Models.Channel", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<int>("ChannelType")
.HasColumnType("integer");
b.Property<string>("DisplayName")
.HasColumnType("text");
b.Property<string>("ExternalId")
.HasColumnType("text");
b.Property<Guid?>("FeaturePermissionId")
.HasColumnType("uuid");
b.Property<Guid?>("ParentChannelId")
.HasColumnType("uuid");
b.Property<int?>("PermissionsId")
.HasColumnType("integer");
b.Property<string>("Protocol")
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("FeaturePermissionId");
b.HasIndex("ParentChannelId");
b.HasIndex("PermissionsId");
b.ToTable("Channels");
});
modelBuilder.Entity("vassago.Models.ChannelPermissions", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<int?>("LewdnessFilterLevel")
.HasColumnType("integer");
b.Property<bool?>("LinksAllowed")
.HasColumnType("boolean");
b.Property<decimal?>("MaxAttachmentBytes")
.HasColumnType("numeric(20,0)");
b.Property<long?>("MaxTextChars")
.HasColumnType("bigint");
b.Property<int?>("MeannessFilterLevel")
.HasColumnType("integer");
b.Property<bool?>("ReactionsPossible")
.HasColumnType("boolean");
b.HasKey("Id");
b.ToTable("ChannelPermissions");
});
modelBuilder.Entity("vassago.Models.FeaturePermission", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<bool>("Inheritable")
.HasColumnType("boolean");
b.Property<string>("InternalName")
.HasColumnType("text");
b.Property<int?>("InternalTag")
.HasColumnType("integer");
b.HasKey("Id");
b.ToTable("FeaturePermissions");
});
modelBuilder.Entity("vassago.Models.Message", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<bool>("ActedOn")
.HasColumnType("boolean");
b.Property<Guid?>("AuthorId")
.HasColumnType("uuid");
b.Property<Guid?>("ChannelId")
.HasColumnType("uuid");
b.Property<string>("Content")
.HasColumnType("text");
b.Property<string>("ExternalId")
.HasColumnType("text");
b.Property<bool>("MentionsMe")
.HasColumnType("boolean");
b.Property<string>("Protocol")
.HasColumnType("text");
b.Property<DateTimeOffset>("Timestamp")
.HasColumnType("timestamp with time zone");
b.HasKey("Id");
b.HasIndex("AuthorId");
b.HasIndex("ChannelId");
b.ToTable("Messages");
});
modelBuilder.Entity("vassago.Models.User", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<Guid?>("FeaturePermissionId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("FeaturePermissionId");
b.ToTable("Users");
});
modelBuilder.Entity("vassago.Models.Account", b =>
{
b.HasOne("vassago.Models.FeaturePermission", null)
.WithMany("RestrictedToAccounts")
.HasForeignKey("FeaturePermissionId");
b.HasOne("vassago.Models.User", "IsUser")
.WithMany("Accounts")
.HasForeignKey("IsUserId");
b.HasOne("vassago.Models.Channel", "SeenInChannel")
.WithMany("Users")
.HasForeignKey("SeenInChannelId");
b.Navigation("IsUser");
b.Navigation("SeenInChannel");
});
modelBuilder.Entity("vassago.Models.Attachment", b =>
{
b.HasOne("vassago.Models.Message", "Message")
.WithMany("Attachments")
.HasForeignKey("MessageId");
b.Navigation("Message");
});
modelBuilder.Entity("vassago.Models.Channel", b =>
{
b.HasOne("vassago.Models.FeaturePermission", null)
.WithMany("RestrictedToChannels")
.HasForeignKey("FeaturePermissionId");
b.HasOne("vassago.Models.Channel", "ParentChannel")
.WithMany("SubChannels")
.HasForeignKey("ParentChannelId");
b.HasOne("vassago.Models.ChannelPermissions", "Permissions")
.WithMany()
.HasForeignKey("PermissionsId");
b.Navigation("ParentChannel");
b.Navigation("Permissions");
});
modelBuilder.Entity("vassago.Models.Message", b =>
{
b.HasOne("vassago.Models.Account", "Author")
.WithMany()
.HasForeignKey("AuthorId");
b.HasOne("vassago.Models.Channel", "Channel")
.WithMany("Messages")
.HasForeignKey("ChannelId");
b.Navigation("Author");
b.Navigation("Channel");
});
modelBuilder.Entity("vassago.Models.User", b =>
{
b.HasOne("vassago.Models.FeaturePermission", null)
.WithMany("RestrictedToUsers")
.HasForeignKey("FeaturePermissionId");
});
modelBuilder.Entity("vassago.Models.Channel", b =>
{
b.Navigation("Messages");
b.Navigation("SubChannels");
b.Navigation("Users");
});
modelBuilder.Entity("vassago.Models.FeaturePermission", b =>
{
b.Navigation("RestrictedToAccounts");
b.Navigation("RestrictedToChannels");
b.Navigation("RestrictedToUsers");
});
modelBuilder.Entity("vassago.Models.Message", b =>
{
b.Navigation("Attachments");
});
modelBuilder.Entity("vassago.Models.User", b =>
{
b.Navigation("Accounts");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -1,40 +0,0 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace vassago.Migrations
{
/// <inheritdoc />
public partial class ChannelType : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "IsDM",
table: "Channels");
migrationBuilder.AddColumn<int>(
name: "ChannelType",
table: "Channels",
type: "integer",
nullable: false,
defaultValue: 0);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "ChannelType",
table: "Channels");
migrationBuilder.AddColumn<bool>(
name: "IsDM",
table: "Channels",
type: "boolean",
nullable: false,
defaultValue: false);
}
}
}

View File

@ -1,266 +0,0 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using vassago.Models;
#nullable disable
namespace vassago.Migrations
{
[DbContext(typeof(ChattingContext))]
[Migration("20240510202057_channelpermissions_partofchannel")]
partial class Channelpermissions_partofchannel
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "7.0.5")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("vassago.Models.Account", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("DisplayName")
.HasColumnType("text");
b.Property<string>("ExternalId")
.HasColumnType("text");
b.Property<bool>("IsBot")
.HasColumnType("boolean");
b.Property<Guid?>("IsUserId")
.HasColumnType("uuid");
b.Property<string>("Protocol")
.HasColumnType("text");
b.Property<Guid?>("SeenInChannelId")
.HasColumnType("uuid");
b.Property<string>("Username")
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("IsUserId");
b.HasIndex("SeenInChannelId");
b.ToTable("Accounts");
});
modelBuilder.Entity("vassago.Models.Attachment", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<byte[]>("Content")
.HasColumnType("bytea");
b.Property<string>("ContentType")
.HasColumnType("text");
b.Property<string>("Description")
.HasColumnType("text");
b.Property<decimal?>("ExternalId")
.HasColumnType("numeric(20,0)");
b.Property<string>("Filename")
.HasColumnType("text");
b.Property<Guid?>("MessageId")
.HasColumnType("uuid");
b.Property<int>("Size")
.HasColumnType("integer");
b.Property<string>("Source")
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("MessageId");
b.ToTable("Attachments");
});
modelBuilder.Entity("vassago.Models.Channel", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<int>("ChannelType")
.HasColumnType("integer");
b.Property<string>("DisplayName")
.HasColumnType("text");
b.Property<string>("ExternalId")
.HasColumnType("text");
b.Property<int?>("LewdnessFilterLevel")
.HasColumnType("integer");
b.Property<bool?>("LinksAllowed")
.HasColumnType("boolean");
b.Property<decimal?>("MaxAttachmentBytes")
.HasColumnType("numeric(20,0)");
b.Property<long?>("MaxTextChars")
.HasColumnType("bigint");
b.Property<int?>("MeannessFilterLevel")
.HasColumnType("integer");
b.Property<Guid?>("ParentChannelId")
.HasColumnType("uuid");
b.Property<string>("Protocol")
.HasColumnType("text");
b.Property<bool?>("ReactionsPossible")
.HasColumnType("boolean");
b.HasKey("Id");
b.HasIndex("ParentChannelId");
b.ToTable("Channels");
});
modelBuilder.Entity("vassago.Models.Message", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<bool>("ActedOn")
.HasColumnType("boolean");
b.Property<Guid?>("AuthorId")
.HasColumnType("uuid");
b.Property<Guid?>("ChannelId")
.HasColumnType("uuid");
b.Property<string>("Content")
.HasColumnType("text");
b.Property<string>("ExternalId")
.HasColumnType("text");
b.Property<bool>("MentionsMe")
.HasColumnType("boolean");
b.Property<string>("Protocol")
.HasColumnType("text");
b.Property<DateTimeOffset>("Timestamp")
.HasColumnType("timestamp with time zone");
b.HasKey("Id");
b.HasIndex("AuthorId");
b.HasIndex("ChannelId");
b.ToTable("Messages");
});
modelBuilder.Entity("vassago.Models.User", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.HasKey("Id");
b.ToTable("Users");
});
modelBuilder.Entity("vassago.Models.Account", b =>
{
b.HasOne("vassago.Models.User", "IsUser")
.WithMany("Accounts")
.HasForeignKey("IsUserId");
b.HasOne("vassago.Models.Channel", "SeenInChannel")
.WithMany("Users")
.HasForeignKey("SeenInChannelId");
b.Navigation("IsUser");
b.Navigation("SeenInChannel");
});
modelBuilder.Entity("vassago.Models.Attachment", b =>
{
b.HasOne("vassago.Models.Message", "Message")
.WithMany("Attachments")
.HasForeignKey("MessageId");
b.Navigation("Message");
});
modelBuilder.Entity("vassago.Models.Channel", b =>
{
b.HasOne("vassago.Models.Channel", "ParentChannel")
.WithMany("SubChannels")
.HasForeignKey("ParentChannelId");
b.Navigation("ParentChannel");
});
modelBuilder.Entity("vassago.Models.Message", b =>
{
b.HasOne("vassago.Models.Account", "Author")
.WithMany()
.HasForeignKey("AuthorId");
b.HasOne("vassago.Models.Channel", "Channel")
.WithMany("Messages")
.HasForeignKey("ChannelId");
b.Navigation("Author");
b.Navigation("Channel");
});
modelBuilder.Entity("vassago.Models.Channel", b =>
{
b.Navigation("Messages");
b.Navigation("SubChannels");
b.Navigation("Users");
});
modelBuilder.Entity("vassago.Models.Message", b =>
{
b.Navigation("Attachments");
});
modelBuilder.Entity("vassago.Models.User", b =>
{
b.Navigation("Accounts");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -1,228 +0,0 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace vassago.Migrations
{
/// <inheritdoc />
public partial class Channelpermissions_partofchannel : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_Accounts_FeaturePermissions_FeaturePermissionId",
table: "Accounts");
migrationBuilder.DropForeignKey(
name: "FK_Channels_ChannelPermissions_PermissionsId",
table: "Channels");
migrationBuilder.DropForeignKey(
name: "FK_Channels_FeaturePermissions_FeaturePermissionId",
table: "Channels");
migrationBuilder.DropForeignKey(
name: "FK_Users_FeaturePermissions_FeaturePermissionId",
table: "Users");
migrationBuilder.DropTable(
name: "ChannelPermissions");
migrationBuilder.DropTable(
name: "FeaturePermissions");
migrationBuilder.DropIndex(
name: "IX_Users_FeaturePermissionId",
table: "Users");
migrationBuilder.DropIndex(
name: "IX_Channels_FeaturePermissionId",
table: "Channels");
migrationBuilder.DropIndex(
name: "IX_Channels_PermissionsId",
table: "Channels");
migrationBuilder.DropIndex(
name: "IX_Accounts_FeaturePermissionId",
table: "Accounts");
migrationBuilder.DropColumn(
name: "FeaturePermissionId",
table: "Users");
migrationBuilder.DropColumn(
name: "FeaturePermissionId",
table: "Channels");
migrationBuilder.DropColumn(
name: "FeaturePermissionId",
table: "Accounts");
migrationBuilder.RenameColumn(
name: "PermissionsId",
table: "Channels",
newName: "MeannessFilterLevel");
migrationBuilder.AddColumn<int>(
name: "LewdnessFilterLevel",
table: "Channels",
type: "integer",
nullable: true);
migrationBuilder.AddColumn<bool>(
name: "LinksAllowed",
table: "Channels",
type: "boolean",
nullable: true);
migrationBuilder.AddColumn<decimal>(
name: "MaxAttachmentBytes",
table: "Channels",
type: "numeric(20,0)",
nullable: true);
migrationBuilder.AddColumn<long>(
name: "MaxTextChars",
table: "Channels",
type: "bigint",
nullable: true);
migrationBuilder.AddColumn<bool>(
name: "ReactionsPossible",
table: "Channels",
type: "boolean",
nullable: true);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "LewdnessFilterLevel",
table: "Channels");
migrationBuilder.DropColumn(
name: "LinksAllowed",
table: "Channels");
migrationBuilder.DropColumn(
name: "MaxAttachmentBytes",
table: "Channels");
migrationBuilder.DropColumn(
name: "MaxTextChars",
table: "Channels");
migrationBuilder.DropColumn(
name: "ReactionsPossible",
table: "Channels");
migrationBuilder.RenameColumn(
name: "MeannessFilterLevel",
table: "Channels",
newName: "PermissionsId");
migrationBuilder.AddColumn<Guid>(
name: "FeaturePermissionId",
table: "Users",
type: "uuid",
nullable: true);
migrationBuilder.AddColumn<Guid>(
name: "FeaturePermissionId",
table: "Channels",
type: "uuid",
nullable: true);
migrationBuilder.AddColumn<Guid>(
name: "FeaturePermissionId",
table: "Accounts",
type: "uuid",
nullable: true);
migrationBuilder.CreateTable(
name: "ChannelPermissions",
columns: table => new
{
Id = table.Column<int>(type: "integer", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
LewdnessFilterLevel = table.Column<int>(type: "integer", nullable: true),
LinksAllowed = table.Column<bool>(type: "boolean", nullable: true),
MaxAttachmentBytes = table.Column<decimal>(type: "numeric(20,0)", nullable: true),
MaxTextChars = table.Column<long>(type: "bigint", nullable: true),
MeannessFilterLevel = table.Column<int>(type: "integer", nullable: true),
ReactionsPossible = table.Column<bool>(type: "boolean", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_ChannelPermissions", x => x.Id);
});
migrationBuilder.CreateTable(
name: "FeaturePermissions",
columns: table => new
{
Id = table.Column<Guid>(type: "uuid", nullable: false),
Inheritable = table.Column<bool>(type: "boolean", nullable: false),
InternalName = table.Column<string>(type: "text", nullable: true),
InternalTag = table.Column<int>(type: "integer", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_FeaturePermissions", x => x.Id);
});
migrationBuilder.CreateIndex(
name: "IX_Users_FeaturePermissionId",
table: "Users",
column: "FeaturePermissionId");
migrationBuilder.CreateIndex(
name: "IX_Channels_FeaturePermissionId",
table: "Channels",
column: "FeaturePermissionId");
migrationBuilder.CreateIndex(
name: "IX_Channels_PermissionsId",
table: "Channels",
column: "PermissionsId");
migrationBuilder.CreateIndex(
name: "IX_Accounts_FeaturePermissionId",
table: "Accounts",
column: "FeaturePermissionId");
migrationBuilder.AddForeignKey(
name: "FK_Accounts_FeaturePermissions_FeaturePermissionId",
table: "Accounts",
column: "FeaturePermissionId",
principalTable: "FeaturePermissions",
principalColumn: "Id");
migrationBuilder.AddForeignKey(
name: "FK_Channels_ChannelPermissions_PermissionsId",
table: "Channels",
column: "PermissionsId",
principalTable: "ChannelPermissions",
principalColumn: "Id");
migrationBuilder.AddForeignKey(
name: "FK_Channels_FeaturePermissions_FeaturePermissionId",
table: "Channels",
column: "FeaturePermissionId",
principalTable: "FeaturePermissions",
principalColumn: "Id");
migrationBuilder.AddForeignKey(
name: "FK_Users_FeaturePermissions_FeaturePermissionId",
table: "Users",
column: "FeaturePermissionId",
principalTable: "FeaturePermissions",
principalColumn: "Id");
}
}
}

View File

@ -1,271 +0,0 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using vassago.Models;
#nullable disable
namespace vassago.Migrations
{
[DbContext(typeof(ChattingContext))]
[Migration("20250204004906_cascade")]
partial class Cascade
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "7.0.5")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("vassago.Models.Account", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("DisplayName")
.HasColumnType("text");
b.Property<string>("ExternalId")
.HasColumnType("text");
b.Property<bool>("IsBot")
.HasColumnType("boolean");
b.Property<Guid?>("IsUserId")
.HasColumnType("uuid");
b.Property<string>("Protocol")
.HasColumnType("text");
b.Property<Guid?>("SeenInChannelId")
.HasColumnType("uuid");
b.Property<string>("Username")
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("IsUserId");
b.HasIndex("SeenInChannelId");
b.ToTable("Accounts");
});
modelBuilder.Entity("vassago.Models.Attachment", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<byte[]>("Content")
.HasColumnType("bytea");
b.Property<string>("ContentType")
.HasColumnType("text");
b.Property<string>("Description")
.HasColumnType("text");
b.Property<decimal?>("ExternalId")
.HasColumnType("numeric(20,0)");
b.Property<string>("Filename")
.HasColumnType("text");
b.Property<Guid?>("MessageId")
.HasColumnType("uuid");
b.Property<int>("Size")
.HasColumnType("integer");
b.Property<string>("Source")
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("MessageId");
b.ToTable("Attachments");
});
modelBuilder.Entity("vassago.Models.Channel", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<int>("ChannelType")
.HasColumnType("integer");
b.Property<string>("DisplayName")
.HasColumnType("text");
b.Property<string>("ExternalId")
.HasColumnType("text");
b.Property<int?>("LewdnessFilterLevel")
.HasColumnType("integer");
b.Property<bool?>("LinksAllowed")
.HasColumnType("boolean");
b.Property<decimal?>("MaxAttachmentBytes")
.HasColumnType("numeric(20,0)");
b.Property<long?>("MaxTextChars")
.HasColumnType("bigint");
b.Property<int?>("MeannessFilterLevel")
.HasColumnType("integer");
b.Property<Guid?>("ParentChannelId")
.HasColumnType("uuid");
b.Property<string>("Protocol")
.HasColumnType("text");
b.Property<bool?>("ReactionsPossible")
.HasColumnType("boolean");
b.HasKey("Id");
b.HasIndex("ParentChannelId");
b.ToTable("Channels");
});
modelBuilder.Entity("vassago.Models.Message", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<bool>("ActedOn")
.HasColumnType("boolean");
b.Property<Guid?>("AuthorId")
.HasColumnType("uuid");
b.Property<Guid?>("ChannelId")
.HasColumnType("uuid");
b.Property<string>("Content")
.HasColumnType("text");
b.Property<string>("ExternalId")
.HasColumnType("text");
b.Property<bool>("MentionsMe")
.HasColumnType("boolean");
b.Property<string>("Protocol")
.HasColumnType("text");
b.Property<DateTimeOffset>("Timestamp")
.HasColumnType("timestamp with time zone");
b.HasKey("Id");
b.HasIndex("AuthorId");
b.HasIndex("ChannelId");
b.ToTable("Messages");
});
modelBuilder.Entity("vassago.Models.User", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.HasKey("Id");
b.ToTable("Users");
});
modelBuilder.Entity("vassago.Models.Account", b =>
{
b.HasOne("vassago.Models.User", "IsUser")
.WithMany("Accounts")
.HasForeignKey("IsUserId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("vassago.Models.Channel", "SeenInChannel")
.WithMany("Users")
.HasForeignKey("SeenInChannelId")
.OnDelete(DeleteBehavior.Cascade);
b.Navigation("IsUser");
b.Navigation("SeenInChannel");
});
modelBuilder.Entity("vassago.Models.Attachment", b =>
{
b.HasOne("vassago.Models.Message", "Message")
.WithMany("Attachments")
.HasForeignKey("MessageId")
.OnDelete(DeleteBehavior.Cascade);
b.Navigation("Message");
});
modelBuilder.Entity("vassago.Models.Channel", b =>
{
b.HasOne("vassago.Models.Channel", "ParentChannel")
.WithMany("SubChannels")
.HasForeignKey("ParentChannelId")
.OnDelete(DeleteBehavior.Cascade);
b.Navigation("ParentChannel");
});
modelBuilder.Entity("vassago.Models.Message", b =>
{
b.HasOne("vassago.Models.Account", "Author")
.WithMany()
.HasForeignKey("AuthorId");
b.HasOne("vassago.Models.Channel", "Channel")
.WithMany("Messages")
.HasForeignKey("ChannelId")
.OnDelete(DeleteBehavior.Cascade);
b.Navigation("Author");
b.Navigation("Channel");
});
modelBuilder.Entity("vassago.Models.Channel", b =>
{
b.Navigation("Messages");
b.Navigation("SubChannels");
b.Navigation("Users");
});
modelBuilder.Entity("vassago.Models.Message", b =>
{
b.Navigation("Attachments");
});
modelBuilder.Entity("vassago.Models.User", b =>
{
b.Navigation("Accounts");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -1,133 +0,0 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace vassago.Migrations
{
/// <inheritdoc />
public partial class Cascade : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_Accounts_Channels_SeenInChannelId",
table: "Accounts");
migrationBuilder.DropForeignKey(
name: "FK_Accounts_Users_IsUserId",
table: "Accounts");
migrationBuilder.DropForeignKey(
name: "FK_Attachments_Messages_MessageId",
table: "Attachments");
migrationBuilder.DropForeignKey(
name: "FK_Channels_Channels_ParentChannelId",
table: "Channels");
migrationBuilder.DropForeignKey(
name: "FK_Messages_Channels_ChannelId",
table: "Messages");
migrationBuilder.AddForeignKey(
name: "FK_Accounts_Channels_SeenInChannelId",
table: "Accounts",
column: "SeenInChannelId",
principalTable: "Channels",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_Accounts_Users_IsUserId",
table: "Accounts",
column: "IsUserId",
principalTable: "Users",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_Attachments_Messages_MessageId",
table: "Attachments",
column: "MessageId",
principalTable: "Messages",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_Channels_Channels_ParentChannelId",
table: "Channels",
column: "ParentChannelId",
principalTable: "Channels",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_Messages_Channels_ChannelId",
table: "Messages",
column: "ChannelId",
principalTable: "Channels",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_Accounts_Channels_SeenInChannelId",
table: "Accounts");
migrationBuilder.DropForeignKey(
name: "FK_Accounts_Users_IsUserId",
table: "Accounts");
migrationBuilder.DropForeignKey(
name: "FK_Attachments_Messages_MessageId",
table: "Attachments");
migrationBuilder.DropForeignKey(
name: "FK_Channels_Channels_ParentChannelId",
table: "Channels");
migrationBuilder.DropForeignKey(
name: "FK_Messages_Channels_ChannelId",
table: "Messages");
migrationBuilder.AddForeignKey(
name: "FK_Accounts_Channels_SeenInChannelId",
table: "Accounts",
column: "SeenInChannelId",
principalTable: "Channels",
principalColumn: "Id");
migrationBuilder.AddForeignKey(
name: "FK_Accounts_Users_IsUserId",
table: "Accounts",
column: "IsUserId",
principalTable: "Users",
principalColumn: "Id");
migrationBuilder.AddForeignKey(
name: "FK_Attachments_Messages_MessageId",
table: "Attachments",
column: "MessageId",
principalTable: "Messages",
principalColumn: "Id");
migrationBuilder.AddForeignKey(
name: "FK_Channels_Channels_ParentChannelId",
table: "Channels",
column: "ParentChannelId",
principalTable: "Channels",
principalColumn: "Id");
migrationBuilder.AddForeignKey(
name: "FK_Messages_Channels_ChannelId",
table: "Messages",
column: "ChannelId",
principalTable: "Channels",
principalColumn: "Id");
}
}
}

View File

@ -1,378 +0,0 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using vassago.Models;
#nullable disable
namespace vassago.Migrations
{
[DbContext(typeof(ChattingContext))]
[Migration("20250423002254_UAC")]
partial class UAC
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "7.0.5")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("AccountUAC", b =>
{
b.Property<Guid>("AccountInChannelsId")
.HasColumnType("uuid");
b.Property<Guid>("UACsId")
.HasColumnType("uuid");
b.HasKey("AccountInChannelsId", "UACsId");
b.HasIndex("UACsId");
b.ToTable("AccountUAC");
});
modelBuilder.Entity("ChannelUAC", b =>
{
b.Property<Guid>("ChannelsId")
.HasColumnType("uuid");
b.Property<Guid>("UACsId")
.HasColumnType("uuid");
b.HasKey("ChannelsId", "UACsId");
b.HasIndex("UACsId");
b.ToTable("ChannelUAC");
});
modelBuilder.Entity("UACUser", b =>
{
b.Property<Guid>("UACsId")
.HasColumnType("uuid");
b.Property<Guid>("UsersId")
.HasColumnType("uuid");
b.HasKey("UACsId", "UsersId");
b.HasIndex("UsersId");
b.ToTable("UACUser");
});
modelBuilder.Entity("vassago.Models.Account", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("DisplayName")
.HasColumnType("text");
b.Property<string>("ExternalId")
.HasColumnType("text");
b.Property<bool>("IsBot")
.HasColumnType("boolean");
b.Property<Guid?>("IsUserId")
.HasColumnType("uuid");
b.Property<string>("Protocol")
.HasColumnType("text");
b.Property<Guid?>("SeenInChannelId")
.HasColumnType("uuid");
b.Property<string>("Username")
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("IsUserId");
b.HasIndex("SeenInChannelId");
b.ToTable("Accounts");
});
modelBuilder.Entity("vassago.Models.Attachment", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<byte[]>("Content")
.HasColumnType("bytea");
b.Property<string>("ContentType")
.HasColumnType("text");
b.Property<string>("Description")
.HasColumnType("text");
b.Property<decimal?>("ExternalId")
.HasColumnType("numeric(20,0)");
b.Property<string>("Filename")
.HasColumnType("text");
b.Property<Guid?>("MessageId")
.HasColumnType("uuid");
b.Property<int>("Size")
.HasColumnType("integer");
b.Property<string>("Source")
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("MessageId");
b.ToTable("Attachments");
});
modelBuilder.Entity("vassago.Models.Channel", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<int>("ChannelType")
.HasColumnType("integer");
b.Property<string>("DisplayName")
.HasColumnType("text");
b.Property<string>("ExternalId")
.HasColumnType("text");
b.Property<int?>("LewdnessFilterLevel")
.HasColumnType("integer");
b.Property<bool?>("LinksAllowed")
.HasColumnType("boolean");
b.Property<decimal?>("MaxAttachmentBytes")
.HasColumnType("numeric(20,0)");
b.Property<long?>("MaxTextChars")
.HasColumnType("bigint");
b.Property<int?>("MeannessFilterLevel")
.HasColumnType("integer");
b.Property<Guid?>("ParentChannelId")
.HasColumnType("uuid");
b.Property<string>("Protocol")
.HasColumnType("text");
b.Property<bool?>("ReactionsPossible")
.HasColumnType("boolean");
b.HasKey("Id");
b.HasIndex("ParentChannelId");
b.ToTable("Channels");
});
modelBuilder.Entity("vassago.Models.Message", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<bool>("ActedOn")
.HasColumnType("boolean");
b.Property<Guid?>("AuthorId")
.HasColumnType("uuid");
b.Property<Guid?>("ChannelId")
.HasColumnType("uuid");
b.Property<string>("Content")
.HasColumnType("text");
b.Property<string>("ExternalId")
.HasColumnType("text");
b.Property<bool>("MentionsMe")
.HasColumnType("boolean");
b.Property<string>("Protocol")
.HasColumnType("text");
b.Property<DateTimeOffset>("Timestamp")
.HasColumnType("timestamp with time zone");
b.HasKey("Id");
b.HasIndex("AuthorId");
b.HasIndex("ChannelId");
b.ToTable("Messages");
});
modelBuilder.Entity("vassago.Models.UAC", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("DisplayName")
.HasColumnType("text");
b.Property<Guid>("OwnerId")
.HasColumnType("uuid");
b.HasKey("Id");
b.ToTable("UACs");
});
modelBuilder.Entity("vassago.Models.User", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.HasKey("Id");
b.ToTable("Users");
});
modelBuilder.Entity("AccountUAC", b =>
{
b.HasOne("vassago.Models.Account", null)
.WithMany()
.HasForeignKey("AccountInChannelsId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("vassago.Models.UAC", null)
.WithMany()
.HasForeignKey("UACsId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("ChannelUAC", b =>
{
b.HasOne("vassago.Models.Channel", null)
.WithMany()
.HasForeignKey("ChannelsId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("vassago.Models.UAC", null)
.WithMany()
.HasForeignKey("UACsId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("UACUser", b =>
{
b.HasOne("vassago.Models.UAC", null)
.WithMany()
.HasForeignKey("UACsId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("vassago.Models.User", null)
.WithMany()
.HasForeignKey("UsersId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("vassago.Models.Account", b =>
{
b.HasOne("vassago.Models.User", "IsUser")
.WithMany("Accounts")
.HasForeignKey("IsUserId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("vassago.Models.Channel", "SeenInChannel")
.WithMany("Users")
.HasForeignKey("SeenInChannelId")
.OnDelete(DeleteBehavior.Cascade);
b.Navigation("IsUser");
b.Navigation("SeenInChannel");
});
modelBuilder.Entity("vassago.Models.Attachment", b =>
{
b.HasOne("vassago.Models.Message", "Message")
.WithMany("Attachments")
.HasForeignKey("MessageId")
.OnDelete(DeleteBehavior.Cascade);
b.Navigation("Message");
});
modelBuilder.Entity("vassago.Models.Channel", b =>
{
b.HasOne("vassago.Models.Channel", "ParentChannel")
.WithMany("SubChannels")
.HasForeignKey("ParentChannelId")
.OnDelete(DeleteBehavior.Cascade);
b.Navigation("ParentChannel");
});
modelBuilder.Entity("vassago.Models.Message", b =>
{
b.HasOne("vassago.Models.Account", "Author")
.WithMany()
.HasForeignKey("AuthorId");
b.HasOne("vassago.Models.Channel", "Channel")
.WithMany("Messages")
.HasForeignKey("ChannelId")
.OnDelete(DeleteBehavior.Cascade);
b.Navigation("Author");
b.Navigation("Channel");
});
modelBuilder.Entity("vassago.Models.Channel", b =>
{
b.Navigation("Messages");
b.Navigation("SubChannels");
b.Navigation("Users");
});
modelBuilder.Entity("vassago.Models.Message", b =>
{
b.Navigation("Attachments");
});
modelBuilder.Entity("vassago.Models.User", b =>
{
b.Navigation("Accounts");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -1,131 +0,0 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace vassago.Migrations
{
/// <inheritdoc />
public partial class UAC : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "UACs",
columns: table => new
{
Id = table.Column<Guid>(type: "uuid", nullable: false),
OwnerId = table.Column<Guid>(type: "uuid", nullable: false),
DisplayName = table.Column<string>(type: "text", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_UACs", x => x.Id);
});
migrationBuilder.CreateTable(
name: "AccountUAC",
columns: table => new
{
AccountInChannelsId = table.Column<Guid>(type: "uuid", nullable: false),
UACsId = table.Column<Guid>(type: "uuid", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_AccountUAC", x => new { x.AccountInChannelsId, x.UACsId });
table.ForeignKey(
name: "FK_AccountUAC_Accounts_AccountInChannelsId",
column: x => x.AccountInChannelsId,
principalTable: "Accounts",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_AccountUAC_UACs_UACsId",
column: x => x.UACsId,
principalTable: "UACs",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "ChannelUAC",
columns: table => new
{
ChannelsId = table.Column<Guid>(type: "uuid", nullable: false),
UACsId = table.Column<Guid>(type: "uuid", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ChannelUAC", x => new { x.ChannelsId, x.UACsId });
table.ForeignKey(
name: "FK_ChannelUAC_Channels_ChannelsId",
column: x => x.ChannelsId,
principalTable: "Channels",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_ChannelUAC_UACs_UACsId",
column: x => x.UACsId,
principalTable: "UACs",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "UACUser",
columns: table => new
{
UACsId = table.Column<Guid>(type: "uuid", nullable: false),
UsersId = table.Column<Guid>(type: "uuid", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_UACUser", x => new { x.UACsId, x.UsersId });
table.ForeignKey(
name: "FK_UACUser_UACs_UACsId",
column: x => x.UACsId,
principalTable: "UACs",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_UACUser_Users_UsersId",
column: x => x.UsersId,
principalTable: "Users",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_AccountUAC_UACsId",
table: "AccountUAC",
column: "UACsId");
migrationBuilder.CreateIndex(
name: "IX_ChannelUAC_UACsId",
table: "ChannelUAC",
column: "UACsId");
migrationBuilder.CreateIndex(
name: "IX_UACUser_UsersId",
table: "UACUser",
column: "UsersId");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "AccountUAC");
migrationBuilder.DropTable(
name: "ChannelUAC");
migrationBuilder.DropTable(
name: "UACUser");
migrationBuilder.DropTable(
name: "UACs");
}
}
}

View File

@ -1,386 +0,0 @@
// <auto-generated />
using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using vassago.Models;
#nullable disable
namespace vassago.Migrations
{
[DbContext(typeof(ChattingContext))]
[Migration("20250523181842_channelAliases")]
partial class channelAliases
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "7.0.5")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "hstore");
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("AccountUAC", b =>
{
b.Property<Guid>("AccountInChannelsId")
.HasColumnType("uuid");
b.Property<Guid>("UACsId")
.HasColumnType("uuid");
b.HasKey("AccountInChannelsId", "UACsId");
b.HasIndex("UACsId");
b.ToTable("AccountUAC");
});
modelBuilder.Entity("ChannelUAC", b =>
{
b.Property<Guid>("ChannelsId")
.HasColumnType("uuid");
b.Property<Guid>("UACsId")
.HasColumnType("uuid");
b.HasKey("ChannelsId", "UACsId");
b.HasIndex("UACsId");
b.ToTable("ChannelUAC");
});
modelBuilder.Entity("UACUser", b =>
{
b.Property<Guid>("UACsId")
.HasColumnType("uuid");
b.Property<Guid>("UsersId")
.HasColumnType("uuid");
b.HasKey("UACsId", "UsersId");
b.HasIndex("UsersId");
b.ToTable("UACUser");
});
modelBuilder.Entity("vassago.Models.Account", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("DisplayName")
.HasColumnType("text");
b.Property<string>("ExternalId")
.HasColumnType("text");
b.Property<bool>("IsBot")
.HasColumnType("boolean");
b.Property<Guid?>("IsUserId")
.HasColumnType("uuid");
b.Property<string>("Protocol")
.HasColumnType("text");
b.Property<Guid?>("SeenInChannelId")
.HasColumnType("uuid");
b.Property<string>("Username")
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("IsUserId");
b.HasIndex("SeenInChannelId");
b.ToTable("Accounts");
});
modelBuilder.Entity("vassago.Models.Attachment", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<byte[]>("Content")
.HasColumnType("bytea");
b.Property<string>("ContentType")
.HasColumnType("text");
b.Property<string>("Description")
.HasColumnType("text");
b.Property<decimal?>("ExternalId")
.HasColumnType("numeric(20,0)");
b.Property<string>("Filename")
.HasColumnType("text");
b.Property<Guid?>("MessageId")
.HasColumnType("uuid");
b.Property<int>("Size")
.HasColumnType("integer");
b.Property<string>("Source")
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("MessageId");
b.ToTable("Attachments");
});
modelBuilder.Entity("vassago.Models.Channel", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<Dictionary<string, string>>("Aliases")
.HasColumnType("hstore");
b.Property<int>("ChannelType")
.HasColumnType("integer");
b.Property<string>("DisplayName")
.HasColumnType("text");
b.Property<string>("ExternalId")
.HasColumnType("text");
b.Property<int?>("LewdnessFilterLevel")
.HasColumnType("integer");
b.Property<bool?>("LinksAllowed")
.HasColumnType("boolean");
b.Property<decimal?>("MaxAttachmentBytes")
.HasColumnType("numeric(20,0)");
b.Property<long?>("MaxTextChars")
.HasColumnType("bigint");
b.Property<int?>("MeannessFilterLevel")
.HasColumnType("integer");
b.Property<Guid?>("ParentChannelId")
.HasColumnType("uuid");
b.Property<string>("Protocol")
.HasColumnType("text");
b.Property<bool?>("ReactionsPossible")
.HasColumnType("boolean");
b.HasKey("Id");
b.HasIndex("ParentChannelId");
b.ToTable("Channels");
});
modelBuilder.Entity("vassago.Models.Message", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<bool>("ActedOn")
.HasColumnType("boolean");
b.Property<Guid?>("AuthorId")
.HasColumnType("uuid");
b.Property<Guid?>("ChannelId")
.HasColumnType("uuid");
b.Property<string>("Content")
.HasColumnType("text");
b.Property<string>("ExternalId")
.HasColumnType("text");
b.Property<bool>("MentionsMe")
.HasColumnType("boolean");
b.Property<string>("Protocol")
.HasColumnType("text");
b.Property<DateTimeOffset>("Timestamp")
.HasColumnType("timestamp with time zone");
b.HasKey("Id");
b.HasIndex("AuthorId");
b.HasIndex("ChannelId");
b.ToTable("Messages");
});
modelBuilder.Entity("vassago.Models.UAC", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("Description")
.HasColumnType("text");
b.Property<string>("DisplayName")
.HasColumnType("text");
b.Property<Guid>("OwnerId")
.HasColumnType("uuid");
b.HasKey("Id");
b.ToTable("UACs");
});
modelBuilder.Entity("vassago.Models.User", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.HasKey("Id");
b.ToTable("Users");
});
modelBuilder.Entity("AccountUAC", b =>
{
b.HasOne("vassago.Models.Account", null)
.WithMany()
.HasForeignKey("AccountInChannelsId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("vassago.Models.UAC", null)
.WithMany()
.HasForeignKey("UACsId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("ChannelUAC", b =>
{
b.HasOne("vassago.Models.Channel", null)
.WithMany()
.HasForeignKey("ChannelsId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("vassago.Models.UAC", null)
.WithMany()
.HasForeignKey("UACsId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("UACUser", b =>
{
b.HasOne("vassago.Models.UAC", null)
.WithMany()
.HasForeignKey("UACsId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("vassago.Models.User", null)
.WithMany()
.HasForeignKey("UsersId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("vassago.Models.Account", b =>
{
b.HasOne("vassago.Models.User", "IsUser")
.WithMany("Accounts")
.HasForeignKey("IsUserId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("vassago.Models.Channel", "SeenInChannel")
.WithMany("Users")
.HasForeignKey("SeenInChannelId")
.OnDelete(DeleteBehavior.Cascade);
b.Navigation("IsUser");
b.Navigation("SeenInChannel");
});
modelBuilder.Entity("vassago.Models.Attachment", b =>
{
b.HasOne("vassago.Models.Message", "Message")
.WithMany("Attachments")
.HasForeignKey("MessageId")
.OnDelete(DeleteBehavior.Cascade);
b.Navigation("Message");
});
modelBuilder.Entity("vassago.Models.Channel", b =>
{
b.HasOne("vassago.Models.Channel", "ParentChannel")
.WithMany("SubChannels")
.HasForeignKey("ParentChannelId")
.OnDelete(DeleteBehavior.Cascade);
b.Navigation("ParentChannel");
});
modelBuilder.Entity("vassago.Models.Message", b =>
{
b.HasOne("vassago.Models.Account", "Author")
.WithMany()
.HasForeignKey("AuthorId");
b.HasOne("vassago.Models.Channel", "Channel")
.WithMany("Messages")
.HasForeignKey("ChannelId")
.OnDelete(DeleteBehavior.Cascade);
b.Navigation("Author");
b.Navigation("Channel");
});
modelBuilder.Entity("vassago.Models.Channel", b =>
{
b.Navigation("Messages");
b.Navigation("SubChannels");
b.Navigation("Users");
});
modelBuilder.Entity("vassago.Models.Message", b =>
{
b.Navigation("Attachments");
});
modelBuilder.Entity("vassago.Models.User", b =>
{
b.Navigation("Accounts");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -1,45 +0,0 @@
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace vassago.Migrations
{
/// <inheritdoc />
public partial class channelAliases : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterDatabase()
.Annotation("Npgsql:PostgresExtension:hstore", ",,");
migrationBuilder.AddColumn<string>(
name: "Description",
table: "UACs",
type: "text",
nullable: true);
migrationBuilder.AddColumn<Dictionary<string, string>>(
name: "Aliases",
table: "Channels",
type: "hstore",
nullable: true);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "Description",
table: "UACs");
migrationBuilder.DropColumn(
name: "Aliases",
table: "Channels");
migrationBuilder.AlterDatabase()
.OldAnnotation("Npgsql:PostgresExtension:hstore", ",,");
}
}
}

View File

@ -1,389 +0,0 @@
// <auto-generated />
using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using vassago.Models;
#nullable disable
namespace vassago.Migrations
{
#pragma warning disable 612, 618, 8981
[DbContext(typeof(ChattingContext))]
[Migration("20250605145513_datamemos-more-data")]
partial class datamemosmoredata
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
modelBuilder
.HasAnnotation("ProductVersion", "7.0.5")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "hstore");
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("AccountUAC", b =>
{
b.Property<Guid>("AccountInChannelsId")
.HasColumnType("uuid");
b.Property<Guid>("UACsId")
.HasColumnType("uuid");
b.HasKey("AccountInChannelsId", "UACsId");
b.HasIndex("UACsId");
b.ToTable("AccountUAC");
});
modelBuilder.Entity("ChannelUAC", b =>
{
b.Property<Guid>("ChannelsId")
.HasColumnType("uuid");
b.Property<Guid>("UACsId")
.HasColumnType("uuid");
b.HasKey("ChannelsId", "UACsId");
b.HasIndex("UACsId");
b.ToTable("ChannelUAC");
});
modelBuilder.Entity("UACUser", b =>
{
b.Property<Guid>("UACsId")
.HasColumnType("uuid");
b.Property<Guid>("UsersId")
.HasColumnType("uuid");
b.HasKey("UACsId", "UsersId");
b.HasIndex("UsersId");
b.ToTable("UACUser");
});
modelBuilder.Entity("vassago.Models.Account", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("DisplayName")
.HasColumnType("text");
b.Property<string>("ExternalId")
.HasColumnType("text");
b.Property<bool>("IsBot")
.HasColumnType("boolean");
b.Property<Guid?>("IsUserId")
.HasColumnType("uuid");
b.Property<string>("Protocol")
.HasColumnType("text");
b.Property<Guid?>("SeenInChannelId")
.HasColumnType("uuid");
b.Property<string>("Username")
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("IsUserId");
b.HasIndex("SeenInChannelId");
b.ToTable("Accounts");
});
modelBuilder.Entity("vassago.Models.Attachment", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<byte[]>("Content")
.HasColumnType("bytea");
b.Property<string>("ContentType")
.HasColumnType("text");
b.Property<string>("Description")
.HasColumnType("text");
b.Property<decimal?>("ExternalId")
.HasColumnType("numeric(20,0)");
b.Property<string>("Filename")
.HasColumnType("text");
b.Property<Guid?>("MessageId")
.HasColumnType("uuid");
b.Property<int>("Size")
.HasColumnType("integer");
b.Property<string>("Source")
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("MessageId");
b.ToTable("Attachments");
});
modelBuilder.Entity("vassago.Models.Channel", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<int>("ChannelType")
.HasColumnType("integer");
b.Property<string>("DisplayName")
.HasColumnType("text");
b.Property<string>("ExternalId")
.HasColumnType("text");
b.Property<int?>("LewdnessFilterLevel")
.HasColumnType("integer");
b.Property<bool?>("LinksAllowed")
.HasColumnType("boolean");
b.Property<decimal?>("MaxAttachmentBytes")
.HasColumnType("numeric(20,0)");
b.Property<long?>("MaxTextChars")
.HasColumnType("bigint");
b.Property<int?>("MeannessFilterLevel")
.HasColumnType("integer");
b.Property<Guid?>("ParentChannelId")
.HasColumnType("uuid");
b.Property<string>("Protocol")
.HasColumnType("text");
b.Property<bool?>("ReactionsPossible")
.HasColumnType("boolean");
b.HasKey("Id");
b.HasIndex("ParentChannelId");
b.ToTable("Channels");
});
modelBuilder.Entity("vassago.Models.Message", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<bool>("ActedOn")
.HasColumnType("boolean");
b.Property<Guid?>("AuthorId")
.HasColumnType("uuid");
b.Property<Guid?>("ChannelId")
.HasColumnType("uuid");
b.Property<string>("Content")
.HasColumnType("text");
b.Property<string>("ExternalId")
.HasColumnType("text");
b.Property<bool>("MentionsMe")
.HasColumnType("boolean");
b.Property<string>("Protocol")
.HasColumnType("text");
b.Property<DateTimeOffset>("Timestamp")
.HasColumnType("timestamp with time zone");
b.HasKey("Id");
b.HasIndex("AuthorId");
b.HasIndex("ChannelId");
b.ToTable("Messages");
});
modelBuilder.Entity("vassago.Models.UAC", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<Dictionary<string, string>>("CommandAliases")
.HasColumnType("hstore");
b.Property<string>("Description")
.HasColumnType("text");
b.Property<string>("DisplayName")
.HasColumnType("text");
b.Property<Dictionary<string, string>>("Localization")
.HasColumnType("hstore");
b.Property<Guid>("OwnerId")
.HasColumnType("uuid");
b.HasKey("Id");
b.ToTable("UACs");
});
modelBuilder.Entity("vassago.Models.User", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.HasKey("Id");
b.ToTable("Users");
});
modelBuilder.Entity("AccountUAC", b =>
{
b.HasOne("vassago.Models.Account", null)
.WithMany()
.HasForeignKey("AccountInChannelsId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("vassago.Models.UAC", null)
.WithMany()
.HasForeignKey("UACsId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("ChannelUAC", b =>
{
b.HasOne("vassago.Models.Channel", null)
.WithMany()
.HasForeignKey("ChannelsId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("vassago.Models.UAC", null)
.WithMany()
.HasForeignKey("UACsId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("UACUser", b =>
{
b.HasOne("vassago.Models.UAC", null)
.WithMany()
.HasForeignKey("UACsId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("vassago.Models.User", null)
.WithMany()
.HasForeignKey("UsersId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("vassago.Models.Account", b =>
{
b.HasOne("vassago.Models.User", "IsUser")
.WithMany("Accounts")
.HasForeignKey("IsUserId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("vassago.Models.Channel", "SeenInChannel")
.WithMany("Users")
.HasForeignKey("SeenInChannelId")
.OnDelete(DeleteBehavior.Cascade);
b.Navigation("IsUser");
b.Navigation("SeenInChannel");
});
modelBuilder.Entity("vassago.Models.Attachment", b =>
{
b.HasOne("vassago.Models.Message", "Message")
.WithMany("Attachments")
.HasForeignKey("MessageId")
.OnDelete(DeleteBehavior.Cascade);
b.Navigation("Message");
});
modelBuilder.Entity("vassago.Models.Channel", b =>
{
b.HasOne("vassago.Models.Channel", "ParentChannel")
.WithMany("SubChannels")
.HasForeignKey("ParentChannelId")
.OnDelete(DeleteBehavior.Cascade);
b.Navigation("ParentChannel");
});
modelBuilder.Entity("vassago.Models.Message", b =>
{
b.HasOne("vassago.Models.Account", "Author")
.WithMany()
.HasForeignKey("AuthorId");
b.HasOne("vassago.Models.Channel", "Channel")
.WithMany("Messages")
.HasForeignKey("ChannelId")
.OnDelete(DeleteBehavior.Cascade);
b.Navigation("Author");
b.Navigation("Channel");
});
modelBuilder.Entity("vassago.Models.Channel", b =>
{
b.Navigation("Messages");
b.Navigation("SubChannels");
b.Navigation("Users");
});
modelBuilder.Entity("vassago.Models.Message", b =>
{
b.Navigation("Attachments");
});
modelBuilder.Entity("vassago.Models.User", b =>
{
b.Navigation("Accounts");
});
#pragma warning restore 612, 618, 8981
}
}
}

View File

@ -1,55 +0,0 @@
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace vassago.Migrations
{
#pragma warning disable 8981
/// <inheritdoc />
public partial class datamemosmoredata : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<Dictionary<string, string>>(
name: "CommandAliases",
table: "UACs",
type: "hstore",
nullable: true);
migrationBuilder.AddColumn<Dictionary<string, string>>(
name: "Localization",
table: "UACs",
type: "hstore",
nullable: true);
//NOTE for future me: migrationBuilder.SQL("SELECT localization INTO Aliases from Channels;");, but also make the rows for it.
//too lazy now, really leaning on the "this will work fine for my 0 users"
migrationBuilder.DropColumn(
name: "Aliases",
table: "Channels");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<Dictionary<string, string>>(
name: "Aliases",
table: "Channels",
type: "hstore",
nullable: true);
migrationBuilder.DropColumn(
name: "CommandAliases",
table: "UACs");
migrationBuilder.DropColumn(
name: "Localization",
table: "UACs");
}
}
}
#pragma warning restore 8981

View File

@ -1,391 +0,0 @@
// <auto-generated />
using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using vassago.Models;
#nullable disable
namespace vassago.Migrations
{
#pragma warning disable CS8981
[DbContext(typeof(ChattingContext))]
[Migration("20250620023827_locales")]
partial class locales
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "7.0.5")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "hstore");
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("AccountUAC", b =>
{
b.Property<Guid>("AccountInChannelsId")
.HasColumnType("uuid");
b.Property<Guid>("UACsId")
.HasColumnType("uuid");
b.HasKey("AccountInChannelsId", "UACsId");
b.HasIndex("UACsId");
b.ToTable("AccountUAC");
});
modelBuilder.Entity("ChannelUAC", b =>
{
b.Property<Guid>("ChannelsId")
.HasColumnType("uuid");
b.Property<Guid>("UACsId")
.HasColumnType("uuid");
b.HasKey("ChannelsId", "UACsId");
b.HasIndex("UACsId");
b.ToTable("ChannelUAC");
});
modelBuilder.Entity("UACUser", b =>
{
b.Property<Guid>("UACsId")
.HasColumnType("uuid");
b.Property<Guid>("UsersId")
.HasColumnType("uuid");
b.HasKey("UACsId", "UsersId");
b.HasIndex("UsersId");
b.ToTable("UACUser");
});
modelBuilder.Entity("vassago.Models.Account", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("DisplayName")
.HasColumnType("text");
b.Property<string>("ExternalId")
.HasColumnType("text");
b.Property<bool>("IsBot")
.HasColumnType("boolean");
b.Property<Guid?>("IsUserId")
.HasColumnType("uuid");
b.Property<string>("Protocol")
.HasColumnType("text");
b.Property<Guid?>("SeenInChannelId")
.HasColumnType("uuid");
b.Property<string>("Username")
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("IsUserId");
b.HasIndex("SeenInChannelId");
b.ToTable("Accounts");
});
modelBuilder.Entity("vassago.Models.Attachment", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<byte[]>("Content")
.HasColumnType("bytea");
b.Property<string>("ContentType")
.HasColumnType("text");
b.Property<string>("Description")
.HasColumnType("text");
b.Property<decimal?>("ExternalId")
.HasColumnType("numeric(20,0)");
b.Property<string>("Filename")
.HasColumnType("text");
b.Property<Guid?>("MessageId")
.HasColumnType("uuid");
b.Property<int>("Size")
.HasColumnType("integer");
b.Property<string>("Source")
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("MessageId");
b.ToTable("Attachments");
});
modelBuilder.Entity("vassago.Models.Channel", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<int>("ChannelType")
.HasColumnType("integer");
b.Property<string>("DisplayName")
.HasColumnType("text");
b.Property<string>("ExternalId")
.HasColumnType("text");
b.Property<int?>("LewdnessFilterLevel")
.HasColumnType("integer");
b.Property<bool?>("LinksAllowed")
.HasColumnType("boolean");
b.Property<decimal?>("MaxAttachmentBytes")
.HasColumnType("numeric(20,0)");
b.Property<long?>("MaxTextChars")
.HasColumnType("bigint");
b.Property<int?>("MeannessFilterLevel")
.HasColumnType("integer");
b.Property<Guid?>("ParentChannelId")
.HasColumnType("uuid");
b.Property<string>("Protocol")
.HasColumnType("text");
b.Property<bool?>("ReactionsPossible")
.HasColumnType("boolean");
b.HasKey("Id");
b.HasIndex("ParentChannelId");
b.ToTable("Channels");
});
modelBuilder.Entity("vassago.Models.Message", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<bool>("ActedOn")
.HasColumnType("boolean");
b.Property<Guid?>("AuthorId")
.HasColumnType("uuid");
b.Property<Guid?>("ChannelId")
.HasColumnType("uuid");
b.Property<string>("Content")
.HasColumnType("text");
b.Property<string>("ExternalId")
.HasColumnType("text");
b.Property<bool>("MentionsMe")
.HasColumnType("boolean");
b.Property<string>("Protocol")
.HasColumnType("text");
b.Property<DateTimeOffset>("Timestamp")
.HasColumnType("timestamp with time zone");
b.HasKey("Id");
b.HasIndex("AuthorId");
b.HasIndex("ChannelId");
b.ToTable("Messages");
});
modelBuilder.Entity("vassago.Models.UAC", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<Dictionary<string, string>>("CommandAlterations")
.HasColumnType("hstore");
b.Property<string>("Description")
.HasColumnType("text");
b.Property<string>("DisplayName")
.HasColumnType("text");
b.Property<Guid>("OwnerId")
.HasColumnType("uuid");
b.Property<Dictionary<string, string>>("Translations")
.HasColumnType("hstore");
b.HasKey("Id");
b.ToTable("UACs");
});
modelBuilder.Entity("vassago.Models.User", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.HasKey("Id");
b.ToTable("Users");
});
modelBuilder.Entity("AccountUAC", b =>
{
b.HasOne("vassago.Models.Account", null)
.WithMany()
.HasForeignKey("AccountInChannelsId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("vassago.Models.UAC", null)
.WithMany()
.HasForeignKey("UACsId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("ChannelUAC", b =>
{
b.HasOne("vassago.Models.Channel", null)
.WithMany()
.HasForeignKey("ChannelsId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("vassago.Models.UAC", null)
.WithMany()
.HasForeignKey("UACsId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("UACUser", b =>
{
b.HasOne("vassago.Models.UAC", null)
.WithMany()
.HasForeignKey("UACsId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("vassago.Models.User", null)
.WithMany()
.HasForeignKey("UsersId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("vassago.Models.Account", b =>
{
b.HasOne("vassago.Models.User", "IsUser")
.WithMany("Accounts")
.HasForeignKey("IsUserId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("vassago.Models.Channel", "SeenInChannel")
.WithMany("Users")
.HasForeignKey("SeenInChannelId")
.OnDelete(DeleteBehavior.Cascade);
b.Navigation("IsUser");
b.Navigation("SeenInChannel");
});
modelBuilder.Entity("vassago.Models.Attachment", b =>
{
b.HasOne("vassago.Models.Message", "Message")
.WithMany("Attachments")
.HasForeignKey("MessageId")
.OnDelete(DeleteBehavior.Cascade);
b.Navigation("Message");
});
modelBuilder.Entity("vassago.Models.Channel", b =>
{
b.HasOne("vassago.Models.Channel", "ParentChannel")
.WithMany("SubChannels")
.HasForeignKey("ParentChannelId")
.OnDelete(DeleteBehavior.Cascade);
b.Navigation("ParentChannel");
});
modelBuilder.Entity("vassago.Models.Message", b =>
{
b.HasOne("vassago.Models.Account", "Author")
.WithMany()
.HasForeignKey("AuthorId");
b.HasOne("vassago.Models.Channel", "Channel")
.WithMany("Messages")
.HasForeignKey("ChannelId")
.OnDelete(DeleteBehavior.Cascade);
b.Navigation("Author");
b.Navigation("Channel");
});
modelBuilder.Entity("vassago.Models.Channel", b =>
{
b.Navigation("Messages");
b.Navigation("SubChannels");
b.Navigation("Users");
});
modelBuilder.Entity("vassago.Models.Message", b =>
{
b.Navigation("Attachments");
});
modelBuilder.Entity("vassago.Models.User", b =>
{
b.Navigation("Accounts");
});
#pragma warning restore 612, 618
}
}
#pragma warning restore CS8981
}

View File

@ -1,40 +0,0 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace vassago.Migrations
{
#pragma warning disable 8981
/// <inheritdoc />
public partial class locales : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.RenameColumn(
name: "Localization",
table: "UACs",
newName: "Translations");
migrationBuilder.RenameColumn(
name: "CommandAliases",
table: "UACs",
newName: "CommandAlterations");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.RenameColumn(
name: "Translations",
table: "UACs",
newName: "Localization");
migrationBuilder.RenameColumn(
name: "CommandAlterations",
table: "UACs",
newName: "CommandAliases");
}
}
#pragma warning restore 8981
}

View File

@ -1,392 +0,0 @@
// <auto-generated />
using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using vassago.Models;
#nullable disable
namespace vassago.Migrations
{
[DbContext(typeof(ChattingContext))]
[Migration("20250620230813_TranslatedMessages")]
partial class TranslatedMessages
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "7.0.5")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "hstore");
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("AccountUAC", b =>
{
b.Property<Guid>("AccountInChannelsId")
.HasColumnType("uuid");
b.Property<Guid>("UACsId")
.HasColumnType("uuid");
b.HasKey("AccountInChannelsId", "UACsId");
b.HasIndex("UACsId");
b.ToTable("AccountUAC");
});
modelBuilder.Entity("ChannelUAC", b =>
{
b.Property<Guid>("ChannelsId")
.HasColumnType("uuid");
b.Property<Guid>("UACsId")
.HasColumnType("uuid");
b.HasKey("ChannelsId", "UACsId");
b.HasIndex("UACsId");
b.ToTable("ChannelUAC");
});
modelBuilder.Entity("UACUser", b =>
{
b.Property<Guid>("UACsId")
.HasColumnType("uuid");
b.Property<Guid>("UsersId")
.HasColumnType("uuid");
b.HasKey("UACsId", "UsersId");
b.HasIndex("UsersId");
b.ToTable("UACUser");
});
modelBuilder.Entity("vassago.Models.Account", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("DisplayName")
.HasColumnType("text");
b.Property<string>("ExternalId")
.HasColumnType("text");
b.Property<bool>("IsBot")
.HasColumnType("boolean");
b.Property<Guid?>("IsUserId")
.HasColumnType("uuid");
b.Property<string>("Protocol")
.HasColumnType("text");
b.Property<Guid?>("SeenInChannelId")
.HasColumnType("uuid");
b.Property<string>("Username")
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("IsUserId");
b.HasIndex("SeenInChannelId");
b.ToTable("Accounts");
});
modelBuilder.Entity("vassago.Models.Attachment", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<byte[]>("Content")
.HasColumnType("bytea");
b.Property<string>("ContentType")
.HasColumnType("text");
b.Property<string>("Description")
.HasColumnType("text");
b.Property<decimal?>("ExternalId")
.HasColumnType("numeric(20,0)");
b.Property<string>("Filename")
.HasColumnType("text");
b.Property<Guid?>("MessageId")
.HasColumnType("uuid");
b.Property<int>("Size")
.HasColumnType("integer");
b.Property<string>("Source")
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("MessageId");
b.ToTable("Attachments");
});
modelBuilder.Entity("vassago.Models.Channel", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<int>("ChannelType")
.HasColumnType("integer");
b.Property<string>("DisplayName")
.HasColumnType("text");
b.Property<string>("ExternalId")
.HasColumnType("text");
b.Property<int?>("LewdnessFilterLevel")
.HasColumnType("integer");
b.Property<bool?>("LinksAllowed")
.HasColumnType("boolean");
b.Property<decimal?>("MaxAttachmentBytes")
.HasColumnType("numeric(20,0)");
b.Property<long?>("MaxTextChars")
.HasColumnType("bigint");
b.Property<int?>("MeannessFilterLevel")
.HasColumnType("integer");
b.Property<Guid?>("ParentChannelId")
.HasColumnType("uuid");
b.Property<string>("Protocol")
.HasColumnType("text");
b.Property<bool?>("ReactionsPossible")
.HasColumnType("boolean");
b.HasKey("Id");
b.HasIndex("ParentChannelId");
b.ToTable("Channels");
});
modelBuilder.Entity("vassago.Models.Message", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<bool>("ActedOn")
.HasColumnType("boolean");
b.Property<Guid?>("AuthorId")
.HasColumnType("uuid");
b.Property<Guid?>("ChannelId")
.HasColumnType("uuid");
b.Property<string>("Content")
.HasColumnType("text");
b.Property<string>("ExternalId")
.HasColumnType("text");
b.Property<bool>("MentionsMe")
.HasColumnType("boolean");
b.Property<string>("Protocol")
.HasColumnType("text");
b.Property<DateTimeOffset>("Timestamp")
.HasColumnType("timestamp with time zone");
b.Property<string>("TranslatedContent")
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("AuthorId");
b.HasIndex("ChannelId");
b.ToTable("Messages");
});
modelBuilder.Entity("vassago.Models.UAC", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<Dictionary<string, string>>("CommandAlterations")
.HasColumnType("hstore");
b.Property<string>("Description")
.HasColumnType("text");
b.Property<string>("DisplayName")
.HasColumnType("text");
b.Property<Guid>("OwnerId")
.HasColumnType("uuid");
b.Property<Dictionary<string, string>>("Translations")
.HasColumnType("hstore");
b.HasKey("Id");
b.ToTable("UACs");
});
modelBuilder.Entity("vassago.Models.User", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.HasKey("Id");
b.ToTable("Users");
});
modelBuilder.Entity("AccountUAC", b =>
{
b.HasOne("vassago.Models.Account", null)
.WithMany()
.HasForeignKey("AccountInChannelsId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("vassago.Models.UAC", null)
.WithMany()
.HasForeignKey("UACsId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("ChannelUAC", b =>
{
b.HasOne("vassago.Models.Channel", null)
.WithMany()
.HasForeignKey("ChannelsId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("vassago.Models.UAC", null)
.WithMany()
.HasForeignKey("UACsId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("UACUser", b =>
{
b.HasOne("vassago.Models.UAC", null)
.WithMany()
.HasForeignKey("UACsId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("vassago.Models.User", null)
.WithMany()
.HasForeignKey("UsersId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("vassago.Models.Account", b =>
{
b.HasOne("vassago.Models.User", "IsUser")
.WithMany("Accounts")
.HasForeignKey("IsUserId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("vassago.Models.Channel", "SeenInChannel")
.WithMany("Users")
.HasForeignKey("SeenInChannelId")
.OnDelete(DeleteBehavior.Cascade);
b.Navigation("IsUser");
b.Navigation("SeenInChannel");
});
modelBuilder.Entity("vassago.Models.Attachment", b =>
{
b.HasOne("vassago.Models.Message", "Message")
.WithMany("Attachments")
.HasForeignKey("MessageId")
.OnDelete(DeleteBehavior.Cascade);
b.Navigation("Message");
});
modelBuilder.Entity("vassago.Models.Channel", b =>
{
b.HasOne("vassago.Models.Channel", "ParentChannel")
.WithMany("SubChannels")
.HasForeignKey("ParentChannelId")
.OnDelete(DeleteBehavior.Cascade);
b.Navigation("ParentChannel");
});
modelBuilder.Entity("vassago.Models.Message", b =>
{
b.HasOne("vassago.Models.Account", "Author")
.WithMany()
.HasForeignKey("AuthorId");
b.HasOne("vassago.Models.Channel", "Channel")
.WithMany("Messages")
.HasForeignKey("ChannelId")
.OnDelete(DeleteBehavior.Cascade);
b.Navigation("Author");
b.Navigation("Channel");
});
modelBuilder.Entity("vassago.Models.Channel", b =>
{
b.Navigation("Messages");
b.Navigation("SubChannels");
b.Navigation("Users");
});
modelBuilder.Entity("vassago.Models.Message", b =>
{
b.Navigation("Attachments");
});
modelBuilder.Entity("vassago.Models.User", b =>
{
b.Navigation("Accounts");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -1,28 +0,0 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace vassago.Migrations
{
/// <inheritdoc />
public partial class TranslatedMessages : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "TranslatedContent",
table: "Messages",
type: "text",
nullable: true);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "TranslatedContent",
table: "Messages");
}
}
}

View File

@ -1,389 +0,0 @@
// <auto-generated />
using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using vassago.Models;
#nullable disable
namespace vassago.Migrations
{
[DbContext(typeof(ChattingContext))]
partial class ChattingContextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "7.0.5")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "hstore");
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("AccountUAC", b =>
{
b.Property<Guid>("AccountInChannelsId")
.HasColumnType("uuid");
b.Property<Guid>("UACsId")
.HasColumnType("uuid");
b.HasKey("AccountInChannelsId", "UACsId");
b.HasIndex("UACsId");
b.ToTable("AccountUAC");
});
modelBuilder.Entity("ChannelUAC", b =>
{
b.Property<Guid>("ChannelsId")
.HasColumnType("uuid");
b.Property<Guid>("UACsId")
.HasColumnType("uuid");
b.HasKey("ChannelsId", "UACsId");
b.HasIndex("UACsId");
b.ToTable("ChannelUAC");
});
modelBuilder.Entity("UACUser", b =>
{
b.Property<Guid>("UACsId")
.HasColumnType("uuid");
b.Property<Guid>("UsersId")
.HasColumnType("uuid");
b.HasKey("UACsId", "UsersId");
b.HasIndex("UsersId");
b.ToTable("UACUser");
});
modelBuilder.Entity("vassago.Models.Account", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("DisplayName")
.HasColumnType("text");
b.Property<string>("ExternalId")
.HasColumnType("text");
b.Property<bool>("IsBot")
.HasColumnType("boolean");
b.Property<Guid?>("IsUserId")
.HasColumnType("uuid");
b.Property<string>("Protocol")
.HasColumnType("text");
b.Property<Guid?>("SeenInChannelId")
.HasColumnType("uuid");
b.Property<string>("Username")
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("IsUserId");
b.HasIndex("SeenInChannelId");
b.ToTable("Accounts");
});
modelBuilder.Entity("vassago.Models.Attachment", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<byte[]>("Content")
.HasColumnType("bytea");
b.Property<string>("ContentType")
.HasColumnType("text");
b.Property<string>("Description")
.HasColumnType("text");
b.Property<decimal?>("ExternalId")
.HasColumnType("numeric(20,0)");
b.Property<string>("Filename")
.HasColumnType("text");
b.Property<Guid?>("MessageId")
.HasColumnType("uuid");
b.Property<int>("Size")
.HasColumnType("integer");
b.Property<string>("Source")
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("MessageId");
b.ToTable("Attachments");
});
modelBuilder.Entity("vassago.Models.Channel", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<int>("ChannelType")
.HasColumnType("integer");
b.Property<string>("DisplayName")
.HasColumnType("text");
b.Property<string>("ExternalId")
.HasColumnType("text");
b.Property<int?>("LewdnessFilterLevel")
.HasColumnType("integer");
b.Property<bool?>("LinksAllowed")
.HasColumnType("boolean");
b.Property<decimal?>("MaxAttachmentBytes")
.HasColumnType("numeric(20,0)");
b.Property<long?>("MaxTextChars")
.HasColumnType("bigint");
b.Property<int?>("MeannessFilterLevel")
.HasColumnType("integer");
b.Property<Guid?>("ParentChannelId")
.HasColumnType("uuid");
b.Property<string>("Protocol")
.HasColumnType("text");
b.Property<bool?>("ReactionsPossible")
.HasColumnType("boolean");
b.HasKey("Id");
b.HasIndex("ParentChannelId");
b.ToTable("Channels");
});
modelBuilder.Entity("vassago.Models.Message", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<bool>("ActedOn")
.HasColumnType("boolean");
b.Property<Guid?>("AuthorId")
.HasColumnType("uuid");
b.Property<Guid?>("ChannelId")
.HasColumnType("uuid");
b.Property<string>("Content")
.HasColumnType("text");
b.Property<string>("ExternalId")
.HasColumnType("text");
b.Property<bool>("MentionsMe")
.HasColumnType("boolean");
b.Property<string>("Protocol")
.HasColumnType("text");
b.Property<DateTimeOffset>("Timestamp")
.HasColumnType("timestamp with time zone");
b.Property<string>("TranslatedContent")
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("AuthorId");
b.HasIndex("ChannelId");
b.ToTable("Messages");
});
modelBuilder.Entity("vassago.Models.UAC", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<Dictionary<string, string>>("CommandAlterations")
.HasColumnType("hstore");
b.Property<string>("Description")
.HasColumnType("text");
b.Property<string>("DisplayName")
.HasColumnType("text");
b.Property<Guid>("OwnerId")
.HasColumnType("uuid");
b.Property<Dictionary<string, string>>("Translations")
.HasColumnType("hstore");
b.HasKey("Id");
b.ToTable("UACs");
});
modelBuilder.Entity("vassago.Models.User", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.HasKey("Id");
b.ToTable("Users");
});
modelBuilder.Entity("AccountUAC", b =>
{
b.HasOne("vassago.Models.Account", null)
.WithMany()
.HasForeignKey("AccountInChannelsId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("vassago.Models.UAC", null)
.WithMany()
.HasForeignKey("UACsId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("ChannelUAC", b =>
{
b.HasOne("vassago.Models.Channel", null)
.WithMany()
.HasForeignKey("ChannelsId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("vassago.Models.UAC", null)
.WithMany()
.HasForeignKey("UACsId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("UACUser", b =>
{
b.HasOne("vassago.Models.UAC", null)
.WithMany()
.HasForeignKey("UACsId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("vassago.Models.User", null)
.WithMany()
.HasForeignKey("UsersId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("vassago.Models.Account", b =>
{
b.HasOne("vassago.Models.User", "IsUser")
.WithMany("Accounts")
.HasForeignKey("IsUserId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("vassago.Models.Channel", "SeenInChannel")
.WithMany("Users")
.HasForeignKey("SeenInChannelId")
.OnDelete(DeleteBehavior.Cascade);
b.Navigation("IsUser");
b.Navigation("SeenInChannel");
});
modelBuilder.Entity("vassago.Models.Attachment", b =>
{
b.HasOne("vassago.Models.Message", "Message")
.WithMany("Attachments")
.HasForeignKey("MessageId")
.OnDelete(DeleteBehavior.Cascade);
b.Navigation("Message");
});
modelBuilder.Entity("vassago.Models.Channel", b =>
{
b.HasOne("vassago.Models.Channel", "ParentChannel")
.WithMany("SubChannels")
.HasForeignKey("ParentChannelId")
.OnDelete(DeleteBehavior.Cascade);
b.Navigation("ParentChannel");
});
modelBuilder.Entity("vassago.Models.Message", b =>
{
b.HasOne("vassago.Models.Account", "Author")
.WithMany()
.HasForeignKey("AuthorId");
b.HasOne("vassago.Models.Channel", "Channel")
.WithMany("Messages")
.HasForeignKey("ChannelId")
.OnDelete(DeleteBehavior.Cascade);
b.Navigation("Author");
b.Navigation("Channel");
});
modelBuilder.Entity("vassago.Models.Channel", b =>
{
b.Navigation("Messages");
b.Navigation("SubChannels");
b.Navigation("Users");
});
modelBuilder.Entity("vassago.Models.Message", b =>
{
b.Navigation("Attachments");
});
modelBuilder.Entity("vassago.Models.User", b =>
{
b.Navigation("Accounts");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -1,33 +0,0 @@
namespace vassago.Models;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Reflection;
using System.Text.Json.Serialization;
public class Account
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
public string ExternalId { get; set; }
public string Username { get; set; }
private string _displayName = null;
public string DisplayName
{
get
{
return _displayName ?? Username;
}
set
{
_displayName = value;
}
}
public bool IsBot { get; set; } //webhook counts
public Channel SeenInChannel { get; set; }
public string Protocol { get; set; }
public List<UAC> UACs { get; set; }
[JsonIgnore]
public User IsUser {get; set;}
}

View File

@ -1,18 +0,0 @@
namespace vassago.Models;
using System;
using System.ComponentModel.DataAnnotations.Schema;
public class Attachment
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
public ulong? ExternalId { get; set; }
public Uri Source { get; set; }
public byte[] Content { get; set; }
public string Filename { get; set; }
public Message Message { get; set; }
public string ContentType { get; internal set; }
public string Description { get; internal set; }
public int Size { get; internal set; }
}

View File

@ -1,109 +0,0 @@
namespace vassago.Models;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json;
using static vassago.Models.Enumerations;
public class Channel
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
public string ExternalId { get; set; }
public string DisplayName { get; set; }
[DeleteBehavior(DeleteBehavior.Cascade)]
public List<Channel> SubChannels { get; set; }
[JsonIgnore]
public Channel ParentChannel { get; set; }
public Guid? ParentChannelId { get; set; }
public string Protocol { get; set; }
[DeleteBehavior(DeleteBehavior.Cascade)]
public List<Message> Messages { get; set; }
[DeleteBehavior(DeleteBehavior.Cascade)]
public List<Account> Users { get; set; }
public ChannelType ChannelType { get; set; }
//Permissions
public ulong? MaxAttachmentBytes { get; set; }
public uint? MaxTextChars { get; set; }
public bool? LinksAllowed { get; set; }
public bool? ReactionsPossible { get; set; }
public Enumerations.LewdnessFilterLevel? LewdnessFilterLevel { get; set; }
public Enumerations.MeannessFilterLevel? MeannessFilterLevel { get; set; }
public List<UAC> UACs { get; set; }
public DefinitePermissionSettings EffectivePermissions
{
get
{
var path = new Stack<Channel>(); //omg i actually get to use a data structure from university
var walker = this;
path.Push(this);
while (walker.ParentChannel != null)
{
walker = walker.ParentChannel;
path.Push(walker);
}
DefinitePermissionSettings toReturn = new DefinitePermissionSettings();
while (path.Count > 0)
{
walker = path.Pop();
toReturn.LewdnessFilterLevel = walker.LewdnessFilterLevel ?? toReturn.LewdnessFilterLevel;
toReturn.MeannessFilterLevel = walker.MeannessFilterLevel ?? toReturn.MeannessFilterLevel;
toReturn.LinksAllowed = walker.LinksAllowed ?? toReturn.LinksAllowed;
toReturn.MaxAttachmentBytes = walker.MaxAttachmentBytes ?? toReturn.MaxAttachmentBytes;
toReturn.MaxTextChars = walker.MaxTextChars ?? toReturn.MaxTextChars;
toReturn.ReactionsPossible = walker.ReactionsPossible ?? toReturn.ReactionsPossible;
}
return toReturn;
}
}
public string LineageSummary
{
get
{
if (this.ParentChannel != null)
{
return this.ParentChannel.LineageSummary + '/' + this.DisplayName;
}
else
{
return this.Protocol;
}
}
}
///<summary>
///break self-referencing loops for library-agnostic serialization
///</summary>
public Channel AsSerializable()
{
var toReturn = this.MemberwiseClone() as Channel;
toReturn.ParentChannel = null;
if (toReturn.Users?.Count > 0)
{
foreach (var account in toReturn.Users)
{
account.SeenInChannel = null;
}
}
return toReturn;
}
}
public class DefinitePermissionSettings
{
public ulong MaxAttachmentBytes { get; set; }
public uint MaxTextChars { get; set; }
public bool LinksAllowed { get; set; }
public bool ReactionsPossible { get; set; }
public Enumerations.LewdnessFilterLevel LewdnessFilterLevel { get; set; }
public Enumerations.MeannessFilterLevel MeannessFilterLevel { get; set; }
}

View File

@ -1,22 +0,0 @@
namespace vassago.Models;
using Microsoft.Extensions.Configuration;
using Microsoft.EntityFrameworkCore;
public class ChattingContext : DbContext
{
public DbSet<Attachment> Attachments { get; set; }
public DbSet<Channel> Channels { get; set; }
public DbSet<UAC> UACs { get; set; }
public DbSet<Message> Messages { get; set; }
public DbSet<Account> Accounts { get; set; }
public DbSet<User> Users { get; set; }
public ChattingContext(DbContextOptions<ChattingContext> options) : base(options) { }
public ChattingContext() : base() { }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseNpgsql(Shared.DBConnectionString);
//.EnableSensitiveDataLogging(true); //"sensitive" is one thing. writing "did something" every time you think a thought is a different thing.
}
}

View File

@ -1,81 +0,0 @@
using System;
using System.ComponentModel;
using System.Reflection;
namespace vassago.Models;
public static class Enumerations
{
public enum LewdnessFilterLevel
{
[Description("this is a christian minecraft server 🙏")]
Strict,
[Description("G-Rated")]
G,
[Description("polite company")]
Moderate,
[Description(";) ;) ;)")]
Unrestricted
}
public enum MeannessFilterLevel
{
[Description("good vibes only")]
Strict,
[Description("a bit cheeky")]
Medium,
[Description("387.44m mi of printed circuits")]
Unrestricted
}
public enum ChannelType
{
[Description("Normal")]
Normal,
[Description("DM")]
DM,
[Description("protocol psuedo-channel")]
Protocol,
[Description("organizational psuedo-channel")]
OU
}
///<summary>
///bro. don't even get me started. tl;dr: hashtag microsoft.
///</summary>
public enum HttpVerb
{
Get,
Post,
Put,
Delete,
Head,
Patch,
Options
}
public static string GetDescription<T>(this T enumerationValue)
where T : struct
{
Type type = enumerationValue.GetType();
if (!type.IsEnum)
{
throw new ArgumentException("EnumerationValue must be of Enum type", nameof(enumerationValue));
}
//Tries to find a DescriptionAttribute for a potential friendly name
//for the enum
MemberInfo[] memberInfo = type.GetMember(enumerationValue.ToString());
if (memberInfo != null && memberInfo.Length > 0)
{
object[] attrs = memberInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attrs != null && attrs.Length > 0)
{
//Pull out the description value
return ((DescriptionAttribute)attrs[0]).Description;
}
}
//If we have no description attribute, just return the ToString of the enum
return enumerationValue.ToString();
}
}

View File

@ -1,33 +0,0 @@
using System;
using System.Collections.Generic;
using franz;
using gray_messages;
//psst, future adam: that means we're gray_messages.chat.chat_message.
namespace gray_messages.chat
{
public class chat_message : gray_messages.message
{
//expect this to be the same every time. and, "localhost". but importantly, it'll remind me of the port.
public Uri Api_Uri { get; set; }
public Guid MessageId { get; set; }
public string Content { get; set; }
public string RawContent { get; set; }
public bool MentionsMe { get; set; }
public DateTimeOffset Timestamp { get; set; }
public uint AttachmentCount { get; set; }
public Guid AccountId { get; set; }
public string AccountName { get; set; }
public Guid UserId { get; set; }
public string UserName { get; set; }
public Guid ChannelId { get; set; }
public string ChannelName { get; set; }
public string ChannelProtoocl { get; set; }
public List<Guid> UAC_Matches { get; set; }
public List<string> BehavedOnBy { get; set; }
}
}

View File

@ -1,27 +0,0 @@
namespace vassago.Models;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Reflection;
using System.Threading.Tasks;
using Discord.WebSocket;
using Microsoft.EntityFrameworkCore;
public class Message
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
public string Protocol { get; set; }
public string ExternalId { get; set; }
public string Content { get; set; }
public string TranslatedContent { get; set; }
public bool MentionsMe { get; set; }
public DateTimeOffset Timestamp { get; set; }
public bool ActedOn { get; set; }
[DeleteBehavior(DeleteBehavior.Cascade)]
public List<Attachment> Attachments { get; set; }
public Account Author { get; set; }
public Channel Channel { get; set; }
public Guid? ChannelId { get; set; }
}

View File

@ -1,37 +0,0 @@
namespace vassago.Models;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using vassago.Models;
//TODO: rename.
//"uac" originally meant "user account control". but it might just be channel control. in fact, channel-control is much more fun,
//then the platform manages the permissions for you!
//but now I'm going to add locales to it, so it's kind of... "miscellaneous attached data". Official Sticky Notes, if you will.
public class UAC
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
///<summary indulgence="haiku-like">
///behaviors will have
///a hardcoded ID thing
///so they can find theirs.
///</summary>
public Guid OwnerId { get; set;}
public string DisplayName { get; set; }
public List<Account> AccountInChannels { get; set; }
public List<Channel> Channels { get; set; }
public List<User> Users { get; set; }
///<summary>"but past adam", you may ask. "if UACs are configured before runtime, why not write html into your source control, as part of the project,
///with the benefit of an html editor?"
///absolutely fair question. **But**: the plan is for external services, e.g., over kafka, to manage their own. So from Vassago's perspective,
///it's variably before and after compile time. shrug.emote.
///</summary>
public string Description { get; set; }
public Dictionary<string, string> CommandAlterations {get; set;}
public Dictionary<string, string> Translations {get; set;}
}

View File

@ -1,39 +0,0 @@
namespace vassago.Models;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Reflection;
using Microsoft.EntityFrameworkCore;
public class User
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
[DeleteBehavior(DeleteBehavior.Cascade)]
public List<Account> Accounts { get; set; }
public List<UAC> UACs { get; set; }
//if I ever get lots and lots of tags, or some automatic way to register a feature's arbitrary tags, then I can move this off.
//public bool Tag_CanTwitchSummon { get; set; }
public string DisplayName
{
get
{
if (Accounts?.Any() ?? false)
{
return Accounts.Select(a => a.DisplayName).Distinct()
.MaxBy(distinctName =>
Accounts.Select(a => a.DisplayName)
.Where(selectedName => selectedName == distinctName).Count()
);
}
else
{
return $"[accountless {Id}]";
}
}
}
}

View File

@ -1,51 +1,83 @@
using Microsoft.AspNetCore.Mvc.Razor; using System;
using Microsoft.EntityFrameworkCore; using System.IO;
using Microsoft.AspNetCore.Mvc.NewtonsoftJson; using System.Linq;
using vassago; using System.Text.RegularExpressions;
using vassago.Models; using System.Net;
using System.Threading.Tasks;
using Discord;
using Discord.WebSocket;
using Microsoft.Extensions.Configuration;
var builder = WebApplication.CreateBuilder(args); namespace silverworker_discord
{
class Program
{
private DiscordSocketClient _client;
// Add services to the container. IConfigurationRoot config = new ConfigurationBuilder()
builder.Services.AddControllersWithViews(); .AddJsonFile("appsettings.json", true, true)
builder.Services.AddSingleton<IHostedService, vassago.ConsoleService>(); .Build();
builder.Services.AddDbContext<ChattingContext>();
builder.Services.AddControllers().AddNewtonsoftJson(options => { private ISocketMessageChannel botChatterChannel = null;
options.SerializerSettings.ReferenceLoopHandling = private ISocketMessageChannel announcementChannel = null;
Newtonsoft.Json.ReferenceLoopHandling.Ignore;
public static void Main(string[] args)
=> new Program().MainAsync().GetAwaiter().GetResult();
private Task Log(LogMessage msg)
{
Console.WriteLine(msg.ToString());
return Task.CompletedTask;
}
public async Task MainAsync()
{
_client = new DiscordSocketClient();
_client.Log += Log;
await _client.LoginAsync(TokenType.Bot, config["token"]);
await _client.StartAsync();
_client.MessageReceived += MessageReceived;
_client.UserJoined += UserJoined;
_client.Ready += () => Task.Run(() =>{
Console.WriteLine("Bot is connected!");
botChatterChannel = _client.GetChannel(ulong.Parse(config["botChatterChannel"])) as ISocketMessageChannel;
announcementChannel = _client.GetChannel(ulong.Parse(config["announcementChannel"])) as ISocketMessageChannel;
}); });
builder.Services.AddProblemDetails(); // Block this task until the program is closed.
builder.Services.Configure<RazorViewEngineOptions>(o => { await Task.Delay(-1);
o.ViewLocationFormats.Clear(); }
o.ViewLocationFormats.Add("/WebInterface/Views/{1}/{0}" + RazorViewEngine.ViewExtension);
o.ViewLocationFormats.Add("/WebInterface/Views/Shared/{0}" + RazorViewEngine.ViewExtension);
});
builder.Services.AddSwaggerGen();
var app = builder.Build(); private async Task MessageReceived(SocketMessage messageParam)
{
var message = messageParam as SocketUserMessage;
if (message == null) return;
if (message.Author.Id == _client.CurrentUser.Id) return;
app.UseStaticFiles(); Console.WriteLine($"{message.Channel}, {message.Content}, {message.Id}");
app.UseRouting(); if (message.Channel.Id == botChatterChannel.Id)
app.UseAuthorization(); {
app.MapControllerRoute( if(message.Attachments?.Count > 0)
name: "default", {
pattern: "{controller=Home}/{action=Index}/{id?}"); Console.WriteLine(message.Attachments.Count);
foreach (var att in message.Attachments)
app.UseSwagger(); {
Console.WriteLine(att.Url);
app.UseSwaggerUI(c => await WebRequest.Create("http://192.168.1.151:3001/shortcuts?display_url=" + att.Url).GetResponseAsync();
{ }
c.SwaggerEndpoint("/swagger/v1/swagger.json", "api"); }
}); }
}
//app.UseExceptionHandler(); private Task UserJoined(SocketGuildUser arg)
//app.UseStatusCodePages(); {
Console.WriteLine($"user joined: {arg.Nickname}. Guid: {arg.Guild.Id}. Channel: {arg.Guild.DefaultChannel}");
if (app.Environment.IsDevelopment()) var abbreviatedNickname = arg.Nickname;
{ if(arg.Nickname.Length > 3){
app.UseDeveloperExceptionPage(); abbreviatedNickname = arg.Nickname.Substring(0, arg.Nickname.Length / 3);
}
Console.WriteLine($"imma call him {abbreviatedNickname}");
return arg.Guild.DefaultChannel.SendMessageAsync($"oh hey {abbreviatedNickname}- IPLAYTHESEALOFORICHALCOS <:ORICHALCOS:852749196633309194>");
}
}
} }
Shared.App = app;
app.Run();

View File

@ -1,37 +0,0 @@
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:42619",
"sslPort": 44354
}
},
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "http://localhost:5093",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "https://localhost:7206;http://localhost:5093",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

View File

@ -1,497 +0,0 @@
//https://discord.com/oauth2/authorize?client_id=913003037348491264&permissions=274877942784&scope=bot%20messages.read
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Discord;
using Discord.WebSocket;
using vassago.Models;
using vassago.Behavior;
using Discord.Rest;
using Microsoft.EntityFrameworkCore;
using System.Threading;
using System.Reactive.Linq;
namespace vassago.ProtocolInterfaces.DiscordInterface;
//data received
//translate data to internal type
//store
//ship off to behaver
public class DiscordInterface : ProtocolInterface
{
public static new string Protocol { get => "discord"; }
internal DiscordSocketClient client;
private bool eventsSignedUp = false;
private static readonly SemaphoreSlim discordChannelSetup = new(1, 1);
private Channel protocolAsChannel;
public override Channel SelfChannel { get => protocolAsChannel; }
public async Task Init(string config)
{
var token = config;
await SetupDiscordChannel();
client = new DiscordSocketClient(new DiscordSocketConfig() { GatewayIntents = GatewayIntents.All });
client.Log += (msg) =>
{
Console.WriteLine(msg.ToString());
return Task.CompletedTask;
};
client.Connected += () => Task.Run(SelfConnected);
client.Ready += () => Task.Run(ClientReady);
await client.LoginAsync(TokenType.Bot, token);
await client.StartAsync();
}
private async Task SetupDiscordChannel()
{
await discordChannelSetup.WaitAsync();
try
{
protocolAsChannel = Rememberer.SearchChannel(c => c.ParentChannel == null && c.Protocol == Protocol);
if (protocolAsChannel == null)
{
protocolAsChannel = new Channel()
{
DisplayName = "discord (itself)",
MeannessFilterLevel = Enumerations.MeannessFilterLevel.Strict,
LewdnessFilterLevel = Enumerations.LewdnessFilterLevel.Moderate,
MaxTextChars = 2000,
MaxAttachmentBytes = 25 * 1024 * 1024, //allegedly it's 25, but I worry it's not actually.
LinksAllowed = true,
ReactionsPossible = true,
ExternalId = null,
Protocol = Protocol,
SubChannels = []
};
}
else
{
Console.WriteLine($"discord, channel with id {protocolAsChannel.Id}, already exists");
}
protocolAsChannel.DisplayName = "discord (itself)";
protocolAsChannel = Rememberer.RememberChannel(protocolAsChannel);
Console.WriteLine($"protocol as channel addeed; {protocolAsChannel}");
}
finally
{
discordChannelSetup.Release();
}
}
private async Task ClientReady()
{
if (!eventsSignedUp)
{
eventsSignedUp = true;
Console.WriteLine($"Bot is connected ({client.CurrentUser.Username}; {client.CurrentUser.Mention})! going to sign up for message received and user joined in client ready");
client.MessageReceived += MessageReceived;
// _client.MessageUpdated +=
client.UserJoined += UserJoined;
client.SlashCommandExecuted += SlashCommandHandler;
//client.ChannelCreated +=
// _client.ChannelDestroyed +=
// _client.ChannelUpdated +=
// _client.GuildMemberUpdated +=
// _client.UserBanned +=
// _client.UserLeft +=
// _client.ThreadCreated +=
// _client.ThreadUpdated +=
// _client.ThreadDeleted +=
// _client.JoinedGuild +=
// _client.GuildUpdated +=
// _client.LeftGuild +=
await SlashCommandsHelper.Register(client);
}
else
{
Console.WriteLine("bot appears to be RE connected, so I'm not going to sign up twice");
}
}
private async Task SelfConnected()
{
await discordChannelSetup.WaitAsync();
try
{
var selfAccount = UpsertAccount(client.CurrentUser, protocolAsChannel);
selfAccount.DisplayName = client.CurrentUser.Username;
Behaver.Instance.MarkSelf(selfAccount);
}
finally
{
discordChannelSetup.Release();
}
}
private async Task MessageReceived(SocketMessage messageParam)
{
if (messageParam is not SocketUserMessage)
{
Console.WriteLine($"{messageParam.Content}, but not a user message");
return;
}
var suMessage = messageParam as SocketUserMessage;
Console.WriteLine($"#{suMessage.Channel}[{DateTime.Now}][{suMessage.Author.Username} [id={suMessage.Author.Id}]][msg id: {suMessage.Id}] {suMessage.Content}");
var m = UpsertMessage(suMessage);
if (suMessage.MentionedUsers?.FirstOrDefault(muid => muid.Id == client.CurrentUser.Id) != null)
{
var mentionOfMe = "<@" + client.CurrentUser.Id + ">";
m.MentionsMe = true;
}
await Behaver.Instance.ActOn(m);
m.ActedOn = true; // for its own ruposess it might act on it later, but either way, fuck it, we checked.
// ...but we don't save?
}
private Task UserJoined(SocketGuildUser arg)
{
var guild = UpsertChannel(arg.Guild);
var defaultChannel = UpsertChannel(arg.Guild.DefaultChannel);
defaultChannel.ParentChannel = guild;
var u = UpsertAccount(arg, guild);
u.DisplayName = arg.DisplayName;
return null;
}
internal static async Task SlashCommandHandler(SocketSlashCommand command)
{
switch (command.CommandName)
{
case "freedomunits":
try
{
var amt = (double)(command.Data.Options.First(o => o.Name == "amount").Value);
var src = (string)command.Data.Options.First(o => o.Name == "src-unit").Value;
var dest = (string)command.Data.Options.First(o => o.Name == "dest-unit").Value;
var conversionResult = Conversion.Converter.Convert(amt, src, dest);
await command.RespondAsync($"> {amt} {src} -> {dest}\n{conversionResult}");
}
catch (Exception e)
{
await command.RespondAsync($"error: {e.Message}. aaadam!");
}
break;
default:
await command.RespondAsync($"\\*smiles and nods*\n");
await command.Channel.SendFileAsync($"assets/loud sweating.gif");
Console.Error.WriteLine($"can't understand command name: {command.CommandName}");
break;
}
}
internal static vassago.Models.Attachment UpsertAttachment(IAttachment dAttachment)
{
var a = Rememberer.SearchAttachment(ai => ai.ExternalId == dAttachment.Id)
?? new vassago.Models.Attachment();
a.ContentType = dAttachment.ContentType;
a.Description = dAttachment.Description;
a.Filename = dAttachment.Filename;
a.Size = dAttachment.Size;
a.Source = new Uri(dAttachment.Url);
Rememberer.RememberAttachment(a);
return a;
}
internal Message UpsertMessage(IUserMessage dMessage)
{
var m = Rememberer.SearchMessage(mi => mi.ExternalId == dMessage.Id.ToString() && mi.Protocol == Protocol)
?? new()
{
Protocol = Protocol
};
if (dMessage.Attachments?.Count > 0)
{
m.Attachments = [];
foreach (var da in dMessage.Attachments)
{
m.Attachments.Add(UpsertAttachment(da));
}
}
m.Content = dMessage.Content;
m.ExternalId = dMessage.Id.ToString();
m.Timestamp = dMessage.EditedTimestamp ?? dMessage.CreatedAt;
m.Channel = UpsertChannel(dMessage.Channel);
m.Author = UpsertAccount(dMessage.Author, m.Channel);
if (dMessage.Channel is IGuildChannel)
{
m.Author.DisplayName = (dMessage.Author as IGuildUser).DisplayName;//discord forgot how display names work.
}
m.MentionsMe = (dMessage.Author.Id != client.CurrentUser.Id
&& (dMessage.MentionedUserIds?.FirstOrDefault(muid => muid == client.CurrentUser.Id) > 0));
Rememberer.RememberMessage(m);
Console.WriteLine($"received message; author: {m.Author.DisplayName}, {m.Author.Id}. messageid:{m.Id}");
return m;
}
internal Channel UpsertChannel(IMessageChannel channel)
{
Channel c = Rememberer.SearchChannel(ci => ci.ExternalId == channel.Id.ToString() && ci.Protocol == Protocol);
if (c == null)
{
Console.WriteLine($"couldn't find channel under protocol {Protocol} with externalId {channel.Id.ToString()}");
c = new Channel()
{
Users = []
};
}
c.ExternalId = channel.Id.ToString();
c.ChannelType = (channel is IPrivateChannel) ? vassago.Models.Enumerations.ChannelType.DM : vassago.Models.Enumerations.ChannelType.Normal;
c.Messages ??= [];
c.Protocol = Protocol;
if (channel is IGuildChannel)
{
Console.WriteLine($"{channel.Name} is a guild channel. So i'm going to upsert the guild, {(channel as IGuildChannel).Guild}");
c.ParentChannel = UpsertChannel((channel as IGuildChannel).Guild);
}
else if (channel is IPrivateChannel)
{
c.ParentChannel = protocolAsChannel;
Console.WriteLine("i'm a private channel so I'm setting my parent channel to the protocol as channel");
}
else
{
c.ParentChannel = protocolAsChannel;
Console.Error.WriteLine($"trying to upsert channel {channel.Id}/{channel.Name}, but it's neither guildchannel nor private channel. shrug.jpg");
}
Console.WriteLine($"upsertion of channel {c.DisplayName}, it's type {c.ChannelType}");
switch (c.ChannelType)
{
case vassago.Models.Enumerations.ChannelType.DM:
var asPriv = (channel as IPrivateChannel);
var sender = asPriv?.Recipients?.FirstOrDefault(u => u.Id != client.CurrentUser.Id); // why yes, there's a list of recipients, and it's the sender.
if (sender != null)
{
c.DisplayName = "DM: " + sender.Username;
}
else
{
//I sent it, so I don't know the recipient's name.
}
break;
default:
c.DisplayName = channel.Name;
break;
}
Channel parentChannel = null;
if (channel is IGuildChannel)
{
parentChannel = Rememberer.SearchChannel(c => c.ExternalId == (channel as IGuildChannel).Guild.Id.ToString() && c.Protocol == Protocol);
if (parentChannel is null)
{
Console.Error.WriteLine("why am I still null?");
}
}
else if (channel is IPrivateChannel)
{
parentChannel = protocolAsChannel;
}
else
{
parentChannel = protocolAsChannel;
Console.Error.WriteLine($"trying to upsert channel {channel.Id}/{channel.Name}, but it's neither guildchannel nor private channel. shrug.jpg");
}
parentChannel.SubChannels ??= [];
if (!parentChannel.SubChannels.Contains(c))
{
parentChannel.SubChannels.Add(c);
}
c = Rememberer.RememberChannel(c);
//Console.WriteLine($"no one knows how to make good tooling. c.users.first, which needs client currentuser id tostring. c: {c}, c.Users {c.Users}, client: {client}, client.CurrentUser: {client.CurrentUser}, client.currentUser.Id: {client.CurrentUser.Id}");
var selfAccountInChannel = c.Users?.FirstOrDefault(a => a.ExternalId == client.CurrentUser.Id.ToString());
if (selfAccountInChannel == null)
{
selfAccountInChannel = UpsertAccount(client.CurrentUser, c);
}
return c;
}
internal Channel UpsertChannel(IGuild channel)
{
Channel c = Rememberer.SearchChannel(ci => ci.ExternalId == channel.Id.ToString() && ci.Protocol == Protocol);
if (c == null)
{
Console.WriteLine($"couldn't find channel under protocol {Protocol} with externalId {channel.Id.ToString()}");
c = new Channel();
}
c.DisplayName = channel.Name;
c.ExternalId = channel.Id.ToString();
c.ChannelType = vassago.Models.Enumerations.ChannelType.OU;
c.Messages ??= [];
c.Protocol = protocolAsChannel.Protocol;
c.ParentChannel = protocolAsChannel;
c.SubChannels ??= [];
c.MaxAttachmentBytes = channel.MaxUploadLimit;
return Rememberer.RememberChannel(c);
}
internal static Account UpsertAccount(IUser discordUser, Channel inChannel)
{
var acc = Rememberer.SearchAccount(ui => ui.ExternalId == discordUser.Id.ToString() && ui.SeenInChannel.Id == inChannel.Id);
Console.WriteLine($"upserting account, retrieved {acc?.Id}.");
if (acc != null)
{
Console.WriteLine($"acc's user: {acc.IsUser?.Id}");
}
acc ??= new Account()
{
IsUser = Rememberer.SearchUser(u => u.Accounts.Any(a => a.ExternalId == discordUser.Id.ToString() && a.Protocol == Protocol))
?? new User()
};
acc.Username = discordUser.Username;
acc.ExternalId = discordUser.Id.ToString();
acc.IsBot = discordUser.IsBot || discordUser.IsWebhook;
acc.Protocol = Protocol;
acc.SeenInChannel = inChannel;
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");
}
Rememberer.RememberAccount(acc);
inChannel.Users ??= [];
if (!inChannel.Users.Contains(acc))
{
inChannel.Users.Add(acc);
Rememberer.RememberChannel(inChannel);
}
return acc;
}
private static async Task<int> AttemptReact(IUserMessage msg, string e)
{
Console.WriteLine("discord attempting to react");
var c = Rememberer.SearchChannel(c => c.ExternalId == msg.Channel.Id.ToString());// db.Channels.FirstOrDefault(c => c.ExternalId == msg.Channel.Id.ToString());
//var preferredEmote = c.EmoteOverrides?[e] ?? e; //TODO: emote overrides
var preferredEmote = e;
if (Emoji.TryParse(preferredEmote, out Emoji emoji))
{
msg.AddReactionAsync(emoji);
return 200;
}
if (!Emote.TryParse(preferredEmote, out Emote emote))
{
if (preferredEmote == e)
Console.Error.WriteLine($"never heard of emote {e}");
return 405;
}
msg.AddReactionAsync(emote);
return 200;
}
private static string TruncateText(string msg, uint? chars)
{
chars ??= 500;
if (msg?.Length > chars)
{
return msg.Substring(0, (int)chars - 2) + "✂";
}
else
{
return msg;
}
}
public override async Task<int> SendMessage(Channel channel, string text)
{
var dcCh = await client.GetChannelAsync(ulong.Parse(channel.ExternalId));
if (dcCh == null)
{
return 404;
}
if (dcCh is IMessageChannel msgChannel)
{
await msgChannel.SendMessageAsync(TruncateText(text, channel.MaxTextChars));
return 200;
}
else
{
return 503;
}
}
public override async Task<int> SendFile(Channel channel, string base64dData, string filename, string accompanyingText)
{
var dcCh = await client.GetChannelAsync(ulong.Parse(channel.ExternalId));
if (dcCh == null)
{
return 404;
}
if (dcCh is IMessageChannel msgChannel)
{
using (var ms = new MemoryStream(Convert.FromBase64String(base64dData)))
{
await msgChannel.SendFileAsync(ms, filename, TruncateText(accompanyingText, channel.MaxTextChars));
}
return 200;
}
else
{
return 503;
}
}
public override async Task<int> React(Message message, string reaction)
{
var dcCh = await client.GetChannelAsync(ulong.Parse(message.Channel.ExternalId));
if (dcCh == null)
return 404;
if (dcCh is IMessageChannel msgChannel)
{
var dcMsg = await msgChannel.GetMessageAsync(ulong.Parse(message.ExternalId));
if (dcMsg == null)
return 404;
return await AttemptReact(dcMsg as IUserMessage, reaction);
}
else
{
return 503;
}
}
public override async Task<int> Reply(Message message, string text)
{
var dcCh = await client.GetChannelAsync(ulong.Parse(message.Channel.ExternalId));
if (dcCh == null)
return 404;
if (dcCh is IMessageChannel msgChannel)
{
var dcMsg = await msgChannel.GetMessageAsync(ulong.Parse(message.ExternalId));
if (dcMsg == null)
return 404;
(dcMsg as IUserMessage).ReplyAsync(TruncateText(text, message.Channel.MaxTextChars));
return 200;
}
else
{
return 503;
}
}
}

View File

@ -1,123 +0,0 @@
using System;
using System.Linq;
using System.Collections.Generic;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Discord.WebSocket;
using Discord;
using Discord.Net;
namespace vassago.ProtocolInterfaces.DiscordInterface
{
public static class SlashCommandsHelper
{
private static List<CommandSetup> slashCommands = new List<CommandSetup>()
{
new CommandSetup(){
Id = "freedomunits",
UpdatedAt = new DateTime(2023, 5, 21, 13, 3, 0),
guild = 825293851110801428, //TODO: demagic this magic number
register = Register_FreedomUnits
}
};
public static async Task Register(DiscordSocketClient client)
{
if(Shared.SetupSlashCommands)
{
var commandsInContext = await client.GetGlobalApplicationCommandsAsync();
await Register(client, commandsInContext, null);
foreach (var guild in client.Guilds)
{
try
{
await Register(client, await guild.GetApplicationCommandsAsync(), guild);
}
catch (HttpException ex)
{
Console.Error.WriteLine($"error registering slash commands for guild {guild.Name} (id {guild.Id}) - {ex.Message}");
}
}
}
}
private static async Task Register(DiscordSocketClient client, IEnumerable<SocketApplicationCommand> commandsInContext, SocketGuild guild)
{
foreach (var existingCommand in commandsInContext)
{
var myVersion = slashCommands.FirstOrDefault(c => c.Id == existingCommand.Name && c.guild == guild?.Id);
if (myVersion == null)
{
Console.WriteLine($"deleting command {existingCommand.Name} - (created at {existingCommand.CreatedAt}, it's in guild {existingCommand.Guild?.Id} while I'm in {guild?.Id})");
await existingCommand.DeleteAsync();
}
else
{
Console.WriteLine(existingCommand.CreatedAt);
if (myVersion.UpdatedAt > existingCommand.CreatedAt)
{
Console.WriteLine($"overwriting command {existingCommand.Name}");
await myVersion.register(false, client, guild);
}
myVersion.alreadyRegistered = true;
}
}
foreach (var remaining in slashCommands.Where(sc => sc.alreadyRegistered == false && sc.guild == guild?.Id))
{
Console.WriteLine($"creating new command {remaining.Id} ({(remaining.guild == null ? "global" : $"for guild {remaining.guild}")})");
await remaining.register(true, client, guild);
}
}
private static async Task Register_FreedomUnits(bool isNew, DiscordSocketClient client, SocketGuild guild)
{
var builtCommand = new SlashCommandBuilder()
.WithName("freedomunits")
.WithDescription("convert between misc units (currency: iso 4217 code)")
.AddOption("amount", ApplicationCommandOptionType.Number, "source amount", isRequired: true)
.AddOption(new SlashCommandOptionBuilder()
.WithName("src-unit")
.WithDescription("unit converting FROM")
.WithRequired(true)
.WithType(ApplicationCommandOptionType.String))
.AddOption(new SlashCommandOptionBuilder()
.WithName("dest-unit")
.WithDescription("unit converting TO")
.WithRequired(true)
.WithType(ApplicationCommandOptionType.String))
.Build();
try
{
if (guild != null)
{
if (isNew)
await guild.CreateApplicationCommandAsync(builtCommand);
else
await guild.BulkOverwriteApplicationCommandAsync(new ApplicationCommandProperties[] { builtCommand });
}
else
{
if (isNew)
await client.CreateGlobalApplicationCommandAsync(builtCommand);
else
await client.BulkOverwriteGlobalApplicationCommandsAsync(new ApplicationCommandProperties[] { builtCommand });
}
}
catch (HttpException exception)
{
var json = JsonConvert.SerializeObject(exception.Errors, Formatting.Indented);
Console.Error.WriteLine(json);
}
}
private class CommandSetup
{
public string Id { get; set; }
//the date/time you updated yours IN UTC.
public DateTimeOffset UpdatedAt { get; set; }
public Registration register { get; set; }
public ulong? guild { get; set; }
public bool alreadyRegistered {get;set; } = false;
public delegate Task Registration(bool isNew, DiscordSocketClient client, SocketGuild guild);
}
}
}

View File

@ -1,22 +0,0 @@
namespace vassago.ProtocolInterfaces;
using vassago.Models;
public abstract class ProtocolInterface
{
public static string Protocol { get; }
public abstract Channel SelfChannel { get; }
public abstract Task<int> SendMessage(Channel channel, string text);
public virtual async Task<int> SendFile(Channel channel, string path, string accompanyingText)
{
if (!File.Exists(path))
{
return 404;
}
var fstring = Convert.ToBase64String(File.ReadAllBytes(path));
return await SendFile(channel, fstring, Path.GetFileName(path), accompanyingText);
}
public abstract Task<int> SendFile(Channel channel, string base64dData, string filename, string accompanyingText);
public abstract Task<int> React(Message message, string reaction);
public abstract Task<int> Reply(Message message, string text);
}

View File

@ -1,7 +0,0 @@
namespace vassago.TwitchInterface;
public class TwitchConfig
{
public string username {get; set;}
public string oauth {get; set;}
}

View File

@ -1,312 +0,0 @@
using RestSharp;
using System.Security.Cryptography.X509Certificates;
using System.Text.RegularExpressions;
using TwitchLib.Api.Helix.Models.Users.GetUsers;
using TwitchLib.Api;
using TwitchLib.Client.Events;
using TwitchLib.Client.Models;
using TwitchLib.Client;
using TwitchLib.Communication.Clients;
using TwitchLib.Communication.Models;
using vassago.Behavior;
using vassago.Models;
using vassago.ProtocolInterfaces;
namespace vassago.TwitchInterface;
public class TwitchInterface : ProtocolInterface
{
public static new string Protocol { get => "twitch"; }
private static SemaphoreSlim channelSetupSemaphpore = new SemaphoreSlim(1, 1);
private Channel protocolAsChannel;
public override Channel SelfChannel { get => protocolAsChannel;}
private Account selfAccountInProtocol;
TwitchClient client;
private async Task SetupTwitchChannel()
{
await channelSetupSemaphpore.WaitAsync();
try
{
protocolAsChannel = Rememberer.SearchChannel(c => c.ParentChannel == null && c.Protocol == Protocol);
if (protocolAsChannel == null)
{
protocolAsChannel = new Channel()
{
DisplayName = "twitch (itself)",
MeannessFilterLevel = Enumerations.MeannessFilterLevel.Medium,
LewdnessFilterLevel = Enumerations.LewdnessFilterLevel.G,
MaxTextChars = 500,
MaxAttachmentBytes = 0,
LinksAllowed = false,
ReactionsPossible = false,
ExternalId = null,
Protocol = Protocol,
SubChannels = []
};
protocolAsChannel.DisplayName = "twitch (itself)";
protocolAsChannel = Rememberer.RememberChannel(protocolAsChannel);
Console.WriteLine($"protocol as channle added; {protocolAsChannel}");
}
else
{
Console.WriteLine($"twitch, channel with id {protocolAsChannel.Id}, already exists");
}
//protocolAsChan
}
finally
{
channelSetupSemaphpore.Release();
}
}
///<param name="oauth">https://www.twitchapps.com/tmi/</param>
public async Task Init(TwitchConfig tc)
{
await SetupTwitchChannel();
WebSocketClient customClient = new WebSocketClient(new ClientOptions
{
MessagesAllowedInPeriod = 750,
ThrottlingPeriod = TimeSpan.FromSeconds(30)
}
);
client = new TwitchClient(customClient);
client.Initialize(new ConnectionCredentials(tc.username, tc.oauth, capabilities: new Capabilities()));
client.OnLog += Client_OnLog;
client.OnJoinedChannel += Client_OnJoinedChannel;
client.OnMessageReceived += Client_OnMessageReceivedAsync;
client.OnWhisperReceived += Client_OnWhisperReceivedAsync;
client.OnConnected += Client_OnConnected;
client.Connect();
Console.WriteLine("twitch client 1 connected");
}
private async void Client_OnWhisperReceivedAsync(object sender, OnWhisperReceivedArgs e)
{
//data received
Console.WriteLine($"whisper#{e.WhisperMessage.Username}[{DateTime.Now}][{e.WhisperMessage.DisplayName} [id={e.WhisperMessage.Username}]][msg id: {e.WhisperMessage.MessageId}] {e.WhisperMessage.Message}");
//translate to internal, upsert
var m = UpsertMessage(e.WhisperMessage);
//can't send whispers without giving up cellphone number.
//m.Reply = (t) => { return Task.Run(() => { client.SendWhisper(e.WhisperMessage.Username, t); }); };
m.Channel.ChannelType = vassago.Models.Enumerations.ChannelType.DM;
//act on
await Behaver.Instance.ActOn(m);
m.ActedOn = true;
//TODO: remember it again?
}
private async void Client_OnMessageReceivedAsync(object sender, OnMessageReceivedArgs e)
{
//data eived
Console.WriteLine($"#{e.ChatMessage.Channel}[{DateTime.Now}][{e.ChatMessage.DisplayName} [id={e.ChatMessage.Username}]][msg id: {e.ChatMessage.Id}] {e.ChatMessage.Message}");
//translate to internal, upsert
var m = UpsertMessage(e.ChatMessage);
m.Channel.ChannelType = vassago.Models.Enumerations.ChannelType.Normal;
//act on
await Behaver.Instance.ActOn(m);
m.ActedOn = true;
//TODO: remember again?
}
private void Client_OnConnected(object sender, OnConnectedArgs e)
{
Console.WriteLine($"twitch marking selfaccount as seeninchannel {protocolAsChannel.Id}");
selfAccountInProtocol = UpsertAccount(e.BotUsername, protocolAsChannel);
selfAccountInProtocol.DisplayName = e.BotUsername;
Behaver.Instance.MarkSelf(selfAccountInProtocol);
Console.WriteLine($"Connected to {e.AutoJoinChannel}");
AttemptJoin(e.BotUsername);
}
private void Client_OnJoinedChannel(object sender, OnJoinedChannelArgs e)
{
client.SendMessage(e.Channel, "beep boop");
}
private void Client_OnLog(object sender, OnLogArgs e)
{
Console.WriteLine($"{e.DateTime.ToString()}: {e.BotUsername} - {e.Data}");
}
private Account UpsertAccount(string username, Channel inChannel)
{
//Console.WriteLine($"upserting twitch account. username: {username}. inChannel: {inChannel?.Id}");
var acc = Rememberer.SearchAccount(ui => ui.ExternalId == username && ui.SeenInChannel.ExternalId == inChannel.ExternalId);
// Console.WriteLine($"upserting twitch account, retrieved {acc?.Id}.");
if (acc != null)
{
Console.WriteLine($"acc's usser: {acc.IsUser?.Id}");
}
acc ??= new Account()
{
IsUser = Rememberer.SearchUser(
u => u.Accounts.Any(a => a.ExternalId == username && a.Protocol == Protocol))
?? new vassago.Models.User()
};
acc.Username = username;
acc.ExternalId = username;
//acc.IsBot = false? there is a way to tell, but you have to go back through the API
acc.Protocol = Protocol;
acc.SeenInChannel = inChannel;
// 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 vassago.Models.User() { Accounts = [acc] };
// if (inChannel.Users?.Count > 0)
// {
// Console.WriteLine($"channel has {inChannel.Users.Count} accounts");
// }
Rememberer.RememberAccount(acc);
inChannel.Users ??= [];
if (!inChannel.Users.Contains(acc))
{
inChannel.Users.Add(acc);
Rememberer.RememberChannel(inChannel);
}
return acc;
}
private Channel UpsertChannel(string channelName)
{
Channel c = Rememberer.SearchChannel(ci => ci.ExternalId == channelName
&& ci.Protocol == Protocol);
if (c == null)
{
// Console.WriteLine($"couldn't find channel under protocol {PROTOCOL} with externalId {channelName}");
c = new Channel()
{
Users = []
};
}
c.DisplayName = channelName;
c.ExternalId = channelName;
c.ChannelType = vassago.Models.Enumerations.ChannelType.Normal;
c.Messages ??= [];
c.Protocol = Protocol;
c.ParentChannel = protocolAsChannel;
c.SubChannels = c.SubChannels ?? new List<Channel>();
c = Rememberer.RememberChannel(c);
var selfAccountInChannel = c.Users?.FirstOrDefault(a => a.ExternalId == selfAccountInProtocol.ExternalId);
if (selfAccountInChannel == null)
{
selfAccountInChannel = UpsertAccount(selfAccountInProtocol.Username, c);
}
return c;
}
private Channel UpsertDMChannel(string whisperWith)
{
Channel c = Rememberer.SearchChannel(ci => ci.ExternalId == $"w_{whisperWith}"
&& ci.Protocol == Protocol);
if (c == null)
{
// Console.WriteLine($"couldn't find channel under protocol {PROTOCOL}, whisper with {whisperWith}");
c = new Channel()
{
Users = []
};
}
c.DisplayName = $"Whisper: {whisperWith}";
c.ExternalId = $"w_{whisperWith}";
c.ChannelType = vassago.Models.Enumerations.ChannelType.DM;
c.Messages ??= [];
c.Protocol = Protocol;
c.ParentChannel = protocolAsChannel;
c.SubChannels = c.SubChannels ?? new List<Channel>();
c = Rememberer.RememberChannel(c);
var selfAccountInChannel = c.Users.FirstOrDefault(a => a.ExternalId == selfAccountInProtocol.ExternalId);
if (selfAccountInChannel == null)
{
selfAccountInChannel = UpsertAccount(selfAccountInChannel.Username, c);
}
return c;
}
//n.b., I see you future adam. "we should unify these, they're redundant".
//ah, but that's the trick, they aren't! twitchlib has a common base class, but
//none of the features we care about are on it!
private Message UpsertMessage(ChatMessage chatMessage)
{
var m = Rememberer.SearchMessage(mi => mi.ExternalId == chatMessage.Id && mi.Protocol == Protocol)
?? new()
{
Protocol = Protocol,
Timestamp = (DateTimeOffset)DateTime.SpecifyKind(DateTime.UtcNow, DateTimeKind.Utc)
};
m.Content = chatMessage.Message;
m.ExternalId = chatMessage.Id;
m.Channel = UpsertChannel(chatMessage.Channel);
m.Author = UpsertAccount(chatMessage.Username, m.Channel);
m.MentionsMe = Regex.IsMatch(m.Content?.ToLower(), $"@\\b{selfAccountInProtocol.Username.ToLower()}\\b");
Rememberer.RememberMessage(m);
return m;
}
//n.b., I see you future adam. "we should unify these, they're redundant".
//ah, but that's the trick, they aren't! twitchlib has a common base class, but
//none of the features we care about are on it!
private Message UpsertMessage(WhisperMessage whisperMessage)
{
//WhisperMessage.Id corresponds to chatMessage.Id. \*eye twitch*
var m = Rememberer.SearchMessage(mi => mi.ExternalId == whisperMessage.MessageId && mi.Protocol == Protocol)
?? new()
{
Id = Guid.NewGuid(),
Protocol = Protocol,
Timestamp = (DateTimeOffset)DateTime.SpecifyKind(DateTime.UtcNow, DateTimeKind.Utc)
};
m.Content = whisperMessage.Message;
m.ExternalId = whisperMessage.MessageId;
m.Channel = UpsertDMChannel(whisperMessage.Username);
m.Author = UpsertAccount(whisperMessage.Username, m.Channel);
m.MentionsMe = Regex.IsMatch(m.Content?.ToLower(), $"@\\b{selfAccountInProtocol.Username.ToLower()}\\b");
Rememberer.RememberMessage(m);
return m;
}
public string AttemptJoin(string channelTarget)
{
client.JoinChannel(channelTarget);
return $"attempt join {channelTarget} - o7";
}
internal void AttemptLeave(string channelTarget)
{
client.SendMessage(channelTarget, "o7");
client.LeaveChannel(channelTarget);
}
public override async Task<int> SendMessage(Channel channel, string text)
{
Task.Run(() => { client.SendMessage(channel.ExternalId, text); });
return 200;
}
public override async Task<int> SendFile(Channel channel, string base64dData, string filename, string accompanyingText)
{
return 405;
}
public override async Task<int> React(Message message, string reaction)
{
return 405;
}
public override async Task<int> Reply(Message message, string text)
{
Task.Run(() => { client.SendReply(message.Channel.ExternalId, message.ExternalId, text); });
return 200;
}
}

View File

@ -1,52 +1,7 @@
# discord-bot # discord-bot
copy appsettings.json to appsettings.ENV.json and fill it in. dotnet seems to understand files called appsettings.json (and appsettings.xml?) and knows how to overwrite *specific values found within* the .[ENV].[extension] version copy appsettings.json and fill it in
# auth link # TODO
https://discord.com/oauth2/authorize?client_id=913003037348491264&permissions=274877942784&scope=bot listen to kafka, send the message back over it
that's read messages/view channels, send messages, send messages in threads, and attach files. but not add reactions?
# concepts
## 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
### Attachment
debating whether to save a copy of every single attachment. Discord allows 100MB attachments for turbo users, and shtikbot lives in several art channels. (unfortunately, being that shtikbot doesn't have a viable SMS spam vector, it's limited to 8MB, in contradiction to discord itself reporting a server that doesn't agree to put its own name on discord's finer-grained rules has a limit of 10MB)
### Channel
a place where communication can happen. any level of these can have any number of children. In matrix, everything is a "room" - even spaces and threads. Seems like a fine idea. So for vassago, a discord "channel" is a channel. a "thread" is a child of that channel. a "category" is a parent of that channel. A "server" (formerly "guild") is a parent of that channel. and fuck it, Discord itself is a "channel". Includes permissions vassago has for a channel; MaxAttachmentBytes, etc. go down the hierarchy until you find an override.
### FeaturePermission
the permissions of a feature. It can be restricted to accounts, to users, to channels. It has an internal name... and tag? and it can be (or not be) inheritable?
### Message
a message (duh). features bools for "mentions me", the external ID, the reference to the account, the channel.
### User
a person or program who operates an account. recognizing that 2 `Account`s belong to 1 `User` can be done by that user (using LinkMe). I should be able to collapse myself automatically.
## Behavior
both a "feature" and an "anti-feature". a channel might dictate something isn't allowed (lewdness in a g-rated channel). A person might not be allowed to do something - lots of me-only things like directing other bots (and the now rendered-moot Torrent feature). A behavior might need a command alias in a particular channel (freedomunits in jubel's)
so "behavior" might need to tag other data types? do I have it do a full select every time we get a message? ...no, only if the (other) triggering conditions are met. Then you can take your time.

View File

@ -1,329 +0,0 @@
namespace vassago;
using System.Linq.Expressions;
using vassago.Models;
using Microsoft.EntityFrameworkCore;
public static class Rememberer
{
private static readonly SemaphoreSlim dbAccessSemaphore = new(1, 1);
private static readonly ChattingContext db = new();
private static List<Channel> channels;
private static bool channelCacheDirty = true;
private static void cacheChannels()
{
dbAccessSemaphore.Wait();
channels = db.Channels.ToList();
Console.WriteLine($"caching channels. {channels.Count} channels retrieved");
foreach (Channel ch in channels)
{
if (ch.ParentChannelId != null)
{
ch.ParentChannel = channels.FirstOrDefault(c => c.Id == ch.ParentChannelId);
ch.ParentChannel.SubChannels ??= [];
if (!ch.ParentChannel.SubChannels.Contains(ch))
{
ch.ParentChannel.SubChannels.Add(ch);
}
}
if (ch.Messages?.Count > 0)
{
Console.WriteLine($"{ch.DisplayName} got {ch.Messages.Count} messages");
}
ch.SubChannels ??= [];
}
channelCacheDirty = false;
dbAccessSemaphore.Release();
}
public static Account SearchAccount(Expression<Func<Account, bool>> predicate)
{
Account toReturn;
dbAccessSemaphore.Wait();
toReturn = db.Accounts?.Include(a => a.IsUser)?.FirstOrDefault(predicate);
dbAccessSemaphore.Release();
return toReturn;
}
public static List<Account> SearchAccounts(Expression<Func<Account, bool>> predicate)
{
List<Account> toReturn;
dbAccessSemaphore.Wait();
toReturn = db.Accounts.Where(predicate).ToList();
dbAccessSemaphore.Release();
return toReturn;
}
public static Attachment SearchAttachment(Expression<Func<Attachment, bool>> predicate)
{
Attachment toReturn;
dbAccessSemaphore.Wait();
toReturn = db.Attachments.FirstOrDefault(predicate);
dbAccessSemaphore.Release();
return toReturn;
}
public static Channel SearchChannel(Func<Channel, bool> predicate)
{
if (channelCacheDirty)
Task.Run(() => cacheChannels()).Wait();
return channels.FirstOrDefault(predicate);
}
public static Message SearchMessage(Expression<Func<Message, bool>> predicate)
{
Message toReturn;
dbAccessSemaphore.Wait();
toReturn = db.Messages.FirstOrDefault(predicate);
dbAccessSemaphore.Release();
return toReturn;
}
public static List<Message> SearchMessages(Expression<Func<Message, bool>> predicate)
{
List<Message> toReturn;
dbAccessSemaphore.Wait();
toReturn = db.Messages.Where(predicate).ToList();
dbAccessSemaphore.Release();
return toReturn;
}
public static User SearchUser(Expression<Func<User, bool>> predicate)
{
User toReturn;
dbAccessSemaphore.Wait();
toReturn = db.Users.Where(predicate).Include(u => u.Accounts).FirstOrDefault(predicate);
dbAccessSemaphore.Release();
return toReturn;
}
public static void RememberAccount(Account toRemember)
{
dbAccessSemaphore.Wait();
toRemember.IsUser ??= new User { Accounts = [toRemember] };
db.Update(toRemember.IsUser);
db.SaveChanges();
dbAccessSemaphore.Release();
}
public static void RememberAttachment(Attachment toRemember)
{
dbAccessSemaphore.Wait();
toRemember.Message ??= new Message() { Attachments = [toRemember] };
db.Update(toRemember.Message);
db.SaveChanges();
dbAccessSemaphore.Release();
}
public static Channel RememberChannel(Channel toRemember)
{
if (channelCacheDirty)
Task.Run(() => cacheChannels()).Wait(); //so we always do 2 db trips?
dbAccessSemaphore.Wait();
db.Update(toRemember);
db.SaveChanges();
dbAccessSemaphore.Release();
channelCacheDirty = true;
cacheChannels();
return toRemember;
}
public static void RememberMessage(Message toRemember)
{
dbAccessSemaphore.Wait();
toRemember.Channel ??= new();
toRemember.Channel.Messages ??= [];
if (!toRemember.Channel.Messages.Contains(toRemember))
{
toRemember.Channel.Messages.Add(toRemember);
db.Update(toRemember.Channel);
}
db.Update(toRemember);
db.SaveChanges();
dbAccessSemaphore.Release();
}
public static void RememberUser(User toRemember)
{
dbAccessSemaphore.Wait();
db.Users.Update(toRemember);
db.SaveChanges();
dbAccessSemaphore.Release();
}
public static void ForgetAccount(Account toForget)
{
var user = toForget.IsUser;
var usersOnlyAccount = user.Accounts?.Count == 1;
if (usersOnlyAccount)
{
Rememberer.ForgetUser(user);
}
else
{
dbAccessSemaphore.Wait();
db.Accounts.Remove(toForget);
db.SaveChanges();
dbAccessSemaphore.Release();
}
}
public static void ForgetAttachment(Attachment toForget)
{
dbAccessSemaphore.Wait();
db.Attachments.Remove(toForget);
db.SaveChanges();
dbAccessSemaphore.Release();
}
public static void ForgetChannel(Channel toForget)
{
if (toForget.SubChannels?.Count > 0)
{
foreach (var childChannel in toForget.SubChannels.ToList())
{
ForgetChannel(childChannel);
}
}
if (toForget.Users?.Count > 0)
{
foreach (var account in toForget.Users.ToList())
{
ForgetAccount(account);
}
}
dbAccessSemaphore.Wait();
db.Channels.Remove(toForget);
db.SaveChanges();
dbAccessSemaphore.Release();
channelCacheDirty = true;
cacheChannels();
}
public static void ForgetMessage(Message toForget)
{
dbAccessSemaphore.Wait();
db.Messages.Remove(toForget);
db.SaveChanges();
dbAccessSemaphore.Release();
}
public static void ForgetUAC(UAC toForget)
{
dbAccessSemaphore.Wait();
db.UACs.Remove(toForget);
db.SaveChanges();
dbAccessSemaphore.Release();
}
public static void ForgetUser(User toForget)
{
dbAccessSemaphore.Wait();
db.Users.Remove(toForget);
db.SaveChanges();
dbAccessSemaphore.Release();
}
public static List<Account> AccountsOverview()
{
List<Account> toReturn;
dbAccessSemaphore.Wait();
toReturn = [.. db.Accounts];
dbAccessSemaphore.Release();
return toReturn;
}
public static List<Channel> ChannelsOverview()
{
if (channelCacheDirty)
Task.Run(() => cacheChannels()).Wait();
return channels;
}
public static Account AccountDetail(Guid Id)
{
Account toReturn;
dbAccessSemaphore.Wait();
toReturn = db.Accounts.Find(Id);
dbAccessSemaphore.Release();
return toReturn;
}
public static Attachment AttachmentDetail(Guid Id)
{
Attachment toReturn;
dbAccessSemaphore.Wait();
toReturn = db.Attachments.Find(Id);
dbAccessSemaphore.Release();
return toReturn;
}
public static Channel ChannelDetail(Guid Id, bool messages = false)
{
if (channelCacheDirty)
Task.Run(() => cacheChannels()).Wait();
var ch = channels.Find(c => c.Id == Id);
if (messages)
ch.Messages = SearchMessages(m => m.ChannelId == ch.Id);
return ch;
}
public static Message MessageDetail(Guid Id)
{
Message toReturn;
dbAccessSemaphore.Wait();
toReturn = db.Messages.Find(Id);
db.Entry(toReturn).Reference(m => m.Channel).Load();
dbAccessSemaphore.Release();
return toReturn;
}
public static UAC UACDetail(Guid Id)
{
UAC toReturn;
dbAccessSemaphore.Wait();
toReturn = db.UACs.Find(Id);
dbAccessSemaphore.Release();
return toReturn;
}
public static User UserDetail(Guid Id)
{
User toReturn;
dbAccessSemaphore.Wait();
toReturn = db.Users.Find(Id);
dbAccessSemaphore.Release();
return toReturn;
}
public static List<User> UsersOverview()
{
List<User> toReturn;
dbAccessSemaphore.Wait();
toReturn = db.Users.ToList();
dbAccessSemaphore.Release();
return toReturn;
}
public static List<UAC> UACsOverview()
{
List<UAC> toReturn;
dbAccessSemaphore.Wait();
toReturn = db.UACs.Include(uac => uac.Users).Include(uac => uac.Channels).Include(uac => uac.AccountInChannels).ToList();
dbAccessSemaphore.Release();
return toReturn;
}
public static UAC SearchUAC(Expression<Func<UAC, bool>> predicate)
{
UAC toReturn;
dbAccessSemaphore.Wait();
toReturn = db.UACs.Include(uac => uac.Users).Include(uac => uac.Channels).Include(uac => uac.AccountInChannels)
.FirstOrDefault(predicate);
dbAccessSemaphore.Release();
return toReturn;
}
public static List<UAC> MatchUACs(Message message)
{
var msgId = message.Id;
var accId = message.Author.Id;
var usrId = message.Author.IsUser.Id;
var chId = message.Channel.Id;
return SearchUACs(uac => uac.AccountInChannels.FirstOrDefault(aic => aic.Id == accId) != null
|| uac.Users.FirstOrDefault(usr => usr.Id == usrId) != null
|| uac.Channels.FirstOrDefault(ch => ch.Id == chId) != null);
}
public static List<UAC> SearchUACs(Expression<Func<UAC, bool>> predicate)
{
List<UAC> toReturn;
dbAccessSemaphore.Wait();
toReturn = db.UACs.Include(uac => uac.Users).Include(uac => uac.Channels).Include(uac => uac.AccountInChannels)
.Where(predicate).ToList();
dbAccessSemaphore.Release();
return toReturn;
}
public static void RememberUAC(UAC toRemember)
{
dbAccessSemaphore.Wait();
db.Update(toRemember);
db.SaveChanges();
dbAccessSemaphore.Release();
if (toRemember.Channels?.Count() > 0)
cacheChannels();
}
}

View File

@ -1,17 +0,0 @@
namespace vassago;
using System;
using System.Net.Http;
using vassago.Models;
using vassago.ProtocolInterfaces;
public static class Shared
{
public static Random r = new Random();
public static string DBConnectionString { get; set; }
public static HttpClient HttpClient { get; internal set; } = new HttpClient();
public static bool SetupSlashCommands { get; set; }
public static Uri API_URL { get; set; }
public static List<ProtocolInterface> ProtocolList { get; set; } = new();
public static WebApplication App { get; set; }
}

View File

@ -1,35 +0,0 @@
using System.Diagnostics;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using vassago.Models;
using vassago.WebInterface.Models;
namespace vassago.WebInterface.Controllers;
public class AccountsController(ChattingContext db) : Controller
{
private ChattingContext Database => db;
public async Task<IActionResult> Index()
{
return Database.Accounts != null ?
View(await Database.Accounts.ToListAsync()) :
Problem("Entity set '_db.Accounts' is null.");
}
public async Task<IActionResult> Details(Guid id)
{
var account = await Database.Accounts
.Include(a => a.IsUser)
.Include(a => a.SeenInChannel)
.FirstAsync(a => a.Id == id);
return Database.Accounts != null ?
View(account) :
Problem("Entity set '_db.Accounts' is null.");
}
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error()
{
return View(new ErrorPageViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
}

View File

@ -1,61 +0,0 @@
using System.ComponentModel;
using System.Diagnostics;
using System.Text;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using vassago.Models;
using vassago.WebInterface.Models;
namespace vassago.WebInterface.Controllers;
public class ChannelsController() : Controller
{
public IActionResult Details(Guid id)
{
var allChannels = Rememberer.ChannelsOverview();
if (allChannels == null)
return Problem("no channels.");
var channel = allChannels.FirstOrDefault(u => u.Id == id);
if (channel == null)
{
return Problem($"couldn't find channle {id}");
}
var walker = channel;
while (walker != null)
{
ViewData["breadcrumbs"] = $"<a href=\"{Url.ActionLink(action: "Details", controller: "Channels", values: new { id = walker.Id })}\">{walker.DisplayName}</a>/" +
ViewData["breadcrumbs"];
walker = walker.ParentChannel;
}
var sb = new StringBuilder();
sb.Append('[');
sb.Append($"{{text: \"{channel.SubChannels?.Count}\", nodes: [");
var first = true;
foreach (var subChannel in channel.SubChannels)
{
if (!first)
{
sb.Append(',');
}
else
{
first = false;
}
sb.Append($"{{\"text\": \"<a href=\\\"{Url.ActionLink(action: "Details", controller: "Channels", values: new { id = subChannel.Id })}\\\">{subChannel.DisplayName}</a>\"}}");
}
sb.Append("]}]");
ViewData.Add("subChannelsTree", sb.ToString());
return View(
new Tuple<Channel, Enumerations.LewdnessFilterLevel, Enumerations.MeannessFilterLevel>(
channel, channel.EffectivePermissions.LewdnessFilterLevel, channel.EffectivePermissions.MeannessFilterLevel
));
}
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error()
{
return View(new ErrorPageViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
}

View File

@ -1,233 +0,0 @@
using System.Diagnostics;
using System.Text;
using System.Web;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.FileSystemGlobbing.Internal.PathSegments;
using vassago.Models;
using vassago.WebInterface.Models;
namespace vassago.Controllers;
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
public HomeController(ILogger<HomeController> logger)
{
_logger = logger;
}
public IActionResult Index()
{
var allAccounts = Rememberer.AccountsOverview();
var allChannels = Rememberer.ChannelsOverview();
Console.WriteLine($"accounts: {allAccounts?.Count ?? 0}, channels: {allChannels?.Count ?? 0}");
var sb = new StringBuilder();
sb.Append('[');
//UACs
var allUACs = Rememberer.UACsOverview();
var first = true;
if(allUACs.Any())
{
sb.Append("{text: \"uacs\", expanded:true, nodes: [");
first=true;
foreach(var uac in allUACs)
{
if (first)
{
first = false;
}
else
{
sb.Append(',');
}
var displayedName = uac.DisplayName;
if(string.IsNullOrWhiteSpace(displayedName))
{
displayedName = $"[unnamed - {uac.Id}]";
}
sb.Append($"{{\"text\": \"<a href=\\\"{Url.ActionLink(action: "Details", controller: "UACs", values: new {id = uac.Id})}\\\">{displayedName}</a>\"}}");
}
sb.Append("]}");
}
else
{
sb.Append("{text: \"uacs (0)\", }");
}
//users
var users = Rememberer.UsersOverview();
if(users.Any())
{
sb.Append(",{text: \"users\", expanded:true, nodes: [");
first=true;
//refresh list; we'll be knocking them out again in serializeUser
allAccounts = Rememberer.AccountsOverview();
foreach(var user in users)
{
if (first)
{
first = false;
}
else
{
sb.Append(',');
}
serializeUser(ref sb, ref allAccounts, user);
}
sb.Append("]}");
}
//type error, e is not defined
//channels
sb.Append(",{text: \"channels\", expanded:true, nodes: [");
var topLevelChannels = Rememberer.ChannelsOverview().Where(x => x.ParentChannel == null).ToList();
first = true;
foreach (var topLevelChannel in topLevelChannels)
{
if (first)
{
first = false;
}
else
{
sb.Append(',');
}
serializeChannel(ref sb, ref allChannels, ref allAccounts, topLevelChannel);
}
sb.Append("]}");
if (allChannels.Any())
{
sb.Append(",{text: \"orphaned channels\", expanded:true, nodes: [");
first = true;
while (true)
{
if (first)
{
first = false;
}
else
{
sb.Append(',');
}
serializeChannel(ref sb, ref allChannels, ref allAccounts, allChannels.First());
if (!allChannels.Any())
{
break;
}
}
sb.Append("]}");
}
if (allAccounts.Any())
{
sb.Append(",{text: \"channelless accounts\", expanded:true, nodes: [");
first = true;
foreach (var acc in allAccounts)
{
if (first)
{
first = false;
}
else
{
sb.Append(',');
}
serializeAccount(ref sb, acc);
}
sb.Append("]}");
}
sb.Append("]");
ViewData.Add("treeString", sb.ToString());
return View("Index");
}
private void serializeChannel(ref StringBuilder sb, ref List<Channel> allChannels, ref List<Account> allAccounts, Channel currentChannel)
{
allChannels.Remove(currentChannel);
//"but adam", you say, "there's an href attribute, why make a link?" because that makes the entire bar a link, and trying to expand the node will probably click the link
sb.Append($"{{\"text\": \"<a href=\\\"{Url.ActionLink(action: "Details", controller: "Channels", values: new {id = currentChannel.Id})}\\\">{currentChannel.DisplayName}</a>\"");
sb.Append(", expanded:true ");
var theseAccounts = allAccounts.Where(a => a.SeenInChannel?.Id == currentChannel.Id).ToList();
allAccounts.RemoveAll(a => a.SeenInChannel?.Id == currentChannel.Id);
var first = true;
if (currentChannel.SubChannels != null || theseAccounts != null)
{
sb.Append(", \"nodes\": [");
if (currentChannel.SubChannels != null)
{
foreach (var subChannel in currentChannel.SubChannels)
{
if (first)
{
first = false;
}
else
{
sb.Append(',');
}
serializeChannel(ref sb, ref allChannels, ref allAccounts, subChannel);
}
if (theseAccounts != null && !first) //"first" here tells us that we have at least one subchannel
{
sb.Append(',');
}
}
if (theseAccounts != null)
{
first = true;
sb.Append($"{{\"text\": \"(accounts: {theseAccounts.Count()})\", \"expanded\":true, nodes:[");
foreach (var account in theseAccounts)
{
if (first)
{
first = false;
}
else
{
sb.Append(',');
}
serializeAccount(ref sb, account);
}
sb.Append("]}");
}
sb.Append(']');
}
sb.Append('}');
}
private void serializeAccount(ref StringBuilder sb, Account currentAccount)
{
sb.Append($"{{\"text\": \"<a href=\\\"{Url.ActionLink(action: "Details", controller: "Accounts", values: new {id = currentAccount.Id})}\\\">{currentAccount.DisplayName}</a>\"}}");
}
private void serializeUser(ref StringBuilder sb, ref List<Account> allAccounts, User currentUser)
{
sb.Append($"{{\"text\": \"<a href=\\\"{Url.ActionLink(action: "Details", controller: "Users", values: new {id = currentUser.Id})}\\\">");
sb.Append(currentUser.DisplayName);
sb.Append("</a>\", ");
var ownedAccounts = allAccounts.Where(a => a.IsUser == currentUser);
if (ownedAccounts?.Count() > 0)
{
sb.Append("nodes: [");
sb.Append($"{{\"text\": \"owned accounts:\", \"expanded\":true, \"nodes\": [");
var first = true;
foreach (var acc in ownedAccounts)
{
if(!first)
sb.Append(',');
serializeAccount(ref sb, acc);
first = false;
}
sb.Append("]}]");
}
sb.Append("}");
}
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error()
{
return View(new ErrorPageViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
}

View File

@ -1,25 +0,0 @@
using System.Diagnostics;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using vassago.Models;
using vassago.WebInterface.Models;
namespace vassago.WebInterface.Controllers;
public class UACsController() : Controller
{
public IActionResult Index()
{
return View(Rememberer.UACsOverview());
}
public IActionResult Details(Guid id)
{
return View(Rememberer.SearchUAC(uac => uac.Id == id));
}
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error()
{
return View(new ErrorPageViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
}

View File

@ -1,39 +0,0 @@
using System.Diagnostics;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using vassago.Models;
using vassago.WebInterface.Models;
namespace vassago.WebInterface.Controllers;
public class UsersController(ChattingContext db) : Controller
{
private ChattingContext Database => db;
public async Task<IActionResult> Index()
{
return Database.Users != null ?
View(await Database.Users.Include(u => u.Accounts).ToListAsync()) :
Problem("Entity set '_db.Users' is null.");
}
public async Task<IActionResult> Details(Guid id)
{
var user = await Database.Users
.Include(u => u.Accounts)
.FirstAsync(u => u.Id == id);
var allTheChannels = await Database.Channels.ToListAsync();
foreach(var acc in user.Accounts)
{
acc.SeenInChannel = allTheChannels.FirstOrDefault(c => c.Id == acc.SeenInChannel.Id);
}
return Database.Users != null ?
View(user) :
Problem("Entity set '_db.Users' is null.");
}
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error()
{
return View(new ErrorPageViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
}

View File

@ -1,52 +0,0 @@
using System.Diagnostics;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using vassago.Models;
using vassago.ProtocolInterfaces.DiscordInterface;
namespace vassago.Controllers.api;
[Route("api/[controller]")]
[ApiController]
public class AccountsController : ControllerBase
{
private readonly ILogger<AccountsController> _logger;
public AccountsController(ILogger<AccountsController> logger)
{
_logger = logger;
}
//microsoft: "you can't have multiple [FromBody]. The reason for this rule is some bullshti about storage buffers."
//cool story, bro. nobody gives a fuck, look at the boilerplate you've necessitated.
public class extraSpecialObjectReadGlorifiedTupleFor_UnlinkUser
{
public Guid acc_guid;
}
[HttpPatch]
[Route("UnlinkUser")]
[Produces("application/json")]
public IActionResult UnlinkUser([FromBody] extraSpecialObjectReadGlorifiedTupleFor_UnlinkUser req)
{
var acc_guid = req.acc_guid;
var accFromDb = Rememberer.SearchAccount(acc => acc.Id == acc_guid);
if (accFromDb == null)
{
var err = $"attempt to unlink user for acc {acc_guid}, not found";
_logger.LogError(err);
return NotFound(err);
}
var userFromDb = Rememberer.SearchUser(c => c.Id == accFromDb.IsUser.Id);
if (userFromDb == null)
{
var err = $"attempt to unlink user for {acc_guid}, doesn't have a user";
_logger.LogError(err);
return NotFound(err);
}
accFromDb.IsUser = null;
Rememberer.RememberAccount(accFromDb);
return Ok(accFromDb);
}
}

View File

@ -1,53 +0,0 @@
using System.Diagnostics;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using vassago.Models;
using vassago.ProtocolInterfaces.DiscordInterface;
namespace vassago.Controllers.api;
[Route("api/[controller]")]
[ApiController]
public class InternalAPIProtocolController : ControllerBase
{
private readonly ILogger<InternalAPIProtocolController> _logger;
public InternalAPIProtocolController(ILogger<InternalAPIProtocolController> logger)
{
_logger = logger;
}
[HttpPost]
[Route("PostMessage")]
[Produces("application/json")]
public IActionResult PostMessage(string messageText, Guid channelId)
{
return StatusCode(Behaver.Instance.SendMessage(channelId, messageText).Result);
}
[HttpPost]
[Route("ReplyToMessage")]
[Produces("application/json")]
public IActionResult ReplyToMessage(string messageText, Guid repliedMessageId)
{
Console.WriteLine($"ReplyToMessage - {repliedMessageId}, {messageText}");
return StatusCode(Behaver.Instance.Reply(repliedMessageId, messageText).Result);
}
[HttpPost]
[Route("SendFile")]
[Produces("application/json")]
public IActionResult SendFile(Guid channelId, string accompanyingText, string base64dData, string filename)
{
Console.WriteLine($"SendFile- {channelId}, {filename} (base64'd, {base64dData?.Length} chars), {accompanyingText}");
return StatusCode(Behaver.Instance.SendFile(channelId, base64dData, filename, accompanyingText).Result);
}
[HttpPost]
[Route("ReactToMessage")]
[Produces("application/json")]
public IActionResult ReactToMessage(string reactionString, Guid reactedMessageId)
{
Console.WriteLine($"ReactToMessage- {reactedMessageId}, {reactionString}");
return StatusCode(Behaver.Instance.React(reactedMessageId, reactionString).Result);
}
}

View File

@ -1,214 +0,0 @@
using System.Diagnostics;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using vassago.Models;
namespace vassago.Controllers.api;
[Route("api/[controller]")]
[ApiController]
public class RemembererController : ControllerBase
{
private readonly ILogger<RemembererController> _logger;
public RemembererController(ILogger<RemembererController> logger)
{
_logger = logger;
}
//Create
[HttpPut]
[Route("Account")]
[Produces("application/json")]
public Account CreateAccount(Guid id)
{
return Rememberer.AccountDetail(id);
}
[HttpPut]
[Route("Attachment")]
[Produces("application/json")]
public Attachment CreateAttachment(Guid id)
{
return Rememberer.AttachmentDetail(id);
}
[HttpPut]
[Route("Channels")]
[Produces("application/json")]
public Channel CreateChannel(Guid id)
{
return Rememberer.ChannelDetail(id);
}
[HttpPut]
[Route("Message")]
[Produces("application/json")]
public Message CreateMessage(Guid id)
{
return Rememberer.MessageDetail(id);
}
[HttpPut]
[Route("UAC")]
[Produces("application/json")]
public UAC CreateUAC(Guid id)
{
return Rememberer.UACDetail(id);
}
[HttpPut]
[Route("User")]
[Produces("application/json")]
public User CreateUser(Guid id)
{
return Rememberer.UserDetail(id);
}
//Read
[HttpGet]
[Route("Account")]
[Produces("application/json")]
public Account GetAccount(Guid id)
{
return Rememberer.AccountDetail(id);
}
[HttpGet]
[Route("Attachment")]
[Produces("application/json")]
public Attachment GetAttachment(Guid id)
{
return Rememberer.AttachmentDetail(id);
}
[HttpGet]
[Route("Channels")]
[Produces("application/json")]
public Channel GetChannel(Guid id)
{
return Rememberer.ChannelDetail(id);
}
[HttpGet]
[Route("Message")]
[Produces("application/json")]
public Message GetMessage(Guid id)
{
return Rememberer.MessageDetail(id);
}
[HttpGet]
[Route("UAC")]
[Produces("application/json")]
public UAC GetUAC(Guid id)
{
return Rememberer.UACDetail(id);
}
[HttpGet]
[Route("User")]
[Produces("application/json")]
public User GetUser(Guid id)
{
return Rememberer.UserDetail(id);
}
//Update
[HttpPatch]
[Route("Channels")]
[Produces("application/json")]
public IActionResult Patch([FromBody] Channel channel)
{
var fromDb = Rememberer.ChannelDetail(channel.Id);
if (fromDb == null)
{
_logger.LogError($"attempt to update channel {channel.Id}, not found");
return NotFound();
}
else
{
_logger.LogDebug($"patching {channel.DisplayName} (id: {channel.Id})");
}
//settable values: lewdness filter level, meanness filter level. maybe i could decorate them...
fromDb.LewdnessFilterLevel = channel.LewdnessFilterLevel;
fromDb.MeannessFilterLevel = channel.MeannessFilterLevel;
Rememberer.RememberChannel(fromDb);
return Ok(fromDb);
}
//Delete
[HttpDelete]
[Route("Account")]
[Produces("application/json")]
public IActionResult DeleteAccount(Guid id)
{
var fromDb = Rememberer.AccountDetail(id);
if (fromDb == null)
{
_logger.LogError($"attempt to delete account {id}, not found");
return NotFound();
}
Rememberer.ForgetAccount(fromDb);
return Ok();
}
[HttpDelete]
[Route("Attachment")]
[Produces("application/json")]
public IActionResult DeleteAttachment(Guid id)
{
var fromDb = Rememberer.AttachmentDetail(id);
if (fromDb == null)
{
_logger.LogError($"attempt to delete attachment {id}, not found");
return NotFound();
}
Rememberer.ForgetAttachment(fromDb);
return Ok();
}
[HttpDelete]
[Route("Channels/{id}")]
[Produces("application/json")]
public IActionResult DeleteChannel(Guid id)
{
var fromDb = Rememberer.ChannelDetail(id);
_logger.LogDebug($"delete channel {id}");
if (fromDb == null)
{
_logger.LogError($"attempt to delete channel {id}, not found");
return NotFound();
}
Rememberer.ForgetChannel(fromDb);
_logger.LogDebug($"delete channel {id} success");
return Ok();
}
[HttpDelete]
[Route("Message/{id}")]
[Produces("application/json")]
public IActionResult DeleteMessage(Guid id)
{
var fromDb = Rememberer.MessageDetail(id);
if (fromDb == null)
{
_logger.LogError($"attempt to delete message {id}, not found");
return NotFound();
}
Rememberer.ForgetMessage(fromDb);
return Ok();
}
[HttpDelete]
[Route("UAC/{id}")]
[Produces("application/json")]
public IActionResult DeleteUAC(Guid id)
{
var fromDb = Rememberer.UACDetail(id);
if (fromDb == null)
{
_logger.LogError($"attempt to delete uac {id}, not found");
return NotFound();
}
Rememberer.ForgetUAC(fromDb);
return Ok();
}
[HttpDelete]
[Route("User/{id}")]
[Produces("application/json")]
public IActionResult DeleteUser(Guid id)
{
var fromDb = Rememberer.UserDetail(id);
if (fromDb == null)
{
_logger.LogError($"attempt to delete user {id}, not found");
return NotFound();
}
Rememberer.ForgetUser(fromDb);
return Ok();
}
}

View File

@ -1,267 +0,0 @@
using System.Diagnostics;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using vassago.Models;
using vassago.ProtocolInterfaces.DiscordInterface;
namespace vassago.Controllers.api;
[Route("api/[controller]")]
[ApiController]
public class UACController : ControllerBase
{
private readonly ILogger<UACController> _logger;
public UACController(ILogger<UACController> logger)
{
_logger = logger;
}
//microsoft: "you can't have multiple [FromBody]. The reason for this rule is some bullshti about storage buffers."
//cool story, bro. nobody gives a fuck, look at the boilerplate you've necessitated.
public class extraSpecialObjectReadGlorifiedTupleFor_LinkChannel
{
public Guid uac_guid;
public Guid channel_guid;
}
[HttpPatch]
[Route("LinkChannel")]
[Produces("application/json")]
public IActionResult LinkChannel([FromBody] extraSpecialObjectReadGlorifiedTupleFor_LinkChannel req)
{
var uac_guid = req.uac_guid;
var channel_guid = req.channel_guid;
var uacFromDb = Rememberer.SearchUAC(uac => uac.Id == uac_guid);
if (uacFromDb == null)
{
var err = $"attempt to link channel for uac {uac_guid}, not found";
_logger.LogError(err);
return NotFound(err);
}
var channelFromDb = Rememberer.SearchChannel(c => c.Id == channel_guid);
if (channelFromDb == null)
{
var err = $"attempt to link channel for channel {channel_guid}, not found";
_logger.LogError(err);
return NotFound(err);
}
uacFromDb.Channels ??= [];
if (uacFromDb.Channels.Contains(channelFromDb))
{
return BadRequest("channel already linked");
}
uacFromDb.Channels.Add(channelFromDb);
Rememberer.RememberUAC(uacFromDb);
return Ok(uacFromDb);
}
public class extraSpecialObjectReadGlorifiedTupleFor_LinkUser
{
public Guid uac_guid;
public Guid user_guid;
}
[HttpPatch]
[Route("LinkUser")]
[Produces("application/json")]
public IActionResult LinkUser([FromBody] extraSpecialObjectReadGlorifiedTupleFor_LinkUser req)
{
var uac_guid = req.uac_guid;
var user_guid = req.user_guid;
var uacFromDb = Rememberer.SearchUAC(uac => uac.Id == uac_guid);
if (uacFromDb == null)
{
_logger.LogError($"attempt to link channal for uac {uac_guid}, not found");
return NotFound();
}
var userFromDb = Rememberer.SearchUser(c => c.Id == user_guid);
if (userFromDb == null)
{
_logger.LogError($"attempt to link user for user {user_guid}, not found");
return NotFound();
}
uacFromDb.Users ??= [];
if (uacFromDb.Users.Contains(userFromDb))
{
return BadRequest("user already linked");
}
uacFromDb.Users.Add(userFromDb);
Rememberer.RememberUAC(uacFromDb);
return Ok(uacFromDb);
}
public class extraSpecialObjectReadGlorifiedTupleFor_LinkAccount
{
public Guid uac_guid;
public Guid account_guid;
}
[HttpPatch]
[Route("LinkAccount")]
[Produces("application/json")]
public IActionResult LinkAccount([FromBody] extraSpecialObjectReadGlorifiedTupleFor_LinkAccount req)
{
var uac_guid = req.uac_guid;
var account_guid = req.account_guid;
var uacFromDb = Rememberer.SearchUAC(uac => uac.Id == uac_guid);
if (uacFromDb == null)
{
_logger.LogError($"attempt to link channal for uac {uac_guid}, not found");
return NotFound();
}
var accountFromDb = Rememberer.SearchAccount(c => c.Id == account_guid);
if (accountFromDb == null)
{
_logger.LogError($"attempt to link account for user {account_guid}, not found");
return NotFound();
}
uacFromDb.AccountInChannels ??= [];
if (uacFromDb.AccountInChannels.Contains(accountFromDb))
{
return BadRequest("account already linked");
}
uacFromDb.AccountInChannels.Add(accountFromDb);
Rememberer.RememberUAC(uacFromDb);
return Ok(uacFromDb);
}
[HttpPatch]
[Route("UnlinkUser")]
[Produces("application/json")]
public IActionResult UnlinkUser([FromBody] extraSpecialObjectReadGlorifiedTupleFor_LinkUser req)
{
var uac_guid = req.uac_guid;
var user_guid = req.user_guid;
var uacFromDb = Rememberer.SearchUAC(uac => uac.Id == uac_guid);
if (uacFromDb == null)
{
_logger.LogError($"attempt to unlink uac for uac {uac_guid}, not found");
return NotFound();
}
var userFromDb = Rememberer.SearchUser(c => c.Id == user_guid);
if (userFromDb == null)
{
_logger.LogError($"attempt to unlink user for user {user_guid}, not found");
return NotFound();
}
uacFromDb.Users ??= [];
if (!uacFromDb.Users.Contains(userFromDb))
{
return BadRequest("user not linked");
}
uacFromDb.Users.Remove(userFromDb);
Rememberer.RememberUAC(uacFromDb);
return Ok(uacFromDb);
}
[HttpPatch]
[Route("UnlinkAccount")]
[Produces("application/json")]
public IActionResult UnlinkAccount([FromBody] extraSpecialObjectReadGlorifiedTupleFor_LinkAccount req)
{
var uac_guid = req.uac_guid;
var account_guid = req.account_guid;
var uacFromDb = Rememberer.SearchUAC(uac => uac.Id == uac_guid);
if (uacFromDb == null)
{
_logger.LogError($"attempt to unlink uac for uac {uac_guid}, not found");
return NotFound();
}
var accountFromDb = Rememberer.SearchAccount(a => a.Id == account_guid);
if (accountFromDb == null)
{
_logger.LogError($"attempt to unlink account for user {account_guid}, not found");
return NotFound();
}
uacFromDb.AccountInChannels ??= [];
if (!uacFromDb.AccountInChannels.Contains(accountFromDb))
{
return BadRequest("account not linked");
}
uacFromDb.AccountInChannels.Remove(accountFromDb);
Rememberer.RememberUAC(uacFromDb);
return Ok(uacFromDb);
}
[HttpPatch]
[Route("UnlinkChannel")]
[Produces("application/json")]
public IActionResult UnlinkChannel([FromBody] extraSpecialObjectReadGlorifiedTupleFor_LinkChannel req)
{
var uac_guid = req.uac_guid;
var channel_guid = req.channel_guid;
var uacFromDb = Rememberer.SearchUAC(uac => uac.Id == uac_guid);
if (uacFromDb == null)
{
_logger.LogError($"attempt to unlink channal for uac {uac_guid}, not found");
return NotFound();
}
var channelFromDb = Rememberer.SearchChannel(c => c.Id == channel_guid);
if (channelFromDb == null)
{
_logger.LogError($"attempt to unlink user for user {channel_guid}, not found");
return NotFound();
}
uacFromDb.Users ??= [];
if (!uacFromDb.Channels.Contains(channelFromDb))
{
return BadRequest("user not linked");
}
uacFromDb.Channels.Remove(channelFromDb);
Rememberer.RememberUAC(uacFromDb);
return Ok(uacFromDb);
}
[HttpPut]
[Route("CreateForChannels/{Id}")]
[Produces("application/json")]
public IActionResult CreateForChannels(Guid Id)
{
_logger.LogDebug($"made it to controller. creating for channel {Id}");
var targetChannel = Rememberer.ChannelDetail(Id);
if (targetChannel == null)
{
return NotFound();
}
var newUAC = new UAC()
{
Channels = [targetChannel]
};
Rememberer.RememberUAC(newUAC);
Rememberer.RememberChannel(targetChannel);
return Ok(newUAC.Id);
}
[HttpPut]
[Route("AddTranslation/{Id}")]
[Produces("application/json")]
public IActionResult AddTranslation(Guid Id)
{
_logger.LogDebug($"made it to controller. creating translation for uac {Id}");
var uacFromDb = Rememberer.SearchUAC(uac => uac.Id == Id);
if (uacFromDb == null)
{
_logger.LogError($"attempt to create translation for uac {Id}, not found");
return NotFound();
}
uacFromDb.Translations ??= [];
uacFromDb.Translations.Add(Guid.NewGuid().ToString(), Guid.NewGuid().ToString());
Rememberer.RememberUAC(uacFromDb);
return Ok(uacFromDb.Translations.Count);
}
[HttpPut]
[Route("AddCommandAlteration/{Id}")]
[Produces("application/json")]
public IActionResult AddCommandAlteration(Guid Id)
{
_logger.LogDebug($"made it to controller. creating command alteration for uac {Id}");
var uacFromDb = Rememberer.SearchUAC(uac => uac.Id == Id);
if (uacFromDb == null)
{
_logger.LogError($"attempt to create command alteration for uac {Id}, not found");
return NotFound();
}
uacFromDb.CommandAlterations ??= [];
uacFromDb.CommandAlterations.Add(Guid.NewGuid().ToString(), Guid.NewGuid().ToString());
Rememberer.RememberUAC(uacFromDb);
return Ok(uacFromDb.CommandAlterations.Count);
}
}

View File

@ -1,8 +0,0 @@
namespace vassago.WebInterface.Models;
public class ErrorPageViewModel
{
public string RequestId { get; set; }
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
}

View File

@ -1,72 +0,0 @@
@model Account
@using Newtonsoft.Json
@using System.Text
@{
ViewData["Title"] = "Account details";
}
<a href="/">home</a>/@Html.Raw(ViewData["breadcrumbs"])
<table class="table">
<tbody>
<tr>
<th>Id</th>
<td>@Model.Id</td>
</tr>
<tr>
<th scope="row">belongs to user</th>
<td>@Model.IsUser.DisplayName</td>
<td><button onclick="unlinkAccountUser(() => { window.location.reload(); })">separate</button></2td>
</tr>
<tr>
<th scope="row">Seen in channel</th>
<td class="account @Model.SeenInChannel.Protocol"><div class="protocol-icon">&nbsp;</div>@Model.SeenInChannel.LineageSummary<a href="/Channels/Details/@Model.SeenInChannel.Id">@Model.SeenInChannel.DisplayName</a></td>
</tr>
<tr>
<th scope="row">UACs tied to account</th>
<td>
<div id="uacsTree"></div>
</td>
</tr>
</tbody>
</table>
@section Scripts{
<script type="text/javascript">
@{
var accountAsString = JsonConvert.SerializeObject(Model, new JsonSerializerSettings
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
});
}
const userOnLoad = @Html.Raw(accountAsString);
function jsonifyUser() {
var userNow = structuredClone(userOnLoad);
userNow.Accounts = null;
userNow.DisplayName = document.querySelector("#displayName").value;
console.log(userNow);
return userNow;
}
function getUacsTree() {
@{
var sb = new StringBuilder();
sb.Append("[{text: \"UACs\", \"expanded\":true, nodes: [");
var first = true;
for (int i = 0; i < 1; i++)
{
if (!first)
sb.Append(',');
sb.Append($"{{text: \"<input type=\\\"checkbox\\\" > is goated (w/ sauce)</input>\"}}");
first = false;
}
sb.Append("]}]");
}
console.log(@Html.Raw(sb.ToString()));
var tree = @Html.Raw(sb.ToString());
return tree;
}
$('#uacsTree').bstreeview({ data: getUacsTree() });
//document.querySelectorAll("input[type=checkbox]").forEach(node => { node.onchange = () => { patchModel(jsonifyUser(), '/api/Users/') } });
</script>
}

View File

@ -1,211 +0,0 @@
@using System.ComponentModel
@using Newtonsoft.Json
@using System.Text;
@model Tuple<Channel, Enumerations.LewdnessFilterLevel, Enumerations.MeannessFilterLevel>
@{
var ThisChannel = Model.Item1;
var IfInheritedLewdnessFilterLevel = Model.Item2;
var IfInheritedMeannessFilterLevel = Model.Item3;
}
<a href="/">home</a>/
@Html.Raw(ViewData["breadcrumbs"])
<table class="table">
<tbody>
<tr>
<th scope="row">Display Name</th>
<td>@ThisChannel.DisplayName</td>
</tr>
<tr>
<th scope="row">Channel type</th>
<td>@(Enumerations.GetDescription(ThisChannel.ChannelType))</td>
</tr>
<tr>
<th scope="row">Lewdness Filter Level</th>
<td>
<select name="LewdnessFilterLevel" id="LewdnessFilterLevel" onchange="patchModel(jsonifyChannel())">
<!option value="" @(ThisChannel.LewdnessFilterLevel == null ? "selected" : "")>⤵ inherited - @Enumerations.GetDescription(IfInheritedLewdnessFilterLevel)</!option>
@foreach (Enumerations.LewdnessFilterLevel enumVal in
Enum.GetValues(typeof(Enumerations.LewdnessFilterLevel)))
{
<!option value="@((int)enumVal)" @(ThisChannel.LewdnessFilterLevel == enumVal ? "selected" : "")>
@(Enumerations.GetDescription<Enumerations.LewdnessFilterLevel>(enumVal))</!option>
}
</select>
</td>
</tr>
<tr>
<th scope="row">Links Allowed</th>
<td>@(ThisChannel.LinksAllowed?.ToString() ?? "unknown")</td>
</tr>
<tr>
<th scope="row">Lineage summary</th>
<td>@ThisChannel.LineageSummary</td>
</tr>
<tr>
<th scope="row">max attachment bytes</th>
<td>@ThisChannel.MaxAttachmentBytes (i hear there's "ByteSize")</td>
</tr>
<tr>
<th scope="row">max message length</th>
<td>@(ThisChannel.MaxTextChars?.ToString() ?? "inherited")</td>
</tr>
<tr>
<th scope="row">Meanness Filter Level</th>
<td>
<select name="MeannessFilterLevel" id="MeannessFilterLevel" onchange="patchModel(jsonifyChannel())">
<!option value="" @(ThisChannel.MeannessFilterLevel == null ? "selected" : "")>⤵ inherited - @Enumerations.GetDescription(IfInheritedMeannessFilterLevel)</!option>
@foreach (Enumerations.MeannessFilterLevel enumVal in
Enum.GetValues(typeof(Enumerations.MeannessFilterLevel)))
{
<!option value="@((int)enumVal)" @(ThisChannel.MeannessFilterLevel == enumVal ? "selected" : "")>
@(Enumerations.GetDescription<Enumerations.MeannessFilterLevel>(enumVal))</!option>
}
</select>
</td>
</tr>
<tr>
<th scope="row">Messages (count)</th>
<td>@(ThisChannel.Messages?.Count ?? 0)</td>
</tr>
<tr>
<th scope="row">Protocol</th>
<td>@ThisChannel.Protocol</td>
</tr>
<tr>
<th scope="row">Reactions Possible</th>
<td>@(ThisChannel.ReactionsPossible?.ToString() ?? "inherited")</td>
</tr>
<tr>
<th scope="row">Sub Channels</th>
<td>
@if((ThisChannel.SubChannels?.Count ?? 0) > 0)
{
@Html.Raw("<div id=\"channelsTree\"></div>");
}
else
{
@Html.Raw("0")
}
</td>
</tr>
<tr>
<th scope="row">Accounts</th>
<td>
@if((ThisChannel.Users?.Count ?? 0) > 0)
{
@Html.Raw("<div id=\"accountsTree\"></div>");
}
else
{
@Html.Raw("none")
}
</td>
</tr>
<tr>
<th scope="row">Datamemos</th>
<td>
<div id="dataMemosTree"></div>
</td>
</tr>
<tr>
<td colspan="2">
<button onclick="forget()">forget</button>
</td>
</tr>
</tbody>
</table>
@section Scripts{
<script type="text/javascript">
@{
var modelAsString = JsonConvert.SerializeObject(ThisChannel, new JsonSerializerSettings
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
});
}
const channelOnLoad = @Html.Raw(modelAsString);
function jsonifyChannel() {
var channelNow = structuredClone(channelOnLoad);
channelNow.SubChannels = null;
channelNow.ParentChannel = null;
channelNow.Messages = null;
channelNow.Users = null;
channelNow.LewdnessFilterLevel = document.querySelector("#LewdnessFilterLevel").value;
channelNow.MeannessFilterLevel = document.querySelector("#MeannessFilterLevel").value;
console.log(channelNow);
return channelNow;
}
function forget(){
console.log("here we go");
if(window.confirm("delete? really really?") == true){
deleteModel(jsonifyChannel().Id, window.history.back);
}
}
function createMemo()
{
console.log("creating memo for channel..");
createMemoFor((newMemoId) => {
window.location.href = "/UACs/Details/" + newMemoId;
});
}
function channelsTree() {
//TOOD: see how accountsTree does all our HTML-ification over here in HTML land? but this doesn't? we should pick one and stick with it.
var tree = @Html.Raw(ViewData["subChannelsTree"]);
return tree;
}
function dataMemosTree(){
@{
var dmsb = new StringBuilder();
dmsb.Append("[{text: \"Data Memos\", \"expanded\":true, nodes: [");
var firstMemo = true;
if(ThisChannel.UACs != null) foreach(var memo in ThisChannel.UACs)
{
if(!firstMemo)
dmsb.Append(',');
var effectiveDisplayName = memo.DisplayName;
if(string.IsNullOrWhiteSpace(effectiveDisplayName))
{
effectiveDisplayName = $"[nameless] {memo.Id}";
}
dmsb.Append($"{{text: \"<a href=\\\"/UACs/Details/{memo.Id}\\\">{effectiveDisplayName}</a>\"}}");
firstMemo = false;
}
if(!firstMemo)
dmsb.Append(',');
dmsb.Append($"{{text: \"<button type=\\\"button\\\" class=\\\"btn btn-primary\\\" onclick=\\\"createMemo()\\\">+</button>\"}}");
dmsb.Append("]}]");
}
var tree = @Html.Raw(dmsb.ToString());
return tree;
}
function accountsTree() {
@{
var sb = new StringBuilder();
sb.Append("[{text: \"accounts\", \"expanded\":true, nodes: [");
var first = true;
foreach (var acc in ThisChannel.Users?.OrderBy(a => a?.SeenInChannel?.LineageSummary))
{
if(!first)
sb.Append(',');
sb.Append($"{{text: \"<div class=\\\"account {acc.Protocol}\\\"><div class=\\\"protocol-icon\\\">&nbsp;</div>{acc.SeenInChannel.LineageSummary}/<a href=\\\"/Accounts/Details/{acc.Id}\\\">{acc.DisplayName}</a>\"}}");
first=false;
}
sb.Append("]}]");
}
//console.log(@Html.Raw(sb.ToString()));
var tree = @Html.Raw(sb.ToString());
return tree;
}
$('#channelsTree').bstreeview({ data: channelsTree() });
$('#accountsTree').bstreeview({ data: accountsTree() });
$('#dataMemosTree').bstreeview({ data: dataMemosTree() });
</script>
}

View File

@ -1,17 +0,0 @@
@{
ViewData["Title"] = "Home Page";
}
<div id="tree">tree here</div>
@section Scripts{
<script type="text/javascript">
function getTree() {
var tree = @Html.Raw(ViewData["treeString"]);
console.log('tree');
console.log('@ViewData["treeString"]');
return tree;
}
$('#tree').bstreeview({ data: getTree() });
</script>
}

View File

@ -1,25 +0,0 @@
@model vassago.WebInterface.Models.ErrorPageViewModel
@{
ViewData["Title"] = "Error";
}
<h1 class="text-danger">Error.</h1>
<h2 class="text-danger">An error occurred while processing your request.</h2>
@if (Model.ShowRequestId)
{
<p>
<strong>Request ID:</strong> <code>@Model.RequestId</code>
</p>
}
<h3>Development Mode</h3>
<p>
Swapping to <strong>Development</strong> environment will display more detailed information about the error that occurred.
</p>
<p>
<strong>The Development environment shouldn't be enabled for deployed applications.</strong>
It can result in displaying sensitive information from exceptions to end users.
For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>
and restarting the app.
</p>

View File

@ -1,25 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>@ViewData["Title"] - vassago</title>
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
<link rel="stylesheet" href="~/css/fontawesome.min.css" />
<link rel="stylesheet" href="~/css/bs.min.treeview.css" />
<link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
<link rel="stylesheet" href="~/vassago.styles.css" asp-append-version="true" />
</head>
<body>
<div class="container">
<main role="main" class="pb-3">
@RenderBody()
</main>
</div>
<script src="~/lib/jquery/dist/jquery.min.js"></script>
<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
<script src="~/js/bstreeview.min.js"></script>
<script src="~/js/site.js" asp-append-version="true"></script>
@await RenderSectionAsync("Scripts", required: false)
</body>
</html>

View File

@ -1,48 +0,0 @@
/* Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification
for details on configuring this project to bundle and minify static web assets. */
a.navbar-brand {
white-space: normal;
text-align: center;
word-break: break-all;
}
a {
color: #0077cc;
}
.btn-primary {
color: #fff;
background-color: #1b6ec2;
border-color: #1861ac;
}
.nav-pills .nav-link.active, .nav-pills .show > .nav-link {
color: #fff;
background-color: #1b6ec2;
border-color: #1861ac;
}
.border-top {
border-top: 1px solid #e5e5e5;
}
.border-bottom {
border-bottom: 1px solid #e5e5e5;
}
.box-shadow {
box-shadow: 0 .25rem .75rem rgba(0, 0, 0, .05);
}
button.accept-policy {
font-size: 1rem;
line-height: inherit;
}
.footer {
position: absolute;
bottom: 0;
width: 100%;
white-space: nowrap;
line-height: 60px;
}

View File

@ -1,2 +0,0 @@
<script src="~/lib/jquery-validation/dist/jquery.validate.min.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"></script>

View File

@ -1,263 +0,0 @@
@using System.ComponentModel
@using Newtonsoft.Json
@using System.Text;
@model UAC
<a href="/">home</a>/
@Html.Raw(ViewData["breadcrumbs"])
<table class="table">
<tbody>
<tr>
<th scope="row">Display Name</th>
<td><input class="form-control" type="text" value="@Model.DisplayName" id="displayName"/></td>
</tr>
<tr>
<th scope="row">Description</th>
<td>@Html.Raw(Model.Description)
</tr>
<tr>
<th scope="row">Channels</th>
<td>
<div id="channelsTree"></div>
</td>
</tr>
<tr>
<th scope="row">Users</th>
<td>
<div id="usersTree"></div>
</td>
</tr>
<tr>
<th scope="row">AccountInChannels</th>
<td>
<div id="accountsTree"></div>
</td>
</tr>
<tr>
<th scope="row">Translations (@Model.Translations?.Count)</th>
<td>
next would be iterating over a dictionary. All reference on the internet implies this should work. I'm sick of trying to figure out why it doesn't. razor says to me, so i say to you: go fuck yourself; edit the db manually.
<table class="table">
<tbody>
@Html.Raw("<tr>");
@Html.Raw("<td><input type=\"text\" name=\"Model.Translations[{i}].Key\" value=\"{Model.Translations.ElementAt(i).Key}\" /></td>");
@Html.Raw("<td><input type=\"text\" name=\"Model.Translations[{i}].Value\" value=\"{Model.Translations.ElementAt(i).Value}\" /></td>");
@Html.Raw("<td><button type=\"button\" class=\"btn btn-danger\" onclick=\"removeTranslation()\">delete</button></td>");
@Html.Raw("</tr>");
<tr>
<td colspan="3">
<button type="button" class="btn btn-primary" onclick="addUAC_Translation()">add translation</button>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
<tr>
<th scope="row">Command Alterations (@Model.CommandAlterations?.Count)</th>
<td>
<table class="table">
<tbody>
@Html.Raw("<tr><td colspan=\"3\">o_O</td></tr>");
@Html.Raw("<tr>");
@Html.Raw("<td><input type=\"text\" name=\"Model.CommandAlterations[{j}].Key\" value=\"{Model.CommandAlterations.ElementAt(j).Key}\" /></td>");
@Html.Raw("<td><input type=\"text\" name=\"Model.CommandAlterations[{j}].Value\" value=\"{Model.CommandAlterations.ElementAt(j).Value}\" /></td>");
@Html.Raw("<td><button type=\"button\" class=\"btn btn-danger\" onclick=\"removeCommandAlteration()\">delete</button></td>");
@Html.Raw("</tr>");
<tr>
<td colspan="3">
<button type="button" class="btn btn-primary" onclick="addUAC_CommandAlteration()">add alteration</button>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
<tr>
<td colspan="2"><button class="btn btn-success">asdf</button></td>
</tr>
</tbody>
</table>
"adam", you may say, "why are there both translations and command alterations?"<br />
translations are like.. if someone says "addicting", you can safely guess that they don't know they should be saying "addictive".<br />
so if you say "this game is addicting", that comes in, we just pretend you said "this game is addictive".<br />
Command alterations, I have to acknowledge that you *did* say !freedomunits, but I'm changing my behavior and not converting. <br />
I guess theoretically you could "translate" freedomunits to nothing, then freeerdumberunits to freedomunits? but if we're doing that it becomes necessary to care about order, and get that right.<br />
so let's say, "translations" are "i'll pretend you said", and "command alterations" are "i'll pretend I expected".
<div id="link-modal" class="modal" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Insert GUID</h5>
<button type="button" class="btn btn-close" data-dismiss="link-modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<p>
<ul>
<li>//TODO: search</li>
</ul>
</p>
<p>
<input id="addmodaltext" type="text" />
</p>
</div>
<div class="modal-footer">
<button id="modalsubmit" type="button" class="btn btn-primary">Save changes</button>
<button type="button" class="btn btn-secondary" data-dismiss="link-modal">Close</button>
</div>
</div>
</div>
</div>
<div id="unlink-modal" class="modal" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Confirm</h5>
<button type="button" class="btn-close" data-dismiss="unlink-modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<p>
are you sure you want to unlink
<input id="unlinkModalText" enabled="false" type="text" />
</p>
<p>
to be clear; this is going to "unlink", not like.. delete.
</p>
</div>
<div class="modal-footer">
<button id="modalsubmit" type="button" class="btn btn-danger">unlink</button>
<button type="button" class="btn btn-secondary" data-dismiss="unlink-modal">Close</button>
</div>
</div>
</div>
</div>
<div id="remove-modal" class="modal" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Confirm</h5>
<button type="button" class="btn-close" data-dismiss="remove-modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<p>
are you sure you want to delete
<input id="removeModalText" enabled="false" type="text" />
</p>
</div>
<div class="modal-footer">
<button id="modalsubmit" type="button" class="btn btn-danger">delete</button>
<button type="button" class="btn btn-secondary" data-dismiss="remove-modal">Close</button>
</div>
</div>
</div>
</div>
@section Scripts{
<script type="text/javascript">
function linkModal(submitFn)
{
let modalbutton = document.querySelector("#link-modal button#modalsubmit");
modalbutton.onclick = () => { linkSubmitModal(submitFn); };
$("#link-modal").modal("show");
}
function linkSubmitModal(submitFn)
{
let guid = document.querySelector("#link-modal #addmodaltext").value;
submitFn(guid, () => { window.location.reload(); });
$("#link-modal").modal("hide");
console.log(submitFn + "(guid)");
}
function unlinkModal(submitFn, target)
{
document.querySelector("#unlink-modal #removeModalText").value = target;
let modalbutton = document.querySelector("#unlink-modal button#modalsubmit");
modalbutton.onclick = () => { unlinkModalSubmit(submitFn, target); };
$("#remove-modal").modal("show");
}
function unlinkModalSubmit(submitFn, target)
{
submitFn(target, () => { window.location.reload(); });
$("#unlink-modal").modal("hide");
}
function removeModal(submitFn, idx)
{
document.querySelector("#remove-modal #removeModalText").value = target;
let modalbutton = document.querySelector("#remove-modal button#modalsubmit");
modalbutton.onclick = () => { removeModalSubmit(submitFn, idx); };
$("#remove-modal").modal("show");
}
function removeModalSubmit(submitFn, idx)
{
submitFn(idx, () => { window.location.reload(); });
$("#remove-modal").modal("hide");
}
function channelsTree() {
@{
var sb = new StringBuilder();
sb.Append("[{text: \"Channels\", \"expanded\":true, nodes: [");
sb.Append($"{{text: \"<button type=\\\"button\\\" class=\\\"btn btn-primary\\\" onclick=\\\"linkModal(linkUAC_Channel)\\\">add channel</button>\"}}");
foreach (var acc in Model.Channels?.OrderBy(a => a.DisplayName))
{
sb.Append(',');
sb.Append($"{{text: \"<a href=\\\"/Channels/Details/{acc.Id}\\\">{acc.DisplayName}</a> - <button type=\\\"button\\\" class=\\\"btn btn-danger\\\" onclick=\\\"unlinkModal(unlinkUAC_Channel, '{acc.Id}')\\\">remove</button>\"}}");
}
sb.Append("]}]");
}
var tree = @Html.Raw(sb.ToString());
return tree;
}
function usersTree() {
@{
sb = new StringBuilder();
sb.Append("[{text: \"Users\", \"expanded\":true, nodes: [");
sb.Append($"{{text: \"<button type=\\\"button\\\" class=\\\"btn btn-primary\\\" onclick=\\\"linkModal(linkUAC_User)\\\">add user</button>\"}}");
foreach (var acc in Model.Users?.OrderBy(a => a.DisplayName))
{
sb.Append(',');
sb.Append($"{{text: \"<a href=\\\"/Users/Details/{acc.Id}\\\">{acc.DisplayName}</a> - <button type=\\\"button\\\" class=\\\"btn btn-danger\\\" onclick=\\\"unlinkModal(unlinkUAC_User, '{acc.Id}')\\\">remove</button>\"}}");
}
sb.Append("]}]");
}
var tree = @Html.Raw(sb.ToString());
return tree;
}
function accountsTree() {
@{
sb = new StringBuilder();
sb.Append("[{text: \"Accounts\", \"expanded\":true, nodes: [");
sb.Append($"{{text: \"<button type=\\\"button\\\" class=\\\"btn btn-primary\\\" onclick=\\\"linkModal(linkUAC_Account)\\\">add account</button>\"}}");
foreach (var acc in Model.AccountInChannels?.OrderBy(a => a.DisplayName))
{
sb.Append(',');
sb.Append($"{{text: \"<a href=\\\"/Accounts/Details/{acc.Id}\\\">{acc.DisplayName}</a> - <button type=\\\"button\\\" class=\\\"btn btn-danger\\\" onclick=\\\"unlinkModal(unlinkUAC_Acocunt, '{acc.Id}')\\\">remove</button>\"}}");
}
sb.Append("]}]");
}
var tree = @Html.Raw(sb.ToString());
return tree;
}
$('#channelsTree').bstreeview({ data: channelsTree() });
$('#usersTree').bstreeview({ data: usersTree() });
$('#accountsTree').bstreeview({ data: accountsTree() });
var components = window.location.pathname.split('/');
var uacId = components[3];
</script>
}

View File

@ -1,65 +0,0 @@
@model User
@using Newtonsoft.Json
@using System.Text
@{
ViewData["Title"] = "User details";
}
<a href="/">home</a>/@Html.Raw(ViewData["breadcrumbs"])
<table class="table">
<tbody>
<tr>
<th scope="row">Display Name (here)</th>
<td><input type="text" id="displayName" value="@Model.DisplayName" disabled alt="todo"></input> <button
onclick="patchModel(jsonifyUser())" disabled alt"todo">update</button></td>
</tr>
<tr>
<th scope="row">Accounts</th>
<td>
<div id="accountsTree"></div>
</td>
</tr>
</tbody>
</table>
@section Scripts{
<script type="text/javascript">
@{
var userAsString = JsonConvert.SerializeObject(Model, new JsonSerializerSettings
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
});
}
const userOnLoad = @Html.Raw(userAsString);
function jsonifyUser() {
var userNow = structuredClone(userOnLoad);
userNow.Accounts = null;
userNow.DisplayName = document.querySelector("#displayName").value;
//userNow.Tag_CanTwitchSummon = document.querySelector("#tagCanTwitchSummon").checked;
console.log(userNow);
return userNow;
}
function getAccountsTree() {
@{
var sb = new StringBuilder();
sb.Append("[{text: \"accounts\", \"expanded\":true, nodes: [");
var first = true;
foreach (var acc in Model.Accounts.OrderBy(a => a.SeenInChannel.LineageSummary))
{
if (!first)
sb.Append(',');
sb.Append($"{{text: \"<div class=\\\"account {acc.Protocol}\\\"><div class=\\\"protocol-icon\\\">&nbsp;</div>{acc.SeenInChannel.LineageSummary}/<a href=\\\"/Accounts/Details/{acc.Id}\\\">{acc.DisplayName}</a>\"}}");
first = false;
}
sb.Append("]}]");
}
console.log(@Html.Raw(sb.ToString()));
var tree = @Html.Raw(sb.ToString());
return tree;
}
$('#accountsTree').bstreeview({ data: getAccountsTree() });
document.querySelectorAll("input[type=checkbox]").forEach(node => { node.onchange = () => { patchModel(jsonifyUser()) } });
</script>
}

View File

@ -1,38 +0,0 @@
@model IEnumerable<User>
@{
ViewData["Title"] = "Users";
}
<table class="table">
<thead>
<tr>
<th>
@Html.DisplayNameFor(model => model.Id)
</th>
<th>
name*
</th>
<th>
number of associated accounts
</th>
</tr>
</thead>
<tbody>
@foreach (var item in Model) {
<tr>
<td>
@Html.DisplayFor(modelItem => item.Id)
</td>
<td>
@Html.DisplayFor(modelItem => item.DisplayName)
</td>
<td>
@Html.DisplayFor(modelItem => item.Accounts.Count)x
</td>
<td>
<a asp-action="Details" asp-route-id="@item.Id">Details</a>
</td>
</tr>
}
</tbody>
</table>

View File

@ -1,3 +0,0 @@
@using vassago
@using vassago.Models
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

View File

@ -1,3 +0,0 @@
@{
Layout = "_Layout";
}

5
appsettings.example.json Normal file
View File

@ -0,0 +1,5 @@
{
"token": "59 chars",
"botChatterChannel": 0,
"announcementChannel": 0
}

View File

@ -1,34 +0,0 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning",
"Microsoft.EntityFrameworkCore": "None"
}
},
"AllowedHosts": "*",
"DiscordTokens": [
],
"TwitchConfigs": [
],
"exchangePairsLocation": "assets/exchangepairs.json",
"DBConnectionString": "Host=azure.club;Database=db;Username=user;Password=password",
"SetupSlashCommands": false,
"Webhooks": [
{
"uacID": "9a94855a-e5a2-43b5-8420-ce670472ce95",
"Trigger": "test",
"Description": "<i>test</i>",
"Uri": "http://localhost",
"Method": "POST",
"Headers": [
["Content-Type", "application/json"]
],
"Content": "{\"content\": \"Hello, this is a message from my webhook: {text}. username: {account}, user: {user}\"}"
}
],
"KafkaBootstrap":"http://localhost:9092",
"KafkaName":"vassago",
"API_URL": "http://localhost:5093/api"
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 293 KiB

Some files were not shown because too many files have changed in this diff Show More