forked from adam/discord-bot-shtik
Compare commits
30 Commits
Author | SHA1 | Date | |
---|---|---|---|
ab16600463 | |||
af4d68caa1 | |||
0ac28c35fb | |||
942b11fcce | |||
4bd51721b6 | |||
e364b47c0f | |||
c6ea3ef790 | |||
2f7bc2c0ea | |||
43eaa5ad0d | |||
b4b5544ec4 | |||
c446521977 | |||
54414b8748 | |||
8cb4005192 | |||
cd2fa384d5 | |||
f4bed1e6cb | |||
88ca468708 | |||
464b6a90e4 | |||
581fddf6f9 | |||
bed8d3cbef | |||
2dd9e903db | |||
ef31418166 | |||
6d181e2b68 | |||
10167b597a | |||
26d8373dc8 | |||
a63a3fcb58 | |||
b84e47344b | |||
c5f9ae2c6b | |||
efb4ab00d2 | |||
451ace753d | |||
894b536c04 |
124
Behaver.cs
Normal file
124
Behaver.cs
Normal file
@ -0,0 +1,124 @@
|
||||
namespace vassago;
|
||||
#pragma warning disable 4014 //the "not awaited" error
|
||||
using 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 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();
|
||||
|
||||
public static Behaver Instance
|
||||
{
|
||||
get { return _instance; }
|
||||
}
|
||||
|
||||
public async Task<bool> ActOn(Message message)
|
||||
{
|
||||
foreach (var behavior in Behaviors)
|
||||
{
|
||||
if (behavior.ShouldAct(message))
|
||||
{
|
||||
behavior.ActOn(message);
|
||||
message.ActedOn = true;
|
||||
Console.WriteLine("acted on, moving forward");
|
||||
}
|
||||
}
|
||||
if (message.ActedOn == false && message.MentionsMe && message.Content.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.",
|
||||
@"┐(゚ ~゚ )┌", @"¯\_(ツ)_/¯", @"╮ (. ❛ ᴗ ❛.) ╭", @"╮(╯ _╰ )╭"
|
||||
};
|
||||
await message.Channel.SendMessage(responses[Shared.r.Next(responses.Count)]);
|
||||
message.ActedOn = true;
|
||||
}
|
||||
return message.ActedOn;
|
||||
}
|
||||
|
||||
internal bool IsSelf(Guid AccountId)
|
||||
{
|
||||
var db = new ChattingContext();
|
||||
var acc = db.Accounts.Find(AccountId);
|
||||
|
||||
return SelfAccounts.Any(acc => acc.Id == AccountId);
|
||||
}
|
||||
|
||||
public void MarkSelf(Account selfAccount)
|
||||
{
|
||||
var db = new ChattingContext();
|
||||
if(SelfUser == null)
|
||||
{
|
||||
SelfUser = selfAccount.IsUser;
|
||||
}
|
||||
else if (SelfUser != selfAccount.IsUser)
|
||||
{
|
||||
CollapseUsers(SelfUser, selfAccount.IsUser, db);
|
||||
}
|
||||
SelfAccounts = db.Accounts.Where(a => a.IsUser == SelfUser).ToList();
|
||||
}
|
||||
|
||||
public bool CollapseUsers(User primary, User secondary, ChattingContext db)
|
||||
{
|
||||
Console.WriteLine($"{secondary.Id} is being consumed into {primary.Id}");
|
||||
primary.Accounts.AddRange(secondary.Accounts);
|
||||
foreach(var a in secondary.Accounts)
|
||||
{
|
||||
a.IsUser = primary;
|
||||
}
|
||||
secondary.Accounts.Clear();
|
||||
Console.WriteLine("accounts transferred");
|
||||
try
|
||||
{
|
||||
db.SaveChangesAsync().Wait();
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
Console.WriteLine("First save exception.");
|
||||
Console.Error.WriteLine(e);
|
||||
return false;
|
||||
}
|
||||
Console.WriteLine("saved");
|
||||
|
||||
|
||||
db.Users.Remove(secondary);
|
||||
Console.WriteLine("old account cleaned up");
|
||||
try
|
||||
{
|
||||
db.SaveChangesAsync().Wait();
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
Console.WriteLine("Second save exception.");
|
||||
Console.Error.WriteLine(e);
|
||||
return false;
|
||||
}
|
||||
Console.WriteLine("saved, again, separately");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
#pragma warning restore 4014 //the "async not awaited" error
|
@ -1,64 +0,0 @@
|
||||
namespace vassago.Behavior;
|
||||
#pragma warning disable 4014 //the "not awaited" error
|
||||
using vassago.Models;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public class Behaver
|
||||
{
|
||||
private ChattingContext _db;
|
||||
public List<Account> Selves { get; internal set; } = new List<Account>();
|
||||
public static List<Behavior> Behaviors { get; private set; } = new List<Behavior>();
|
||||
internal Behaver()
|
||||
{
|
||||
_db = new ChattingContext();
|
||||
|
||||
var subtypes = AppDomain.CurrentDomain.GetAssemblies()
|
||||
.SelectMany(domainAssembly => domainAssembly.GetTypes())
|
||||
.Where(type => type.IsSubclassOf(typeof(Behavior)) && !type.IsAbstract &&
|
||||
type.GetCustomAttributes(typeof(StaticPlzAttribute),false)?.Any() == true)
|
||||
.ToList();
|
||||
|
||||
foreach (var subtype in subtypes)
|
||||
{
|
||||
Behaviors.Add((Behavior)Activator.CreateInstance(subtype));
|
||||
}
|
||||
}
|
||||
static Behaver() { }
|
||||
|
||||
private static readonly Behaver _instance = new Behaver();
|
||||
|
||||
public static Behaver Instance
|
||||
{
|
||||
get { return _instance; }
|
||||
}
|
||||
|
||||
public async Task<bool> ActOn(Message message)
|
||||
{
|
||||
foreach (var behavior in Behaviors)
|
||||
{
|
||||
if (behavior.ShouldAct(message))
|
||||
{
|
||||
behavior.ActOn(message);
|
||||
message.ActedOn = true;
|
||||
Console.WriteLine("acted on, moving forward");
|
||||
}
|
||||
}
|
||||
if (message.ActedOn == false && message.MentionsMe && message.Content.Contains('?') && !Behaver.Instance.Selves.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.",
|
||||
@"┐(゚ ~゚ )┌", @"¯\_(ツ)_/¯", @"╮ (. ❛ ᴗ ❛.) ╭", @"╮(╯ _╰ )╭"
|
||||
};
|
||||
await message.Channel.SendMessage(responses[Shared.r.Next(responses.Count)]);
|
||||
message.ActedOn = true;
|
||||
}
|
||||
return message.ActedOn;
|
||||
}
|
||||
}
|
||||
#pragma warning restore 4014 //the "async not awaited" error
|
@ -14,7 +14,7 @@ public abstract class Behavior
|
||||
|
||||
public virtual bool ShouldAct(Message message)
|
||||
{
|
||||
if(Behaver.Instance.Selves.Any(acc => acc.Id == message.Author.Id))
|
||||
if(Behaver.Instance.IsSelf(message.Author.Id))
|
||||
return false;
|
||||
return Regex.IsMatch(message.Content, $"{Trigger}\\b", RegexOptions.IgnoreCase);
|
||||
}
|
||||
|
@ -26,10 +26,11 @@ public class Detiktokify : Behavior
|
||||
}
|
||||
public override bool ShouldAct(Message message)
|
||||
{
|
||||
if(message.Channel.EffectivePermissions.MaxAttachmentBytes == 0)
|
||||
|
||||
if(Behaver.Instance.IsSelf(message.Author.Id))
|
||||
return false;
|
||||
|
||||
if(Behaver.Instance.Selves.Any(acc => acc.Id == message.Author.Id))
|
||||
if(message.Channel.EffectivePermissions.MaxAttachmentBytes == 0)
|
||||
return false;
|
||||
|
||||
var wordLikes = message.Content.Split(' ', StringSplitOptions.TrimEntries);
|
||||
@ -66,7 +67,7 @@ public class Detiktokify : Behavior
|
||||
{
|
||||
Console.Error.WriteLine("tried to dl, failed. \n" + string.Join('\n', res.ErrorOutput));
|
||||
await message.React("problemon");
|
||||
await message.Channel.SendMessage("tried to dl, failed. \n" + string.Join('\n', res.ErrorOutput));
|
||||
await message.Channel.SendMessage("tried to dl, failed. \n");
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -21,8 +21,9 @@ public class FiximageHeic : Behavior
|
||||
private List<Attachment> heics = new List<Attachment>();
|
||||
public override bool ShouldAct(Message message)
|
||||
{
|
||||
if(Behaver.Instance.Selves.Any(acc => acc.Id == message.Author.Id))
|
||||
if(Behaver.Instance.IsSelf(message.Author.Id))
|
||||
return false;
|
||||
|
||||
if (message.Attachments?.Count() > 0)
|
||||
{
|
||||
foreach (var att in message.Attachments)
|
||||
|
@ -19,7 +19,7 @@ public class GeneralSnarkCloudNative : Behavior
|
||||
public override string Trigger => "certain tech buzzwords that no human uses in normal conversation";
|
||||
public override bool ShouldAct(Message message)
|
||||
{
|
||||
if(Behaver.Instance.Selves.Any(acc => acc.Id == message.Author.Id))
|
||||
if(Behaver.Instance.IsSelf(message.Author.Id))
|
||||
return false;
|
||||
|
||||
if(message.Channel.EffectivePermissions.ReactionsPossible)
|
||||
|
@ -20,10 +20,15 @@ public class GeneralSnarkGooglit : Behavior
|
||||
|
||||
public override bool ShouldAct(Message message)
|
||||
{
|
||||
if(Behaver.Instance.Selves.Any(acc => acc.Id == message.Author.Id))
|
||||
return false;
|
||||
return Regex.IsMatch(message.Content, $"(just )?google( (it|that|things|before))?\\b", RegexOptions.IgnoreCase);
|
||||
}
|
||||
// public override bool ShouldAct(Message message)
|
||||
// {
|
||||
// if(Behaver.Instance.IsSelf(message.Author.Id))
|
||||
// return false;
|
||||
|
||||
// return Regex.IsMatch(message.Content, $"(just )?google( (it|that|things|before))?\\b", RegexOptions.IgnoreCase);
|
||||
// }
|
||||
|
||||
public override async Task<bool> ActOn(Message message)
|
||||
{
|
||||
|
62
Behavior/GeneralSnarkMisspellDefinitely.cs
Normal file
62
Behavior/GeneralSnarkMisspellDefinitely.cs
Normal file
@ -0,0 +1,62 @@
|
||||
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)
|
||||
{
|
||||
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.Content, "\\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.Content, "\\b"+k+"\\b", RegexOptions.IgnoreCase))
|
||||
{
|
||||
await message.Reply(k + "? so... " + snarkmap[k] + "?");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
@ -21,7 +21,7 @@ public class GeneralSnarkPlaying : Behavior
|
||||
|
||||
public override bool ShouldAct(Message message)
|
||||
{
|
||||
if(Behaver.Instance.Selves.Any(acc => acc.Id == message.Author.Id))
|
||||
if(Behaver.Instance.IsSelf(message.Author.Id))
|
||||
return false;
|
||||
|
||||
if((MeannessFilterLevel)message.Channel.EffectivePermissions.MeannessFilterLevel < MeannessFilterLevel.Medium ||
|
||||
|
@ -19,6 +19,10 @@ public class GeneralSnarkSkynet : Behavior
|
||||
|
||||
public override async Task<bool> ActOn(Message message)
|
||||
{
|
||||
|
||||
if(Behaver.Instance.IsSelf(message.Author.Id))
|
||||
return false;
|
||||
|
||||
switch (Shared.r.Next(5))
|
||||
{
|
||||
default:
|
||||
|
@ -18,8 +18,9 @@ public class Gratitude : Behavior
|
||||
|
||||
public override bool ShouldAct(Message message)
|
||||
{
|
||||
if(Behaver.Instance.Selves.Any(acc => acc.Id == message.Author.Id))
|
||||
if(Behaver.Instance.IsSelf(message.Author.Id))
|
||||
return false;
|
||||
|
||||
return Regex.IsMatch(message.Content, "\\bthank (yo)?u\\b", RegexOptions.IgnoreCase) && message.MentionsMe;
|
||||
}
|
||||
public override async Task<bool> ActOn(Message message)
|
||||
|
@ -69,9 +69,12 @@ public class LaughAtOwnJoke : Behavior
|
||||
}
|
||||
public override bool ShouldAct(Message message)
|
||||
{
|
||||
if(Behaver.Instance.IsSelf(message.Author.Id))
|
||||
return false;
|
||||
|
||||
Console.WriteLine($"{message.Content} == {_punchline}");
|
||||
return message.Content == _punchline
|
||||
&& Behaver.Instance.Selves.Any(acc => acc.Id == message.Author.Id);
|
||||
&& Behaver.Instance.IsSelf(message.Author.Id);
|
||||
}
|
||||
|
||||
public override async Task<bool> ActOn(Message message)
|
||||
|
@ -41,13 +41,11 @@ public class LinkClose : Behavior
|
||||
|
||||
public override string Description => "the second half of LinkMe - this is confirmation that you are the other one";
|
||||
|
||||
private ChattingContext _db;
|
||||
private string _pw;
|
||||
private Account _primary;
|
||||
|
||||
public LinkClose(string pw, Account primary)
|
||||
{
|
||||
_db = new ChattingContext();
|
||||
_pw = pw;
|
||||
_primary = primary;
|
||||
}
|
||||
@ -59,6 +57,9 @@ public class LinkClose : Behavior
|
||||
|
||||
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)
|
||||
{
|
||||
@ -71,44 +72,14 @@ public class LinkClose : Behavior
|
||||
return true;
|
||||
}
|
||||
|
||||
Console.WriteLine($"{secondary.Id} is being consumed into {_primary.IsUser.Id}");
|
||||
_primary.IsUser.Accounts.AddRange(secondary.Accounts);
|
||||
foreach(var a in secondary.Accounts)
|
||||
if(Behaver.Instance.CollapseUsers(_primary.IsUser, secondary, new ChattingContext()))
|
||||
{
|
||||
a.IsUser = _primary.IsUser;
|
||||
}
|
||||
secondary.Accounts.Clear();
|
||||
Console.WriteLine("accounts transferred");
|
||||
try
|
||||
{
|
||||
await _db.SaveChangesAsync();
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
message.Channel.SendMessage("error in first save");
|
||||
Console.WriteLine("fucks sake if I don't catch Exception it *mysteriously vanishes*");
|
||||
Console.Error.WriteLine(e);
|
||||
return false;
|
||||
}
|
||||
Console.WriteLine("saved");
|
||||
|
||||
|
||||
_db.Users.Remove(secondary);
|
||||
Console.WriteLine("old account cleaned up");
|
||||
try
|
||||
{
|
||||
await _db.SaveChangesAsync();
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
message.Channel.SendMessage("error in second save");
|
||||
Console.WriteLine("fucks sake if I don't catch Exception it *mysteriously vanishes*");
|
||||
Console.Error.WriteLine(e);
|
||||
return false;
|
||||
}
|
||||
Console.WriteLine("saved, again, separately");
|
||||
|
||||
await message.Channel.SendMessage("done :)");
|
||||
}
|
||||
else
|
||||
{
|
||||
await message.Channel.SendMessage("failed :(");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -15,12 +15,13 @@ public class PepTalk : Behavior
|
||||
{
|
||||
public override string Name => "PepTalk";
|
||||
|
||||
public override string Trigger => "i need (an? )?(peptalk|inspiration|ego-?boost)";
|
||||
public override string Trigger => "\\bneeds? (an? )?(peptalk|inspiration|ego-?boost)";
|
||||
|
||||
public override string Description => "assembles a pep talk from a few pieces";
|
||||
|
||||
public override async Task<bool> ActOn(Message message)
|
||||
{var piece1 = new List<string>{
|
||||
{
|
||||
var piece1 = new List<string>{
|
||||
"Champ, ",
|
||||
"Fact: ",
|
||||
"Everybody says ",
|
||||
|
@ -42,10 +42,10 @@ public class QRify : Behavior
|
||||
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))
|
||||
if(message.Channel.EffectivePermissions.MaxAttachmentBytes >= (ulong)(new System.IO.FileInfo($"tmp/qr{todaysnumber}.png").Length))
|
||||
await message.Channel.SendFile($"tmp/qr{todaysnumber}.png", null);
|
||||
else
|
||||
await message.Channel.SendMessage("resulting qr image 2 big 4 here");
|
||||
await message.Channel.SendMessage($"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");
|
||||
}
|
||||
|
@ -16,6 +16,11 @@ public class TwitchSummon : Behavior
|
||||
//HOWEVER, if not-the-broadcaster summons it, 1) all channel permissions to strict and 2) auto-disconnect on stream end
|
||||
//i don't know if the twitch *chat* interface has knowledge of if the stream ends. maybe auto-disconnect after like 2 hours?
|
||||
|
||||
public override bool ShouldAct(Message message)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public override async Task<bool> ActOn(Message message)
|
||||
{
|
||||
var ti = ProtocolInterfaces.ProtocolList.twitchs.FirstOrDefault();
|
||||
|
@ -22,8 +22,7 @@ namespace vassago
|
||||
public async Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var dbc = new ChattingContext();
|
||||
dbc.Database.EnsureCreated();
|
||||
dbc.Database.Migrate();
|
||||
await dbc.Database.MigrateAsync();
|
||||
|
||||
if (DiscordTokens?.Any() ?? false)
|
||||
foreach (var dt in DiscordTokens)
|
||||
@ -40,11 +39,12 @@ namespace vassago
|
||||
await t.Init(tc);
|
||||
ProtocolInterfaces.ProtocolList.twitchs.Add(t);
|
||||
}
|
||||
Console.WriteLine("survived initting");
|
||||
}
|
||||
|
||||
public Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
using System.Diagnostics;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using vassago.Models;
|
||||
|
||||
namespace vassago.Controllers;
|
||||
|
||||
public class ChannelsController : Controller
|
||||
{
|
||||
private readonly ILogger<ChannelsController> _logger;
|
||||
private readonly ChattingContext _db;
|
||||
|
||||
public ChannelsController(ILogger<ChannelsController> logger, ChattingContext db)
|
||||
{
|
||||
_logger = logger;
|
||||
_db = db;
|
||||
}
|
||||
|
||||
public async Task<IActionResult> Index(string searchString)
|
||||
{
|
||||
return _db.Channels != null ?
|
||||
View(_db.Channels.Include(u => u.ParentChannel).ToList().OrderBy(c => c.LineageSummary)) :
|
||||
Problem("Entity set '_db.Channels' is null.");
|
||||
}
|
||||
public async Task<IActionResult> Details(Guid id)
|
||||
{
|
||||
return _db.Channels != null ?
|
||||
View(await _db.Channels.Include(u => u.ParentChannel).FirstAsync(u => u.Id == id)) :
|
||||
Problem("Entity set '_db.Channels' is null.");
|
||||
}
|
||||
|
||||
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
|
||||
public IActionResult Error()
|
||||
{
|
||||
return View(new ErrorPageViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
|
||||
}
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
using System.Diagnostics;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using vassago.Models;
|
||||
|
||||
namespace vassago.Controllers;
|
||||
|
||||
public class HomeController : Controller
|
||||
{
|
||||
private readonly ILogger<HomeController> _logger;
|
||||
|
||||
public HomeController(ILogger<HomeController> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public IActionResult Index()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
|
||||
public IActionResult Error()
|
||||
{
|
||||
return View(new ErrorPageViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
|
||||
}
|
||||
}
|
@ -57,18 +57,21 @@ namespace vassago.Conversion
|
||||
if(currencyConf != null)
|
||||
{
|
||||
knownConversions.RemoveAll(kc => kc.Item1 == currencyConf.Base);
|
||||
knownAliases.Remove(knownAliases.FirstOrDefault(kvp => kvp.Value == currencyConf.Base).Key);
|
||||
foreach (var rate in currencyConf.rates)
|
||||
knownAliases.Remove(knownAliases.FirstOrDefault(kvp => kvp.Value == rate.Key).Key);
|
||||
}
|
||||
if (File.Exists(currencyPath))
|
||||
{
|
||||
currencyConf = JsonConvert.DeserializeObject<ExchangePairs>(File.ReadAllText(currencyPath));
|
||||
|
||||
if(!knownAliases.ContainsValue(currencyConf.Base))
|
||||
{
|
||||
knownAliases.Add(new List<string>() { currencyConf.Base.ToLower() }, 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, rate.Value);
|
||||
Console.WriteLine($"{rate.Key.ToLower()} alias of {rate.Key}");
|
||||
}
|
||||
|
8
Jenkinsfile
vendored
8
Jenkinsfile
vendored
@ -13,5 +13,13 @@ pipeline {
|
||||
archiveArtifacts artifacts: 'bin/Release/net7.0/linux-x64/publish/*'
|
||||
}
|
||||
}
|
||||
stage('Deploy'){
|
||||
when{
|
||||
branch "release"
|
||||
}
|
||||
steps{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
266
Migrations/20240510202057_channelpermissions_partofchannel.Designer.cs
generated
Normal file
266
Migrations/20240510202057_channelpermissions_partofchannel.Designer.cs
generated
Normal file
@ -0,0 +1,266 @@
|
||||
// <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
|
||||
}
|
||||
}
|
||||
}
|
228
Migrations/20240510202057_channelpermissions_partofchannel.cs
Normal file
228
Migrations/20240510202057_channelpermissions_partofchannel.cs
Normal file
@ -0,0 +1,228 @@
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
@ -34,9 +34,6 @@ namespace vassago.Migrations
|
||||
b.Property<string>("ExternalId")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<Guid?>("FeaturePermissionId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<bool>("IsBot")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
@ -54,8 +51,6 @@ namespace vassago.Migrations
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("FeaturePermissionId");
|
||||
|
||||
b.HasIndex("IsUserId");
|
||||
|
||||
b.HasIndex("SeenInChannelId");
|
||||
@ -115,37 +110,6 @@ namespace vassago.Migrations
|
||||
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");
|
||||
|
||||
@ -161,32 +125,20 @@ namespace vassago.Migrations
|
||||
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.ToTable("ChannelPermissions");
|
||||
});
|
||||
b.HasIndex("ParentChannelId");
|
||||
|
||||
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");
|
||||
b.ToTable("Channels");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("vassago.Models.Message", b =>
|
||||
@ -234,22 +186,13 @@ namespace vassago.Migrations
|
||||
.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");
|
||||
@ -274,21 +217,11 @@ namespace vassago.Migrations
|
||||
|
||||
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 =>
|
||||
@ -306,13 +239,6 @@ namespace vassago.Migrations
|
||||
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");
|
||||
@ -322,15 +248,6 @@ namespace vassago.Migrations
|
||||
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");
|
||||
|
@ -5,6 +5,7 @@ using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Components.Web;
|
||||
using static vassago.Models.Enumerations;
|
||||
|
||||
public class Channel
|
||||
@ -13,14 +14,20 @@ public class Channel
|
||||
public Guid Id { get; set; }
|
||||
public string ExternalId { get; set; }
|
||||
public string DisplayName { get; set; }
|
||||
public ChannelPermissions Permissions { get; set; }
|
||||
public List<Channel> SubChannels { get; set; }
|
||||
public Channel ParentChannel { get; set; }
|
||||
public string Protocol { get; set; }
|
||||
public List<Message> Messages { get; set; }
|
||||
public List<Account> Users { get; set; }
|
||||
public ChannelType ChannelType {get; set; }
|
||||
//public Dictionary<string, string> EmoteOverrides{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; }
|
||||
|
||||
[NonSerialized]
|
||||
public Func<string, string, Task> SendFile;
|
||||
@ -33,33 +40,28 @@ public class Channel
|
||||
{
|
||||
get
|
||||
{
|
||||
ChannelPermissions toReturn = Permissions ?? new ChannelPermissions();
|
||||
return GetEffectivePermissions(ref toReturn).Definite();
|
||||
}
|
||||
}
|
||||
private ChannelPermissions GetEffectivePermissions(ref ChannelPermissions settings)
|
||||
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)
|
||||
{
|
||||
if(settings == null) throw new ArgumentNullException();
|
||||
settings.LewdnessFilterLevel = settings.LewdnessFilterLevel ?? Permissions?.LewdnessFilterLevel;
|
||||
settings.MeannessFilterLevel = settings.MeannessFilterLevel ?? Permissions?.MeannessFilterLevel;
|
||||
settings.LinksAllowed = settings.LinksAllowed ?? Permissions?.LinksAllowed;
|
||||
settings.MaxAttachmentBytes = settings.MaxAttachmentBytes ?? Permissions?.MaxAttachmentBytes;
|
||||
settings.MaxTextChars = settings.MaxTextChars ?? Permissions?.MaxTextChars;
|
||||
settings.ReactionsPossible = settings.ReactionsPossible ?? Permissions?.ReactionsPossible;
|
||||
walker = walker.ParentChannel;
|
||||
path.Push(walker);
|
||||
}
|
||||
DefinitePermissionSettings toReturn = new DefinitePermissionSettings();
|
||||
|
||||
if(this.ParentChannel != null &&
|
||||
(settings.LewdnessFilterLevel == null ||
|
||||
settings.MeannessFilterLevel == null ||
|
||||
settings.LinksAllowed == null ||
|
||||
settings.MaxAttachmentBytes == null ||
|
||||
settings.MaxTextChars == null ||
|
||||
settings.ReactionsPossible == null))
|
||||
while(path.Count > 0)
|
||||
{
|
||||
return this.ParentChannel.GetEffectivePermissions(ref settings);
|
||||
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;
|
||||
}
|
||||
else
|
||||
{
|
||||
return settings;
|
||||
|
||||
return toReturn;
|
||||
}
|
||||
}
|
||||
public string LineageSummary
|
||||
@ -77,3 +79,13 @@ public class Channel
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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; }
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
namespace vassago.Models;
|
||||
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
public class ChannelPermissions
|
||||
{
|
||||
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||
public int Id { get; set; }
|
||||
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; }
|
||||
|
||||
internal DefinitePermissionSettings Definite()
|
||||
{
|
||||
return new DefinitePermissionSettings()
|
||||
{
|
||||
MaxAttachmentBytes = this.MaxAttachmentBytes ?? 0,
|
||||
MaxTextChars = this.MaxTextChars ?? 0,
|
||||
LinksAllowed = this.LinksAllowed ?? false,
|
||||
LewdnessFilterLevel = this.LewdnessFilterLevel ?? Enumerations.LewdnessFilterLevel.G,
|
||||
MeannessFilterLevel = this.MeannessFilterLevel ?? Enumerations.MeannessFilterLevel.Strict,
|
||||
ReactionsPossible = this.ReactionsPossible ?? false
|
||||
};
|
||||
}
|
||||
}
|
||||
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; }
|
||||
}
|
@ -9,14 +9,14 @@ public class ChattingContext : DbContext
|
||||
public DbSet<Channel> Channels { get; set; }
|
||||
//public DbSet<Emoji> Emoji {get;set;}
|
||||
public DbSet<Message> Messages { get; set; }
|
||||
public DbSet<ChannelPermissions> ChannelPermissions{get;set;}
|
||||
public DbSet<FeaturePermission> FeaturePermissions{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)
|
||||
{
|
||||
optionsBuilder.UseNpgsql(Shared.DBConnectionString)
|
||||
.EnableSensitiveDataLogging(true); //who the fuck is looking at log output but not allowed to see it? this should be on by default.
|
||||
}
|
||||
}
|
@ -45,7 +45,7 @@ public static class Enumerations
|
||||
Type type = enumerationValue.GetType();
|
||||
if (!type.IsEnum)
|
||||
{
|
||||
throw new ArgumentException("EnumerationValue must be of Enum type", "enumerationValue");
|
||||
throw new ArgumentException("EnumerationValue must be of Enum type", nameof(enumerationValue));
|
||||
}
|
||||
|
||||
//Tries to find a DescriptionAttribute for a potential friendly name
|
||||
|
@ -1,109 +0,0 @@
|
||||
namespace vassago.Models;
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using Discord.WebSocket;
|
||||
using static vassago.Models.Enumerations;
|
||||
|
||||
public enum WellknownPermissions
|
||||
{
|
||||
Administrator,
|
||||
TwitchSummon,
|
||||
}
|
||||
|
||||
public class FeaturePermission
|
||||
{
|
||||
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||
public Guid Id { get; set; }
|
||||
|
||||
public string InternalName { get; set; }
|
||||
public WellknownPermissions? InternalTag { get; set; }
|
||||
|
||||
//a permissions-needing-feature can determine how to use these, but a default "matches" is provided
|
||||
//for a message to "match", it must match in every category for which there are candidates.
|
||||
//e.g., Administrator is going to be restricted to Users only, and that'll be me
|
||||
//e.g., my future Torrent feature would be restricted to accounts and channels.
|
||||
//hmmm, what would be inheritable and what wouldn't?
|
||||
public IEnumerable<User> RestrictedToUsers { get; set; }
|
||||
public IEnumerable<Account> RestrictedToAccounts { get; set; }
|
||||
public IEnumerable<Channel> RestrictedToChannels { get; set; }
|
||||
public bool Inheritable { get; set; } = true;
|
||||
|
||||
public bool Matches(Message message)
|
||||
{
|
||||
if(RestrictedToUsers?.Count() > 0)
|
||||
{
|
||||
if(RestrictedToUsers.FirstOrDefault(u => u.Id == message.Author.IsUser.Id) == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if(RestrictedToChannels?.Count() > 0)
|
||||
{
|
||||
if(Inheritable)
|
||||
{
|
||||
var found = false;
|
||||
var walker = message.Channel;
|
||||
if (RestrictedToChannels.FirstOrDefault(c => c.Id == walker.Id) != null)
|
||||
{
|
||||
found = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
while (walker.ParentChannel != null)
|
||||
{
|
||||
walker = walker.ParentChannel;
|
||||
if(walker.Users.FirstOrDefault(a => a.ExternalId == message.Author.ExternalId) == null)
|
||||
{
|
||||
//the chain is broken; I don't exist in this channel
|
||||
break;
|
||||
}
|
||||
if (RestrictedToChannels.FirstOrDefault(c => c.Id == walker.Id) != null)
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (found)
|
||||
{
|
||||
|
||||
if(RestrictedToAccounts?.Count() > 0)
|
||||
{
|
||||
//walker is the "actual" restricted-to channel, but we're inheriting
|
||||
if(walker.Users.FirstOrDefault(a => a.Id == message.Author.Id) == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(RestrictedToChannels.FirstOrDefault(c => c.Id == message.Channel.Id) == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(RestrictedToAccounts?.Count() > 0)
|
||||
{
|
||||
if(RestrictedToAccounts.FirstOrDefault(a => a.Id == message.Author.Id) == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//if I got all the way down here, I must be good
|
||||
return true;
|
||||
}
|
||||
}
|
30
Program.cs
30
Program.cs
@ -1,13 +1,24 @@
|
||||
using Microsoft.AspNetCore.Mvc.Razor;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.AspNetCore.Mvc.NewtonsoftJson;
|
||||
using vassago.Models;
|
||||
|
||||
#pragma warning disable CA2254
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
// Add services to the container.
|
||||
builder.Services.AddControllersWithViews();
|
||||
builder.Services.AddSingleton<IHostedService, vassago.ConsoleService>();
|
||||
builder.Services.AddDbContext<ChattingContext>(options =>
|
||||
options.UseNpgsql(builder.Configuration.GetConnectionString("ChattingContext")));
|
||||
builder.Services.AddDbContext<ChattingContext>();
|
||||
builder.Services.AddControllers().AddNewtonsoftJson();
|
||||
builder.Services.AddProblemDetails();
|
||||
builder.Services.Configure<RazorViewEngineOptions>(o => {
|
||||
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();
|
||||
|
||||
@ -24,4 +35,19 @@ app.MapControllerRoute(
|
||||
name: "default",
|
||||
pattern: "{controller=Home}/{action=Index}/{id?}");
|
||||
|
||||
app.UseSwagger();
|
||||
|
||||
app.UseSwaggerUI(c =>
|
||||
{
|
||||
c.SwaggerEndpoint("/swagger/v1/swagger.json", "api");
|
||||
});
|
||||
|
||||
app.UseExceptionHandler();
|
||||
app.UseStatusCodePages();
|
||||
|
||||
if (app.Environment.IsDevelopment())
|
||||
{
|
||||
app.UseDeveloperExceptionPage();
|
||||
}
|
||||
|
||||
app.Run();
|
||||
|
@ -58,15 +58,12 @@ public class DiscordInterface
|
||||
protocolAsChannel = new Channel()
|
||||
{
|
||||
DisplayName = "discord (itself)",
|
||||
Permissions = new Models.ChannelPermissions()
|
||||
{
|
||||
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
|
||||
},
|
||||
ReactionsPossible = true,
|
||||
ExternalId = null,
|
||||
Protocol = PROTOCOL,
|
||||
SubChannels = new List<Channel>()
|
||||
@ -117,11 +114,11 @@ public class DiscordInterface
|
||||
|
||||
private async Task SelfConnected()
|
||||
{
|
||||
var selfUser = UpsertAccount(client.CurrentUser, protocolAsChannel.Id);
|
||||
selfUser.DisplayName = client.CurrentUser.Username;
|
||||
|
||||
var selfAccount = UpsertAccount(client.CurrentUser, protocolAsChannel);
|
||||
selfAccount.DisplayName = client.CurrentUser.Username;
|
||||
await _db.SaveChangesAsync();
|
||||
Behaver.Instance.Selves.Add(selfUser);
|
||||
|
||||
Behaver.Instance.MarkSelf(selfAccount);
|
||||
}
|
||||
|
||||
private async Task MessageReceived(SocketMessage messageParam)
|
||||
@ -153,7 +150,7 @@ public class DiscordInterface
|
||||
var guild = UpsertChannel(arg.Guild);
|
||||
var defaultChannel = UpsertChannel(arg.Guild.DefaultChannel);
|
||||
defaultChannel.ParentChannel = guild;
|
||||
var u = UpsertAccount(arg, guild.Id);
|
||||
var u = UpsertAccount(arg, guild);
|
||||
u.DisplayName = arg.DisplayName;
|
||||
}
|
||||
private async Task ButtonHandler(SocketMessageComponent component)
|
||||
@ -229,8 +226,7 @@ public class DiscordInterface
|
||||
m.ExternalId = dMessage.Id.ToString();
|
||||
m.Timestamp = dMessage.EditedTimestamp ?? dMessage.CreatedAt;
|
||||
m.Channel = UpsertChannel(dMessage.Channel);
|
||||
m.Author = UpsertAccount(dMessage.Author, m.Channel.Id);
|
||||
m.Author.SeenInChannel = m.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.
|
||||
@ -298,16 +294,15 @@ public class DiscordInterface
|
||||
c.Protocol = protocolAsChannel.Protocol;
|
||||
c.ParentChannel = protocolAsChannel;
|
||||
c.SubChannels = c.SubChannels ?? new List<Channel>();
|
||||
c.Permissions = c.Permissions ?? new Models.ChannelPermissions();
|
||||
c.Permissions.MaxAttachmentBytes = channel.MaxUploadLimit;
|
||||
c.MaxAttachmentBytes = channel.MaxUploadLimit;
|
||||
|
||||
c.SendMessage = (t) => { throw new InvalidOperationException($"channel {channel.Name} is guild; cannot accept text"); };
|
||||
c.SendFile = (f, t) => { throw new InvalidOperationException($"channel {channel.Name} is guild; send file"); };
|
||||
return c;
|
||||
}
|
||||
internal Account UpsertAccount(IUser user, Guid inChannel)
|
||||
internal Account UpsertAccount(IUser user, Channel inChannel)
|
||||
{
|
||||
var acc = _db.Accounts.FirstOrDefault(ui => ui.ExternalId == user.Id.ToString() && ui.SeenInChannel.Id == inChannel);
|
||||
var acc = _db.Accounts.FirstOrDefault(ui => ui.ExternalId == user.Id.ToString() && ui.SeenInChannel.Id == inChannel.Id);
|
||||
if (acc == null)
|
||||
{
|
||||
acc = new Account();
|
||||
@ -317,6 +312,7 @@ public class DiscordInterface
|
||||
acc.ExternalId = user.Id.ToString();
|
||||
acc.IsBot = user.IsBot || user.IsWebhook;
|
||||
acc.Protocol = PROTOCOL;
|
||||
acc.SeenInChannel = inChannel;
|
||||
|
||||
acc.IsUser = _db.Users.FirstOrDefault(u => u.Accounts.Any(a => a.ExternalId == acc.ExternalId && a.Protocol == acc.Protocol));
|
||||
if(acc.IsUser == null)
|
||||
@ -332,13 +328,11 @@ public class DiscordInterface
|
||||
var c = _db.Channels.FirstOrDefault(c => c.ExternalId == msg.Channel.Id.ToString());
|
||||
//var preferredEmote = c.EmoteOverrides?[e] ?? e; //TODO: emote overrides
|
||||
var preferredEmote = e;
|
||||
Emoji emoji;
|
||||
if (Emoji.TryParse(preferredEmote, out emoji))
|
||||
if (Emoji.TryParse(preferredEmote, out Emoji emoji))
|
||||
{
|
||||
return msg.AddReactionAsync(emoji);
|
||||
}
|
||||
Emote emote;
|
||||
if (!Emote.TryParse(preferredEmote, out emote))
|
||||
if (!Emote.TryParse(preferredEmote, out Emote emote))
|
||||
{
|
||||
if (preferredEmote == e)
|
||||
Console.Error.WriteLine($"never heard of emote {e}");
|
||||
|
@ -9,7 +9,6 @@ using Discord.Net;
|
||||
|
||||
namespace vassago.DiscordInterface
|
||||
{
|
||||
|
||||
public static class SlashCommandsHelper
|
||||
{
|
||||
private static List<CommandSetup> slashCommands = new List<CommandSetup>()
|
||||
|
@ -2,6 +2,6 @@ namespace vassago.ProtocolInterfaces;
|
||||
|
||||
public static class ProtocolList
|
||||
{
|
||||
public static List<DiscordInterface.DiscordInterface> discords = new List<DiscordInterface.DiscordInterface>();
|
||||
public static List<TwitchInterface.TwitchInterface> twitchs = new List<TwitchInterface.TwitchInterface>();
|
||||
public static List<DiscordInterface.DiscordInterface> discords = new();
|
||||
public static List<TwitchInterface.TwitchInterface> twitchs = new();
|
||||
}
|
@ -39,15 +39,12 @@ public class TwitchInterface
|
||||
protocolAsChannel = new Channel()
|
||||
{
|
||||
DisplayName = "twitch (itself)",
|
||||
Permissions = new ChannelPermissions()
|
||||
{
|
||||
MeannessFilterLevel = Enumerations.MeannessFilterLevel.Medium,
|
||||
LewdnessFilterLevel = Enumerations.LewdnessFilterLevel.G,
|
||||
MaxTextChars = 500,
|
||||
MaxAttachmentBytes = 0,
|
||||
LinksAllowed = false,
|
||||
ReactionsPossible = false
|
||||
},
|
||||
ReactionsPossible = false,
|
||||
ExternalId = null,
|
||||
Protocol = PROTOCOL,
|
||||
SubChannels = new List<Channel>()
|
||||
@ -115,10 +112,10 @@ public class TwitchInterface
|
||||
var m = UpsertMessage(e.WhisperMessage);
|
||||
m.Channel.ChannelType = vassago.Models.Enumerations.ChannelType.DM;
|
||||
m.MentionsMe = Regex.IsMatch(e.WhisperMessage.Message?.ToLower(), $"\\b@{e.WhisperMessage.BotUsername.ToLower()}\\b");
|
||||
_db.SaveChanges();
|
||||
await _db.SaveChangesAsync();
|
||||
|
||||
await Behaver.Instance.ActOn(m);
|
||||
_db.SaveChanges();
|
||||
await _db.SaveChangesAsync();
|
||||
}
|
||||
|
||||
private async void Client_OnMessageReceivedAsync(object sender, OnMessageReceivedArgs e)
|
||||
@ -134,18 +131,18 @@ public class TwitchInterface
|
||||
var m = UpsertMessage(e.ChatMessage);
|
||||
m.MentionsMe = Regex.IsMatch(e.ChatMessage.Message?.ToLower(), $"@{e.ChatMessage.BotUsername.ToLower()}\\b") ||
|
||||
e.ChatMessage.ChatReply?.ParentUserLogin == e.ChatMessage.BotUsername;
|
||||
_db.SaveChanges();
|
||||
await _db.SaveChangesAsync();
|
||||
|
||||
await Behaver.Instance.ActOn(m);
|
||||
_db.SaveChanges();
|
||||
await _db.SaveChangesAsync();
|
||||
}
|
||||
|
||||
private async void Client_OnConnected(object sender, OnConnectedArgs e)
|
||||
{
|
||||
var selfUser = UpsertAccount(e.BotUsername, protocolAsChannel.Id);
|
||||
var selfAccount = UpsertAccount(e.BotUsername, protocolAsChannel.Id);
|
||||
|
||||
await _db.SaveChangesAsync();
|
||||
Behaver.Instance.Selves.Add(selfUser);
|
||||
Behaver.Instance.MarkSelf(selfAccount);
|
||||
|
||||
Console.WriteLine($"Connected to {e.AutoJoinChannel}");
|
||||
}
|
||||
|
36
README.md
36
README.md
@ -1,8 +1,42 @@
|
||||
# discord-bot
|
||||
|
||||
copy appsettings.json and fill it in
|
||||
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
|
||||
|
||||
# auth link
|
||||
|
||||
https://discord.com/oauth2/authorize?client_id=913003037348491264&permissions=274877942784&scope=bot
|
||||
that's read messages/view channels, send messages, send messages in threads, and attach files. but not add reactions?
|
||||
|
||||
# concepts
|
||||
|
||||
## Data Types
|
||||
|
||||
### 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.
|
||||
|
||||
### Attachment
|
||||
|
||||
debating whether to save a copy of every single attachment. Discord allows 25MB attachments, and shtikbot lives in several art channels.
|
||||
|
||||
### 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.
|
@ -1,12 +0,0 @@
|
||||
@{
|
||||
ViewData["Title"] = "Home Page";
|
||||
}
|
||||
<div>
|
||||
<a href="Users">Users</a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="Accounts">Accounts</a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="Channels">Channels</a>
|
||||
</div>
|
58
WebInterface/Controllers/ChannelsController.cs
Normal file
58
WebInterface/Controllers/ChannelsController.cs
Normal file
@ -0,0 +1,58 @@
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using vassago.Models;
|
||||
|
||||
namespace vassago.Controllers;
|
||||
|
||||
public class ChannelsController : Controller
|
||||
{
|
||||
private readonly ILogger<ChannelsController> _logger;
|
||||
private readonly ChattingContext _db;
|
||||
|
||||
public ChannelsController(ILogger<ChannelsController> logger, ChattingContext db)
|
||||
{
|
||||
_logger = logger;
|
||||
_db = db;
|
||||
}
|
||||
|
||||
public async Task<IActionResult> Index(string searchString)
|
||||
{
|
||||
return _db.Channels != null ?
|
||||
View(_db.Channels.Include(u => u.ParentChannel).ToList().OrderBy(c => c.LineageSummary)) :
|
||||
Problem("Entity set '_db.Channels' is null.");
|
||||
}
|
||||
public async Task<IActionResult> Details(Guid id)
|
||||
{
|
||||
if(_db.Channels == null)
|
||||
return Problem("Entity set '_db.Channels' is null.");
|
||||
//"but adam", says the strawman, "why load *every* channel and walk your way up? surely there's a .Load command that works or something."
|
||||
//eh. I checked. Not really. You could make an SQL view that recurses its way up, meh idk how. You could just eagerly load *every* related object...
|
||||
//but that would take in all the messages.
|
||||
//realistically I expect this will have less than 1MB of total "channels", and several GB of total messages per (text) channel.
|
||||
var AllChannels = await _db.Channels
|
||||
.Include(u => u.SubChannels)
|
||||
.Include(u => u.Users)
|
||||
.Include(u => u.ParentChannel)
|
||||
.ToListAsync();
|
||||
var channel = AllChannels.First(u => u.Id == 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;
|
||||
}
|
||||
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 });
|
||||
}
|
||||
}
|
193
WebInterface/Controllers/HomeController.cs
Normal file
193
WebInterface/Controllers/HomeController.cs
Normal file
@ -0,0 +1,193 @@
|
||||
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;
|
||||
|
||||
namespace vassago.Controllers;
|
||||
|
||||
public class HomeController : Controller
|
||||
{
|
||||
private readonly ILogger<HomeController> _logger;
|
||||
private readonly ChattingContext _db;
|
||||
|
||||
public HomeController(ILogger<HomeController> logger, ChattingContext db)
|
||||
{
|
||||
_logger = logger;
|
||||
_db = db;
|
||||
}
|
||||
|
||||
public IActionResult Index()
|
||||
{
|
||||
var allAccounts = _db.Accounts.ToList();
|
||||
var allChannels = _db.Channels.Include(c => c.Users).ToList();
|
||||
var sb = new StringBuilder();
|
||||
sb.Append("[");
|
||||
sb.Append("{text: \"channels\", nodes: [");
|
||||
|
||||
var first = true;
|
||||
var topLevelChannels = _db.Channels.Where(x => x.ParentChannel == null);
|
||||
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\", 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\", nodes: [");
|
||||
first = true;
|
||||
foreach (var acc in allAccounts)
|
||||
{
|
||||
if (first)
|
||||
{
|
||||
first = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Append(',');
|
||||
}
|
||||
serializeAccount(ref sb, acc);
|
||||
}
|
||||
sb.Append("]}");
|
||||
}
|
||||
var users = _db.Users.ToList();
|
||||
if(users.Any())
|
||||
{
|
||||
sb.Append(",{text: \"users\", nodes: [");
|
||||
first=true;
|
||||
//refresh list; we'll be knocking them out again in serializeUser
|
||||
allAccounts = _db.Accounts.ToList();
|
||||
foreach(var user in users)
|
||||
{
|
||||
if (first)
|
||||
{
|
||||
first = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Append(',');
|
||||
}
|
||||
serializeUser(ref sb, ref allAccounts, user);
|
||||
}
|
||||
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>\"");
|
||||
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 ?? new List<Channel>())
|
||||
{
|
||||
if (first)
|
||||
{
|
||||
first = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Append(',');
|
||||
}
|
||||
serializeChannel(ref sb, ref allChannels, ref allAccounts, subChannel);
|
||||
}
|
||||
if (theseAccounts != null)
|
||||
{
|
||||
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("]}");
|
||||
}
|
||||
private void serializeAccount(ref StringBuilder sb, Account currentAccount)
|
||||
{
|
||||
sb.Append($"{{\"text\": \"{currentAccount.DisplayName}\"}}");
|
||||
}
|
||||
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})}\\\">"
|
||||
+ currentUser.DisplayName +
|
||||
"</a>\", ");
|
||||
// \"{currentUser.DisplayName}\", ");
|
||||
var ownedAccounts = allAccounts.Where(a => a.IsUser == currentUser);
|
||||
sb.Append("nodes: [");
|
||||
sb.Append($"{{\"text\": \"owned accounts:\", \"expanded\":true, \"nodes\": [");
|
||||
if (ownedAccounts != null)
|
||||
{
|
||||
foreach (var acc in ownedAccounts)
|
||||
{
|
||||
serializeAccount(ref sb, acc);
|
||||
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 });
|
||||
}
|
||||
}
|
44
WebInterface/Controllers/api/ChannelsControler.cs
Normal file
44
WebInterface/Controllers/api/ChannelsControler.cs
Normal file
@ -0,0 +1,44 @@
|
||||
using System.Diagnostics;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using vassago.Models;
|
||||
|
||||
namespace vassago.Controllers.api;
|
||||
|
||||
[Route("api/[controller]")]
|
||||
[ApiController]
|
||||
public class ChannelsController : ControllerBase
|
||||
{
|
||||
private readonly ILogger<ChannelsController> _logger;
|
||||
private readonly ChattingContext _db;
|
||||
|
||||
public ChannelsController(ILogger<ChannelsController> logger, ChattingContext db)
|
||||
{
|
||||
_logger = logger;
|
||||
_db = db;
|
||||
}
|
||||
|
||||
[HttpGet("{id}")]
|
||||
[Produces("application/json")]
|
||||
public Channel Get(Guid id)
|
||||
{
|
||||
return _db.Find<Channel>(id);
|
||||
}
|
||||
|
||||
[HttpPatch]
|
||||
[Produces("application/json")]
|
||||
public IActionResult Patch([FromBody] Channel channel)
|
||||
{
|
||||
var fromDb = _db.Channels.Find(channel.Id);
|
||||
if (fromDb == null)
|
||||
{
|
||||
_logger.LogError($"attempt to update channel {channel.Id}, not found");
|
||||
return NotFound();
|
||||
}
|
||||
//settable values: lewdness filter level, meanness filter level. maybe i could decorate them...
|
||||
fromDb.LewdnessFilterLevel = channel.LewdnessFilterLevel;
|
||||
fromDb.MeannessFilterLevel = channel.MeannessFilterLevel;
|
||||
_db.SaveChanges();
|
||||
return Ok(fromDb);
|
||||
}
|
||||
}
|
113
WebInterface/Views/Channels/Details.cshtml
Normal file
113
WebInterface/Views/Channels/Details.cshtml
Normal file
@ -0,0 +1,113 @@
|
||||
@using System.ComponentModel
|
||||
@using Newtonsoft.Json
|
||||
@model Tuple<Channel, Enumerations.LewdnessFilterLevel, Enumerations.MeannessFilterLevel>
|
||||
@{
|
||||
var ThisChannel = Model.Item1;
|
||||
var IfInheritedLewdnessFilterLevel = Model.Item2;
|
||||
var IfInheritedMeannessFilterLevel = Model.Item3;
|
||||
}
|
||||
|
||||
|
||||
@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>@(ThisChannel.ChannelType != null ? 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>@(ThisChannel.SubChannels?.Count ?? 0)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">Users</th>
|
||||
<td>@(ThisChannel.Users?.Count ?? 0)</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;
|
||||
}
|
||||
</script>
|
||||
}
|
17
WebInterface/Views/Home/Index.cshtml
Normal file
17
WebInterface/Views/Home/Index.cshtml
Normal file
@ -0,0 +1,17 @@
|
||||
@{
|
||||
ViewData["Title"] = "Home Page";
|
||||
}
|
||||
<div id="tree"></div>
|
||||
tree above.
|
||||
|
||||
@section Scripts{
|
||||
<script type="text/javascript">
|
||||
function getTree() {
|
||||
var tree = @Html.Raw(ViewData["treeString"]);
|
||||
console.log(tree);
|
||||
return tree;
|
||||
}
|
||||
|
||||
$('#tree').bstreeview({ data: getTree() });
|
||||
</script>
|
||||
}
|
@ -5,6 +5,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>
|
||||
@ -16,6 +18,7 @@
|
||||
</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>
|
@ -12,5 +12,5 @@
|
||||
"TwitchConfigs": [
|
||||
],
|
||||
"exchangePairsLocation": "assets/exchangepairs.json",
|
||||
"DBConnectionString": "Host=localhost;Database=db;Username=db;Password=db"
|
||||
"DBConnectionString": "Host=azure.club;Database=db;Username=user;Password=password"
|
||||
}
|
||||
|
@ -266,7 +266,7 @@
|
||||
{"item1":"pc", "item2":"AU", "factor":206266.3},
|
||||
{"item1":"blue whale length", "item2": "m", "factor": 29.9},
|
||||
{"item1":"m", "item2": "ångström", "factor": 10000000000},
|
||||
{"item1":"smoot", "item2": "cm", "factor": 170},
|
||||
{"item1":"smoot", "item2": "ft", "factor": 5.583333333333},
|
||||
|
||||
{"item1":"floz", "item2":"mL", "factor":29.57344},
|
||||
{"item1":"L", "item2":"mL", "factor":1000},
|
||||
|
@ -3,11 +3,14 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<NoWarn>$(NoWarn);CA2254</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="bootstrap" Version="5.3.3" />
|
||||
<PackageReference Include="discord.net" Version="3.10.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="7.0.20" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.5">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
@ -18,6 +21,9 @@
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="7.0.4" />
|
||||
<PackageReference Include="QRCoder" Version="1.4.2" />
|
||||
<PackageReference Include="RestSharp" Version="110.2.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.Swagger" Version="6.6.2" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="6.6.2" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="6.6.2" />
|
||||
<PackageReference Include="TwitchLib" Version="3.5.3" />
|
||||
<PackageReference Include="youtubedlsharp" Version="0.3.1" />
|
||||
</ItemGroup>
|
||||
|
10
wwwroot/css/bstreeview.min.css
vendored
Normal file
10
wwwroot/css/bstreeview.min.css
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
/*
|
||||
@preserve
|
||||
bstreeview.css
|
||||
Version: 1.2.0
|
||||
Authors: Sami CHNITER <sami.chniter@gmail.com>
|
||||
Copyright 2020
|
||||
License: Apache License 2.0
|
||||
Project: https://github.com/nhmvienna/bs5treeview
|
||||
*/
|
||||
.bstreeview{position:relative;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;min-width:0;word-wrap:break-word;background-color:#fff;background-clip:border-box;border:1px solid rgba(0,0,0,.125);border-radius:.25rem;padding:0;overflow:hidden}.bstreeview .list-group{margin-bottom:0}.bstreeview .list-group-item{border-radius:0;border-width:1px 0 0 0;padding-top:.5rem;padding-bottom:.5rem;cursor:pointer}.bstreeview .list-group-item:hover{background-color:#dee2e6}.bstreeview>.list-group-item:first-child{border-top-width:0}.bstreeview .state-icon{margin-right:8px}.bstreeview .item-icon{margin-right:5px}
|
9
wwwroot/css/fontawesome.min.css
vendored
Normal file
9
wwwroot/css/fontawesome.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
10
wwwroot/js/bstreeview.min.js
vendored
Normal file
10
wwwroot/js/bstreeview.min.js
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
/*
|
||||
@preserve
|
||||
bstreeview.js
|
||||
Version: 1.2.0
|
||||
Authors: Sami CHNITER <sami.chniter@gmail.com>
|
||||
Copyright 2020
|
||||
License: Apache License 2.0
|
||||
Project:https://github.com/nhmvienna/bs5treeview
|
||||
*/
|
||||
!function (t, e, i, s) { "use strict"; var n = { expandIcon: "fa fa-angle-down fa-fw", collapseIcon: "fa fa-angle-right fa-fw", expandClass: 'show', indent: 1.25, parentsMarginLeft: "1.25rem", openNodeLinkOnNewTab: !0 }, a = '<div role="treeitem" class="list-group-item" data-bs-toggle="collapse"></div>', d = '<div role="group" class="list-group collapse" id="itemid"></div>', o = '<i class="state-icon"></i>', r = '<i class="item-icon"></i>'; function l(e, i) { this.element = e, this.itemIdPrefix = e.id + "-item-", this.settings = t.extend({}, n, i), this.init() } t.extend(l.prototype, { init: function () { this.tree = [], this.nodes = [], this.settings.data && (this.settings.data.isPrototypeOf(String) && (this.settings.data = t.parseJSON(this.settings.data)), this.tree = t.extend(!0, [], this.settings.data), delete this.settings.data), t(this.element).addClass("bstreeview"), this.initData({ nodes: this.tree }); var i = this; this.build(t(this.element), this.tree, 0), t(this.element).on("click", ".list-group-item", function (s) { t(".state-icon", this).toggleClass(i.settings.expandIcon).toggleClass(i.settings.collapseIcon), s.target.hasAttribute("href") && (i.settings.openNodeLinkOnNewTab ? e.open(s.target.getAttribute("href"), "_blank") : e.location = s.target.getAttribute("href")) }) }, initData: function (e) { if (e.nodes) { var i = e, s = this; t.each(e.nodes, function (t, e) { e.nodeId = s.nodes.length, e.parentId = i.nodeId, s.nodes.push(e), e.nodes && s.initData(e) }) } }, build: function (e, i, s) { var n = this, l = n.settings.parentsMarginLeft; s > 0 && (l = (n.settings.indent + s * n.settings.indent).toString() + "rem;"), s += 1, t.each(i, function (i, g) { var h = t(a).attr("data-bs-target", "#" + n.itemIdPrefix + g.nodeId).attr("style", "padding-left:" + l).attr("aria-level", s); if (g.nodes) { var c = t(o).addClass((g.expanded)?n.settings.expandIcon:n.settings.collapseIcon); h.append(c) } if (g.icon) { var f = t(r).addClass(g.icon); h.append(f) } if (h.append(g.text), g.href && h.attr("href", g.href), g.class && h.addClass(g.class), g.id && h.attr("id", g.id), e.append(h), g.nodes) { var p = t(d).attr("id", n.itemIdPrefix + g.nodeId); e.append(p), n.build(p, g.nodes, s); if (g.expanded) p.addClass(n.settings.expandClass) } }) } }), t.fn.bstreeview = function (e) { return this.each(function () { t.data(this, "plugin_bstreeview") || t.data(this, "plugin_bstreeview", new l(this, e)) }) } }(jQuery, window, document);
|
@ -2,3 +2,49 @@
|
||||
// for details on configuring this project to bundle and minify static web assets.
|
||||
|
||||
// Write your JavaScript code.
|
||||
|
||||
function testfunct(caller){
|
||||
console.log("[gibberish]");
|
||||
console.log(caller);
|
||||
}
|
||||
function patchModel(model)
|
||||
{
|
||||
//structure the model your (dang) self into a nice object
|
||||
console.log(model);
|
||||
//i know the page url.
|
||||
console.log(window.location.pathname);
|
||||
var components = window.location.pathname.split('/');
|
||||
if(components[2] !== "Details")
|
||||
{
|
||||
console.log("wtf are you doing? " + components[2] + " is something other than Details")
|
||||
}
|
||||
var type=components[1];
|
||||
var id=components[3];
|
||||
|
||||
//todo: figure out what the URL actually needs to be, rather than assuming you get a whole-ass server to yourself.
|
||||
//you selfish fuck. What are you, fox?
|
||||
var apiUrl = "/api/Channels/"
|
||||
|
||||
console.log("dexter impression: I am now ready to post the following content:");
|
||||
console.log(JSON.stringify(model));
|
||||
fetch(apiUrl, {
|
||||
method: 'PATCH',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(model),
|
||||
})
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error('Network response was not "ok". which is not ok.');
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(returnedSuccessdata => {
|
||||
// perhaps a success callback
|
||||
console.log('returnedSuccessdata:', returnedSuccessdata);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
});
|
||||
}
|
BIN
wwwroot/webfonts/fa-brands-400.ttf
Normal file
BIN
wwwroot/webfonts/fa-brands-400.ttf
Normal file
Binary file not shown.
BIN
wwwroot/webfonts/fa-brands-400.woff2
Normal file
BIN
wwwroot/webfonts/fa-brands-400.woff2
Normal file
Binary file not shown.
BIN
wwwroot/webfonts/fa-regular-400.ttf
Normal file
BIN
wwwroot/webfonts/fa-regular-400.ttf
Normal file
Binary file not shown.
BIN
wwwroot/webfonts/fa-regular-400.woff2
Normal file
BIN
wwwroot/webfonts/fa-regular-400.woff2
Normal file
Binary file not shown.
BIN
wwwroot/webfonts/fa-solid-900.ttf
Normal file
BIN
wwwroot/webfonts/fa-solid-900.ttf
Normal file
Binary file not shown.
BIN
wwwroot/webfonts/fa-solid-900.woff2
Normal file
BIN
wwwroot/webfonts/fa-solid-900.woff2
Normal file
Binary file not shown.
BIN
wwwroot/webfonts/fa-v4compatibility.ttf
Normal file
BIN
wwwroot/webfonts/fa-v4compatibility.ttf
Normal file
Binary file not shown.
BIN
wwwroot/webfonts/fa-v4compatibility.woff2
Normal file
BIN
wwwroot/webfonts/fa-v4compatibility.woff2
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user