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)
|
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 false;
|
||||||
return Regex.IsMatch(message.Content, $"{Trigger}\\b", RegexOptions.IgnoreCase);
|
return Regex.IsMatch(message.Content, $"{Trigger}\\b", RegexOptions.IgnoreCase);
|
||||||
}
|
}
|
||||||
|
@ -26,10 +26,11 @@ public class Detiktokify : Behavior
|
|||||||
}
|
}
|
||||||
public override bool ShouldAct(Message message)
|
public override bool ShouldAct(Message message)
|
||||||
{
|
{
|
||||||
if(message.Channel.EffectivePermissions.MaxAttachmentBytes == 0)
|
|
||||||
|
if(Behaver.Instance.IsSelf(message.Author.Id))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if(Behaver.Instance.Selves.Any(acc => acc.Id == message.Author.Id))
|
if(message.Channel.EffectivePermissions.MaxAttachmentBytes == 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
var wordLikes = message.Content.Split(' ', StringSplitOptions.TrimEntries);
|
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));
|
Console.Error.WriteLine("tried to dl, failed. \n" + string.Join('\n', res.ErrorOutput));
|
||||||
await message.React("problemon");
|
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
|
else
|
||||||
{
|
{
|
||||||
|
@ -21,8 +21,9 @@ public class FiximageHeic : Behavior
|
|||||||
private List<Attachment> heics = new List<Attachment>();
|
private List<Attachment> heics = new List<Attachment>();
|
||||||
public override bool ShouldAct(Message message)
|
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 false;
|
||||||
|
|
||||||
if (message.Attachments?.Count() > 0)
|
if (message.Attachments?.Count() > 0)
|
||||||
{
|
{
|
||||||
foreach (var att in message.Attachments)
|
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 string Trigger => "certain tech buzzwords that no human uses in normal conversation";
|
||||||
public override bool ShouldAct(Message message)
|
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 false;
|
||||||
|
|
||||||
if(message.Channel.EffectivePermissions.ReactionsPossible)
|
if(message.Channel.EffectivePermissions.ReactionsPossible)
|
||||||
|
@ -20,10 +20,15 @@ public class GeneralSnarkGooglit : Behavior
|
|||||||
|
|
||||||
public override bool ShouldAct(Message message)
|
public override bool ShouldAct(Message message)
|
||||||
{
|
{
|
||||||
if(Behaver.Instance.Selves.Any(acc => acc.Id == message.Author.Id))
|
return false;
|
||||||
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)
|
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)
|
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 false;
|
||||||
|
|
||||||
if((MeannessFilterLevel)message.Channel.EffectivePermissions.MeannessFilterLevel < MeannessFilterLevel.Medium ||
|
if((MeannessFilterLevel)message.Channel.EffectivePermissions.MeannessFilterLevel < MeannessFilterLevel.Medium ||
|
||||||
|
@ -19,6 +19,10 @@ public class GeneralSnarkSkynet : Behavior
|
|||||||
|
|
||||||
public override async Task<bool> ActOn(Message message)
|
public override async Task<bool> ActOn(Message message)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
if(Behaver.Instance.IsSelf(message.Author.Id))
|
||||||
|
return false;
|
||||||
|
|
||||||
switch (Shared.r.Next(5))
|
switch (Shared.r.Next(5))
|
||||||
{
|
{
|
||||||
default:
|
default:
|
||||||
|
@ -18,8 +18,9 @@ public class Gratitude : Behavior
|
|||||||
|
|
||||||
public override bool ShouldAct(Message message)
|
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 false;
|
||||||
|
|
||||||
return Regex.IsMatch(message.Content, "\\bthank (yo)?u\\b", RegexOptions.IgnoreCase) && message.MentionsMe;
|
return Regex.IsMatch(message.Content, "\\bthank (yo)?u\\b", RegexOptions.IgnoreCase) && message.MentionsMe;
|
||||||
}
|
}
|
||||||
public override async Task<bool> ActOn(Message message)
|
public override async Task<bool> ActOn(Message message)
|
||||||
|
@ -61,7 +61,7 @@ public class LaughAtOwnJoke : Behavior
|
|||||||
public override string Trigger => "1 in 8";
|
public override string Trigger => "1 in 8";
|
||||||
|
|
||||||
public override string Description => Name;
|
public override string Description => Name;
|
||||||
private string _punchline{get;set;}
|
private string _punchline { get; set; }
|
||||||
|
|
||||||
public LaughAtOwnJoke(string punchline)
|
public LaughAtOwnJoke(string punchline)
|
||||||
{
|
{
|
||||||
@ -69,9 +69,12 @@ public class LaughAtOwnJoke : Behavior
|
|||||||
}
|
}
|
||||||
public override bool ShouldAct(Message message)
|
public override bool ShouldAct(Message message)
|
||||||
{
|
{
|
||||||
|
if(Behaver.Instance.IsSelf(message.Author.Id))
|
||||||
|
return false;
|
||||||
|
|
||||||
Console.WriteLine($"{message.Content} == {_punchline}");
|
Console.WriteLine($"{message.Content} == {_punchline}");
|
||||||
return 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)
|
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";
|
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 string _pw;
|
||||||
private Account _primary;
|
private Account _primary;
|
||||||
|
|
||||||
public LinkClose(string pw, Account primary)
|
public LinkClose(string pw, Account primary)
|
||||||
{
|
{
|
||||||
_db = new ChattingContext();
|
|
||||||
_pw = pw;
|
_pw = pw;
|
||||||
_primary = primary;
|
_primary = primary;
|
||||||
}
|
}
|
||||||
@ -59,6 +57,9 @@ public class LinkClose : Behavior
|
|||||||
|
|
||||||
public override async Task<bool> ActOn(Message message)
|
public override async Task<bool> ActOn(Message message)
|
||||||
{
|
{
|
||||||
|
if(Behaver.Instance.IsSelf(message.Author.Id))
|
||||||
|
return false;
|
||||||
|
|
||||||
var secondary = message.Author.IsUser;
|
var secondary = message.Author.IsUser;
|
||||||
if(_primary.IsUser.Id == secondary.Id)
|
if(_primary.IsUser.Id == secondary.Id)
|
||||||
{
|
{
|
||||||
@ -71,44 +72,14 @@ public class LinkClose : Behavior
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Console.WriteLine($"{secondary.Id} is being consumed into {_primary.IsUser.Id}");
|
if(Behaver.Instance.CollapseUsers(_primary.IsUser, secondary, new ChattingContext()))
|
||||||
_primary.IsUser.Accounts.AddRange(secondary.Accounts);
|
|
||||||
foreach(var a in secondary.Accounts)
|
|
||||||
{
|
{
|
||||||
a.IsUser = _primary.IsUser;
|
await message.Channel.SendMessage("done :)");
|
||||||
}
|
}
|
||||||
secondary.Accounts.Clear();
|
else
|
||||||
Console.WriteLine("accounts transferred");
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
await _db.SaveChangesAsync();
|
await message.Channel.SendMessage("failed :(");
|
||||||
}
|
}
|
||||||
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 :)");
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -15,12 +15,13 @@ public class PepTalk : Behavior
|
|||||||
{
|
{
|
||||||
public override string Name => "PepTalk";
|
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 string Description => "assembles a pep talk from a few pieces";
|
||||||
|
|
||||||
public override async Task<bool> ActOn(Message message)
|
public override async Task<bool> ActOn(Message message)
|
||||||
{var piece1 = new List<string>{
|
{
|
||||||
|
var piece1 = new List<string>{
|
||||||
"Champ, ",
|
"Champ, ",
|
||||||
"Fact: ",
|
"Fact: ",
|
||||||
"Everybody says ",
|
"Everybody says ",
|
||||||
|
@ -42,10 +42,10 @@ public class QRify : Behavior
|
|||||||
File.WriteAllText($"tmp/qr{todaysnumber}.svg", qrCodeAsSvg);
|
File.WriteAllText($"tmp/qr{todaysnumber}.svg", qrCodeAsSvg);
|
||||||
if (ExternalProcess.GoPlz("convert", $"tmp/qr{todaysnumber}.svg tmp/qr{todaysnumber}.png"))
|
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);
|
await message.Channel.SendFile($"tmp/qr{todaysnumber}.png", null);
|
||||||
else
|
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}.svg");
|
||||||
File.Delete($"tmp/qr{todaysnumber}.png");
|
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
|
//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?
|
//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)
|
public override async Task<bool> ActOn(Message message)
|
||||||
{
|
{
|
||||||
var ti = ProtocolInterfaces.ProtocolList.twitchs.FirstOrDefault();
|
var ti = ProtocolInterfaces.ProtocolList.twitchs.FirstOrDefault();
|
||||||
|
@ -22,8 +22,7 @@ namespace vassago
|
|||||||
public async Task StartAsync(CancellationToken cancellationToken)
|
public async Task StartAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var dbc = new ChattingContext();
|
var dbc = new ChattingContext();
|
||||||
dbc.Database.EnsureCreated();
|
await dbc.Database.MigrateAsync();
|
||||||
dbc.Database.Migrate();
|
|
||||||
|
|
||||||
if (DiscordTokens?.Any() ?? false)
|
if (DiscordTokens?.Any() ?? false)
|
||||||
foreach (var dt in DiscordTokens)
|
foreach (var dt in DiscordTokens)
|
||||||
@ -40,11 +39,12 @@ namespace vassago
|
|||||||
await t.Init(tc);
|
await t.Init(tc);
|
||||||
ProtocolInterfaces.ProtocolList.twitchs.Add(t);
|
ProtocolInterfaces.ProtocolList.twitchs.Add(t);
|
||||||
}
|
}
|
||||||
|
Console.WriteLine("survived initting");
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task StopAsync(CancellationToken cancellationToken)
|
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)
|
if(currencyConf != null)
|
||||||
{
|
{
|
||||||
knownConversions.RemoveAll(kc => kc.Item1 == currencyConf.Base);
|
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))
|
if (File.Exists(currencyPath))
|
||||||
{
|
{
|
||||||
currencyConf = JsonConvert.DeserializeObject<ExchangePairs>(File.ReadAllText(currencyPath));
|
currencyConf = JsonConvert.DeserializeObject<ExchangePairs>(File.ReadAllText(currencyPath));
|
||||||
|
|
||||||
knownAliases.Add(new List<string>() { currencyConf.Base.ToLower() }, currencyConf.Base);
|
if(!knownAliases.ContainsValue(currencyConf.Base))
|
||||||
|
{
|
||||||
|
knownAliases.Add(new List<string>() { currencyConf.Base.ToLower() }, currencyConf.Base);
|
||||||
|
}
|
||||||
foreach (var rate in currencyConf.rates)
|
foreach (var rate in currencyConf.rates)
|
||||||
{
|
{
|
||||||
knownAliases.Add(new List<string>() { rate.Key.ToLower() }, rate.Key);
|
if(!knownAliases.ContainsValue(rate.Key))
|
||||||
|
{
|
||||||
|
knownAliases.Add(new List<string>() { rate.Key.ToLower() }, rate.Key);
|
||||||
|
}
|
||||||
AddLinearPair(currencyConf.Base, rate.Key, rate.Value);
|
AddLinearPair(currencyConf.Base, rate.Key, rate.Value);
|
||||||
Console.WriteLine($"{rate.Key.ToLower()} alias of {rate.Key}");
|
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/*'
|
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")
|
b.Property<string>("ExternalId")
|
||||||
.HasColumnType("text");
|
.HasColumnType("text");
|
||||||
|
|
||||||
b.Property<Guid?>("FeaturePermissionId")
|
|
||||||
.HasColumnType("uuid");
|
|
||||||
|
|
||||||
b.Property<bool>("IsBot")
|
b.Property<bool>("IsBot")
|
||||||
.HasColumnType("boolean");
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
@ -54,8 +51,6 @@ namespace vassago.Migrations
|
|||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
|
||||||
b.HasIndex("FeaturePermissionId");
|
|
||||||
|
|
||||||
b.HasIndex("IsUserId");
|
b.HasIndex("IsUserId");
|
||||||
|
|
||||||
b.HasIndex("SeenInChannelId");
|
b.HasIndex("SeenInChannelId");
|
||||||
@ -115,37 +110,6 @@ namespace vassago.Migrations
|
|||||||
b.Property<string>("ExternalId")
|
b.Property<string>("ExternalId")
|
||||||
.HasColumnType("text");
|
.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")
|
b.Property<int?>("LewdnessFilterLevel")
|
||||||
.HasColumnType("integer");
|
.HasColumnType("integer");
|
||||||
|
|
||||||
@ -161,32 +125,20 @@ namespace vassago.Migrations
|
|||||||
b.Property<int?>("MeannessFilterLevel")
|
b.Property<int?>("MeannessFilterLevel")
|
||||||
.HasColumnType("integer");
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<Guid?>("ParentChannelId")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<string>("Protocol")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
b.Property<bool?>("ReactionsPossible")
|
b.Property<bool?>("ReactionsPossible")
|
||||||
.HasColumnType("boolean");
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
|
||||||
b.ToTable("ChannelPermissions");
|
b.HasIndex("ParentChannelId");
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("vassago.Models.FeaturePermission", b =>
|
b.ToTable("Channels");
|
||||||
{
|
|
||||||
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 =>
|
modelBuilder.Entity("vassago.Models.Message", b =>
|
||||||
@ -234,22 +186,13 @@ namespace vassago.Migrations
|
|||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
.HasColumnType("uuid");
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
b.Property<Guid?>("FeaturePermissionId")
|
|
||||||
.HasColumnType("uuid");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
|
||||||
b.HasIndex("FeaturePermissionId");
|
|
||||||
|
|
||||||
b.ToTable("Users");
|
b.ToTable("Users");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("vassago.Models.Account", b =>
|
modelBuilder.Entity("vassago.Models.Account", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("vassago.Models.FeaturePermission", null)
|
|
||||||
.WithMany("RestrictedToAccounts")
|
|
||||||
.HasForeignKey("FeaturePermissionId");
|
|
||||||
|
|
||||||
b.HasOne("vassago.Models.User", "IsUser")
|
b.HasOne("vassago.Models.User", "IsUser")
|
||||||
.WithMany("Accounts")
|
.WithMany("Accounts")
|
||||||
.HasForeignKey("IsUserId");
|
.HasForeignKey("IsUserId");
|
||||||
@ -274,21 +217,11 @@ namespace vassago.Migrations
|
|||||||
|
|
||||||
modelBuilder.Entity("vassago.Models.Channel", b =>
|
modelBuilder.Entity("vassago.Models.Channel", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("vassago.Models.FeaturePermission", null)
|
|
||||||
.WithMany("RestrictedToChannels")
|
|
||||||
.HasForeignKey("FeaturePermissionId");
|
|
||||||
|
|
||||||
b.HasOne("vassago.Models.Channel", "ParentChannel")
|
b.HasOne("vassago.Models.Channel", "ParentChannel")
|
||||||
.WithMany("SubChannels")
|
.WithMany("SubChannels")
|
||||||
.HasForeignKey("ParentChannelId");
|
.HasForeignKey("ParentChannelId");
|
||||||
|
|
||||||
b.HasOne("vassago.Models.ChannelPermissions", "Permissions")
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey("PermissionsId");
|
|
||||||
|
|
||||||
b.Navigation("ParentChannel");
|
b.Navigation("ParentChannel");
|
||||||
|
|
||||||
b.Navigation("Permissions");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("vassago.Models.Message", b =>
|
modelBuilder.Entity("vassago.Models.Message", b =>
|
||||||
@ -306,13 +239,6 @@ namespace vassago.Migrations
|
|||||||
b.Navigation("Channel");
|
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 =>
|
modelBuilder.Entity("vassago.Models.Channel", b =>
|
||||||
{
|
{
|
||||||
b.Navigation("Messages");
|
b.Navigation("Messages");
|
||||||
@ -322,15 +248,6 @@ namespace vassago.Migrations
|
|||||||
b.Navigation("Users");
|
b.Navigation("Users");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("vassago.Models.FeaturePermission", b =>
|
|
||||||
{
|
|
||||||
b.Navigation("RestrictedToAccounts");
|
|
||||||
|
|
||||||
b.Navigation("RestrictedToChannels");
|
|
||||||
|
|
||||||
b.Navigation("RestrictedToUsers");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("vassago.Models.Message", b =>
|
modelBuilder.Entity("vassago.Models.Message", b =>
|
||||||
{
|
{
|
||||||
b.Navigation("Attachments");
|
b.Navigation("Attachments");
|
||||||
|
@ -5,6 +5,7 @@ using System.Collections.Generic;
|
|||||||
using System.ComponentModel.DataAnnotations.Schema;
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Components.Web;
|
||||||
using static vassago.Models.Enumerations;
|
using static vassago.Models.Enumerations;
|
||||||
|
|
||||||
public class Channel
|
public class Channel
|
||||||
@ -13,14 +14,20 @@ public class Channel
|
|||||||
public Guid Id { get; set; }
|
public Guid Id { get; set; }
|
||||||
public string ExternalId { get; set; }
|
public string ExternalId { get; set; }
|
||||||
public string DisplayName { get; set; }
|
public string DisplayName { get; set; }
|
||||||
public ChannelPermissions Permissions { get; set; }
|
|
||||||
public List<Channel> SubChannels { get; set; }
|
public List<Channel> SubChannels { get; set; }
|
||||||
public Channel ParentChannel { get; set; }
|
public Channel ParentChannel { get; set; }
|
||||||
public string Protocol { get; set; }
|
public string Protocol { get; set; }
|
||||||
public List<Message> Messages { get; set; }
|
public List<Message> Messages { get; set; }
|
||||||
public List<Account> Users { get; set; }
|
public List<Account> Users { get; set; }
|
||||||
public ChannelType ChannelType {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]
|
[NonSerialized]
|
||||||
public Func<string, string, Task> SendFile;
|
public Func<string, string, Task> SendFile;
|
||||||
@ -33,33 +40,28 @@ public class Channel
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
ChannelPermissions toReturn = Permissions ?? new ChannelPermissions();
|
var path = new Stack<Channel>(); //omg i actually get to use a data structure from university
|
||||||
return GetEffectivePermissions(ref toReturn).Definite();
|
var walker = this;
|
||||||
}
|
path.Push(this);
|
||||||
}
|
while(walker.ParentChannel != null)
|
||||||
private ChannelPermissions GetEffectivePermissions(ref ChannelPermissions settings)
|
{
|
||||||
{
|
walker = walker.ParentChannel;
|
||||||
if(settings == null) throw new ArgumentNullException();
|
path.Push(walker);
|
||||||
settings.LewdnessFilterLevel = settings.LewdnessFilterLevel ?? Permissions?.LewdnessFilterLevel;
|
}
|
||||||
settings.MeannessFilterLevel = settings.MeannessFilterLevel ?? Permissions?.MeannessFilterLevel;
|
DefinitePermissionSettings toReturn = new DefinitePermissionSettings();
|
||||||
settings.LinksAllowed = settings.LinksAllowed ?? Permissions?.LinksAllowed;
|
|
||||||
settings.MaxAttachmentBytes = settings.MaxAttachmentBytes ?? Permissions?.MaxAttachmentBytes;
|
|
||||||
settings.MaxTextChars = settings.MaxTextChars ?? Permissions?.MaxTextChars;
|
|
||||||
settings.ReactionsPossible = settings.ReactionsPossible ?? Permissions?.ReactionsPossible;
|
|
||||||
|
|
||||||
if(this.ParentChannel != null &&
|
while(path.Count > 0)
|
||||||
(settings.LewdnessFilterLevel == null ||
|
{
|
||||||
settings.MeannessFilterLevel == null ||
|
walker = path.Pop();
|
||||||
settings.LinksAllowed == null ||
|
toReturn.LewdnessFilterLevel = walker.LewdnessFilterLevel ?? toReturn.LewdnessFilterLevel;
|
||||||
settings.MaxAttachmentBytes == null ||
|
toReturn.MeannessFilterLevel = walker.MeannessFilterLevel ?? toReturn.MeannessFilterLevel;
|
||||||
settings.MaxTextChars == null ||
|
toReturn.LinksAllowed = walker.LinksAllowed ?? toReturn.LinksAllowed;
|
||||||
settings.ReactionsPossible == null))
|
toReturn.MaxAttachmentBytes = walker.MaxAttachmentBytes ?? toReturn.MaxAttachmentBytes;
|
||||||
{
|
toReturn.MaxTextChars = walker.MaxTextChars ?? toReturn.MaxTextChars;
|
||||||
return this.ParentChannel.GetEffectivePermissions(ref settings);
|
toReturn.ReactionsPossible = walker.ReactionsPossible ?? toReturn.ReactionsPossible;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
return toReturn;
|
||||||
return settings;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public string LineageSummary
|
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<Channel> Channels { get; set; }
|
||||||
//public DbSet<Emoji> Emoji {get;set;}
|
//public DbSet<Emoji> Emoji {get;set;}
|
||||||
public DbSet<Message> Messages { 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<Account> Accounts { get; set; }
|
||||||
public DbSet<User> Users { get; set; }
|
public DbSet<User> Users { get; set; }
|
||||||
|
|
||||||
public ChattingContext(DbContextOptions<ChattingContext> options) : base(options) { }
|
public ChattingContext(DbContextOptions<ChattingContext> options) : base(options) { }
|
||||||
public ChattingContext() : base() { }
|
public ChattingContext() : base() { }
|
||||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
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.
|
.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();
|
Type type = enumerationValue.GetType();
|
||||||
if (!type.IsEnum)
|
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
|
//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.EntityFrameworkCore;
|
||||||
|
using Microsoft.AspNetCore.Mvc.NewtonsoftJson;
|
||||||
using vassago.Models;
|
using vassago.Models;
|
||||||
|
|
||||||
|
#pragma warning disable CA2254
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
// Add services to the container.
|
// Add services to the container.
|
||||||
builder.Services.AddControllersWithViews();
|
builder.Services.AddControllersWithViews();
|
||||||
builder.Services.AddSingleton<IHostedService, vassago.ConsoleService>();
|
builder.Services.AddSingleton<IHostedService, vassago.ConsoleService>();
|
||||||
builder.Services.AddDbContext<ChattingContext>(options =>
|
builder.Services.AddDbContext<ChattingContext>();
|
||||||
options.UseNpgsql(builder.Configuration.GetConnectionString("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();
|
var app = builder.Build();
|
||||||
|
|
||||||
@ -24,4 +35,19 @@ app.MapControllerRoute(
|
|||||||
name: "default",
|
name: "default",
|
||||||
pattern: "{controller=Home}/{action=Index}/{id?}");
|
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();
|
app.Run();
|
||||||
|
@ -58,15 +58,12 @@ public class DiscordInterface
|
|||||||
protocolAsChannel = new Channel()
|
protocolAsChannel = new Channel()
|
||||||
{
|
{
|
||||||
DisplayName = "discord (itself)",
|
DisplayName = "discord (itself)",
|
||||||
Permissions = new Models.ChannelPermissions()
|
MeannessFilterLevel = Enumerations.MeannessFilterLevel.Strict,
|
||||||
{
|
LewdnessFilterLevel = Enumerations.LewdnessFilterLevel.Moderate,
|
||||||
MeannessFilterLevel = Enumerations.MeannessFilterLevel.Strict,
|
MaxTextChars = 2000,
|
||||||
LewdnessFilterLevel = Enumerations.LewdnessFilterLevel.Moderate,
|
MaxAttachmentBytes = 25 * 1024 * 1024, //allegedly it's 25, but I worry it's not actually.
|
||||||
MaxTextChars = 2000,
|
LinksAllowed = true,
|
||||||
MaxAttachmentBytes = 25 * 1024 * 1024, //allegedly it's 25, but I worry it's not actually.
|
ReactionsPossible = true,
|
||||||
LinksAllowed = true,
|
|
||||||
ReactionsPossible = true
|
|
||||||
},
|
|
||||||
ExternalId = null,
|
ExternalId = null,
|
||||||
Protocol = PROTOCOL,
|
Protocol = PROTOCOL,
|
||||||
SubChannels = new List<Channel>()
|
SubChannels = new List<Channel>()
|
||||||
@ -117,11 +114,11 @@ public class DiscordInterface
|
|||||||
|
|
||||||
private async Task SelfConnected()
|
private async Task SelfConnected()
|
||||||
{
|
{
|
||||||
var selfUser = UpsertAccount(client.CurrentUser, protocolAsChannel.Id);
|
var selfAccount = UpsertAccount(client.CurrentUser, protocolAsChannel);
|
||||||
selfUser.DisplayName = client.CurrentUser.Username;
|
selfAccount.DisplayName = client.CurrentUser.Username;
|
||||||
|
|
||||||
await _db.SaveChangesAsync();
|
await _db.SaveChangesAsync();
|
||||||
Behaver.Instance.Selves.Add(selfUser);
|
|
||||||
|
Behaver.Instance.MarkSelf(selfAccount);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task MessageReceived(SocketMessage messageParam)
|
private async Task MessageReceived(SocketMessage messageParam)
|
||||||
@ -153,7 +150,7 @@ public class DiscordInterface
|
|||||||
var guild = UpsertChannel(arg.Guild);
|
var guild = UpsertChannel(arg.Guild);
|
||||||
var defaultChannel = UpsertChannel(arg.Guild.DefaultChannel);
|
var defaultChannel = UpsertChannel(arg.Guild.DefaultChannel);
|
||||||
defaultChannel.ParentChannel = guild;
|
defaultChannel.ParentChannel = guild;
|
||||||
var u = UpsertAccount(arg, guild.Id);
|
var u = UpsertAccount(arg, guild);
|
||||||
u.DisplayName = arg.DisplayName;
|
u.DisplayName = arg.DisplayName;
|
||||||
}
|
}
|
||||||
private async Task ButtonHandler(SocketMessageComponent component)
|
private async Task ButtonHandler(SocketMessageComponent component)
|
||||||
@ -229,8 +226,7 @@ public class DiscordInterface
|
|||||||
m.ExternalId = dMessage.Id.ToString();
|
m.ExternalId = dMessage.Id.ToString();
|
||||||
m.Timestamp = dMessage.EditedTimestamp ?? dMessage.CreatedAt;
|
m.Timestamp = dMessage.EditedTimestamp ?? dMessage.CreatedAt;
|
||||||
m.Channel = UpsertChannel(dMessage.Channel);
|
m.Channel = UpsertChannel(dMessage.Channel);
|
||||||
m.Author = UpsertAccount(dMessage.Author, m.Channel.Id);
|
m.Author = UpsertAccount(dMessage.Author, m.Channel);
|
||||||
m.Author.SeenInChannel = m.Channel;
|
|
||||||
if(dMessage.Channel is IGuildChannel)
|
if(dMessage.Channel is IGuildChannel)
|
||||||
{
|
{
|
||||||
m.Author.DisplayName = (dMessage.Author as IGuildUser).DisplayName;//discord forgot how display names work.
|
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.Protocol = protocolAsChannel.Protocol;
|
||||||
c.ParentChannel = protocolAsChannel;
|
c.ParentChannel = protocolAsChannel;
|
||||||
c.SubChannels = c.SubChannels ?? new List<Channel>();
|
c.SubChannels = c.SubChannels ?? new List<Channel>();
|
||||||
c.Permissions = c.Permissions ?? new Models.ChannelPermissions();
|
c.MaxAttachmentBytes = channel.MaxUploadLimit;
|
||||||
c.Permissions.MaxAttachmentBytes = channel.MaxUploadLimit;
|
|
||||||
|
|
||||||
c.SendMessage = (t) => { throw new InvalidOperationException($"channel {channel.Name} is guild; cannot accept text"); };
|
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"); };
|
c.SendFile = (f, t) => { throw new InvalidOperationException($"channel {channel.Name} is guild; send file"); };
|
||||||
return c;
|
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)
|
if (acc == null)
|
||||||
{
|
{
|
||||||
acc = new Account();
|
acc = new Account();
|
||||||
@ -317,6 +312,7 @@ public class DiscordInterface
|
|||||||
acc.ExternalId = user.Id.ToString();
|
acc.ExternalId = user.Id.ToString();
|
||||||
acc.IsBot = user.IsBot || user.IsWebhook;
|
acc.IsBot = user.IsBot || user.IsWebhook;
|
||||||
acc.Protocol = PROTOCOL;
|
acc.Protocol = PROTOCOL;
|
||||||
|
acc.SeenInChannel = inChannel;
|
||||||
|
|
||||||
acc.IsUser = _db.Users.FirstOrDefault(u => u.Accounts.Any(a => a.ExternalId == acc.ExternalId && a.Protocol == acc.Protocol));
|
acc.IsUser = _db.Users.FirstOrDefault(u => u.Accounts.Any(a => a.ExternalId == acc.ExternalId && a.Protocol == acc.Protocol));
|
||||||
if(acc.IsUser == null)
|
if(acc.IsUser == null)
|
||||||
@ -332,13 +328,11 @@ public class DiscordInterface
|
|||||||
var c = _db.Channels.FirstOrDefault(c => c.ExternalId == msg.Channel.Id.ToString());
|
var c = _db.Channels.FirstOrDefault(c => c.ExternalId == msg.Channel.Id.ToString());
|
||||||
//var preferredEmote = c.EmoteOverrides?[e] ?? e; //TODO: emote overrides
|
//var preferredEmote = c.EmoteOverrides?[e] ?? e; //TODO: emote overrides
|
||||||
var preferredEmote = e;
|
var preferredEmote = e;
|
||||||
Emoji emoji;
|
if (Emoji.TryParse(preferredEmote, out Emoji emoji))
|
||||||
if (Emoji.TryParse(preferredEmote, out emoji))
|
|
||||||
{
|
{
|
||||||
return msg.AddReactionAsync(emoji);
|
return msg.AddReactionAsync(emoji);
|
||||||
}
|
}
|
||||||
Emote emote;
|
if (!Emote.TryParse(preferredEmote, out Emote emote))
|
||||||
if (!Emote.TryParse(preferredEmote, out emote))
|
|
||||||
{
|
{
|
||||||
if (preferredEmote == e)
|
if (preferredEmote == e)
|
||||||
Console.Error.WriteLine($"never heard of emote {e}");
|
Console.Error.WriteLine($"never heard of emote {e}");
|
||||||
|
@ -9,7 +9,6 @@ using Discord.Net;
|
|||||||
|
|
||||||
namespace vassago.DiscordInterface
|
namespace vassago.DiscordInterface
|
||||||
{
|
{
|
||||||
|
|
||||||
public static class SlashCommandsHelper
|
public static class SlashCommandsHelper
|
||||||
{
|
{
|
||||||
private static List<CommandSetup> slashCommands = new List<CommandSetup>()
|
private static List<CommandSetup> slashCommands = new List<CommandSetup>()
|
||||||
|
@ -2,6 +2,6 @@ namespace vassago.ProtocolInterfaces;
|
|||||||
|
|
||||||
public static class ProtocolList
|
public static class ProtocolList
|
||||||
{
|
{
|
||||||
public static List<DiscordInterface.DiscordInterface> discords = new List<DiscordInterface.DiscordInterface>();
|
public static List<DiscordInterface.DiscordInterface> discords = new();
|
||||||
public static List<TwitchInterface.TwitchInterface> twitchs = new List<TwitchInterface.TwitchInterface>();
|
public static List<TwitchInterface.TwitchInterface> twitchs = new();
|
||||||
}
|
}
|
@ -39,15 +39,12 @@ public class TwitchInterface
|
|||||||
protocolAsChannel = new Channel()
|
protocolAsChannel = new Channel()
|
||||||
{
|
{
|
||||||
DisplayName = "twitch (itself)",
|
DisplayName = "twitch (itself)",
|
||||||
Permissions = new ChannelPermissions()
|
MeannessFilterLevel = Enumerations.MeannessFilterLevel.Medium,
|
||||||
{
|
LewdnessFilterLevel = Enumerations.LewdnessFilterLevel.G,
|
||||||
MeannessFilterLevel = Enumerations.MeannessFilterLevel.Medium,
|
MaxTextChars = 500,
|
||||||
LewdnessFilterLevel = Enumerations.LewdnessFilterLevel.G,
|
MaxAttachmentBytes = 0,
|
||||||
MaxTextChars = 500,
|
LinksAllowed = false,
|
||||||
MaxAttachmentBytes = 0,
|
ReactionsPossible = false,
|
||||||
LinksAllowed = false,
|
|
||||||
ReactionsPossible = false
|
|
||||||
},
|
|
||||||
ExternalId = null,
|
ExternalId = null,
|
||||||
Protocol = PROTOCOL,
|
Protocol = PROTOCOL,
|
||||||
SubChannels = new List<Channel>()
|
SubChannels = new List<Channel>()
|
||||||
@ -115,10 +112,10 @@ public class TwitchInterface
|
|||||||
var m = UpsertMessage(e.WhisperMessage);
|
var m = UpsertMessage(e.WhisperMessage);
|
||||||
m.Channel.ChannelType = vassago.Models.Enumerations.ChannelType.DM;
|
m.Channel.ChannelType = vassago.Models.Enumerations.ChannelType.DM;
|
||||||
m.MentionsMe = Regex.IsMatch(e.WhisperMessage.Message?.ToLower(), $"\\b@{e.WhisperMessage.BotUsername.ToLower()}\\b");
|
m.MentionsMe = Regex.IsMatch(e.WhisperMessage.Message?.ToLower(), $"\\b@{e.WhisperMessage.BotUsername.ToLower()}\\b");
|
||||||
_db.SaveChanges();
|
await _db.SaveChangesAsync();
|
||||||
|
|
||||||
await Behaver.Instance.ActOn(m);
|
await Behaver.Instance.ActOn(m);
|
||||||
_db.SaveChanges();
|
await _db.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void Client_OnMessageReceivedAsync(object sender, OnMessageReceivedArgs e)
|
private async void Client_OnMessageReceivedAsync(object sender, OnMessageReceivedArgs e)
|
||||||
@ -134,18 +131,18 @@ public class TwitchInterface
|
|||||||
var m = UpsertMessage(e.ChatMessage);
|
var m = UpsertMessage(e.ChatMessage);
|
||||||
m.MentionsMe = Regex.IsMatch(e.ChatMessage.Message?.ToLower(), $"@{e.ChatMessage.BotUsername.ToLower()}\\b") ||
|
m.MentionsMe = Regex.IsMatch(e.ChatMessage.Message?.ToLower(), $"@{e.ChatMessage.BotUsername.ToLower()}\\b") ||
|
||||||
e.ChatMessage.ChatReply?.ParentUserLogin == e.ChatMessage.BotUsername;
|
e.ChatMessage.ChatReply?.ParentUserLogin == e.ChatMessage.BotUsername;
|
||||||
_db.SaveChanges();
|
await _db.SaveChangesAsync();
|
||||||
|
|
||||||
await Behaver.Instance.ActOn(m);
|
await Behaver.Instance.ActOn(m);
|
||||||
_db.SaveChanges();
|
await _db.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void Client_OnConnected(object sender, OnConnectedArgs e)
|
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();
|
await _db.SaveChangesAsync();
|
||||||
Behaver.Instance.Selves.Add(selfUser);
|
Behaver.Instance.MarkSelf(selfAccount);
|
||||||
|
|
||||||
Console.WriteLine($"Connected to {e.AutoJoinChannel}");
|
Console.WriteLine($"Connected to {e.AutoJoinChannel}");
|
||||||
}
|
}
|
||||||
|
36
README.md
36
README.md
@ -1,8 +1,42 @@
|
|||||||
# discord-bot
|
# 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
|
# auth link
|
||||||
|
|
||||||
https://discord.com/oauth2/authorize?client_id=913003037348491264&permissions=274877942784&scope=bot
|
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?
|
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" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>@ViewData["Title"] - vassago</title>
|
<title>@ViewData["Title"] - vassago</title>
|
||||||
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
|
<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="~/css/site.css" asp-append-version="true" />
|
||||||
<link rel="stylesheet" href="~/vassago.styles.css" asp-append-version="true" />
|
<link rel="stylesheet" href="~/vassago.styles.css" asp-append-version="true" />
|
||||||
</head>
|
</head>
|
||||||
@ -16,6 +18,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<script src="~/lib/jquery/dist/jquery.min.js"></script>
|
<script src="~/lib/jquery/dist/jquery.min.js"></script>
|
||||||
<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.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>
|
<script src="~/js/site.js" asp-append-version="true"></script>
|
||||||
@await RenderSectionAsync("Scripts", required: false)
|
@await RenderSectionAsync("Scripts", required: false)
|
||||||
</body>
|
</body>
|
@ -12,5 +12,5 @@
|
|||||||
"TwitchConfigs": [
|
"TwitchConfigs": [
|
||||||
],
|
],
|
||||||
"exchangePairsLocation": "assets/exchangepairs.json",
|
"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":"pc", "item2":"AU", "factor":206266.3},
|
||||||
{"item1":"blue whale length", "item2": "m", "factor": 29.9},
|
{"item1":"blue whale length", "item2": "m", "factor": 29.9},
|
||||||
{"item1":"m", "item2": "ångström", "factor": 10000000000},
|
{"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":"floz", "item2":"mL", "factor":29.57344},
|
||||||
{"item1":"L", "item2":"mL", "factor":1000},
|
{"item1":"L", "item2":"mL", "factor":1000},
|
||||||
|
@ -3,11 +3,14 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net7.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<NoWarn>$(NoWarn);CA2254</NoWarn>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="bootstrap" Version="5.3.3" />
|
||||||
<PackageReference Include="discord.net" Version="3.10.0" />
|
<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">
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.5">
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
@ -18,6 +21,9 @@
|
|||||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="7.0.4" />
|
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="7.0.4" />
|
||||||
<PackageReference Include="QRCoder" Version="1.4.2" />
|
<PackageReference Include="QRCoder" Version="1.4.2" />
|
||||||
<PackageReference Include="RestSharp" Version="110.2.0" />
|
<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="TwitchLib" Version="3.5.3" />
|
||||||
<PackageReference Include="youtubedlsharp" Version="0.3.1" />
|
<PackageReference Include="youtubedlsharp" Version="0.3.1" />
|
||||||
</ItemGroup>
|
</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.
|
// for details on configuring this project to bundle and minify static web assets.
|
||||||
|
|
||||||
// Write your JavaScript code.
|
// 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