From e7b70468aede25355dd489ab6893148aae89666a Mon Sep 17 00:00:00 2001 From: Adam R Grey Date: Thu, 30 Nov 2023 12:50:51 -0500 Subject: [PATCH] clearer definition of the concept of permission --- Behavior/Behavior.cs | 2 +- Behavior/GeneralSnarkCloudNative.cs | 2 +- Behavior/RoomRead.cs | 28 +++++ Behavior/TwitchSummon.cs | 9 +- Behavior/TwitchUnsummon.cs | 5 +- Models/Account.cs | 2 - Models/Channel.cs | 6 +- Models/ChattingContext.cs | 2 +- Models/Enums.cs | 14 ++- Models/FeaturePermission.cs | 109 ++++++++++++++++++ Models/PermissionSettings.cs | 38 ------ Models/User.cs | 14 ++- .../DiscordInterface/DiscordInterface.cs | 4 +- .../TwitchInterface/TwitchInterface.cs | 2 +- Views/Users/Index.cshtml | 4 +- assets/jokes.txt | 5 +- 16 files changed, 183 insertions(+), 63 deletions(-) create mode 100644 Behavior/RoomRead.cs create mode 100644 Models/FeaturePermission.cs delete mode 100644 Models/PermissionSettings.cs diff --git a/Behavior/Behavior.cs b/Behavior/Behavior.cs index 4c77304..838a9fb 100644 --- a/Behavior/Behavior.cs +++ b/Behavior/Behavior.cs @@ -28,4 +28,4 @@ public abstract class Behavior ///the behavior should be static. I.e., we make one at the start and it's ready to check and go for the whole lifetime. ///As opposed to LaughAtOwnJoke, which only needs to be created to wait for 1 punchline one time. /// -public class StaticPlzAttribute : Attribute {} \ No newline at end of file +public class StaticPlzAttribute : Attribute {} diff --git a/Behavior/GeneralSnarkCloudNative.cs b/Behavior/GeneralSnarkCloudNative.cs index 0c0090f..943f292 100644 --- a/Behavior/GeneralSnarkCloudNative.cs +++ b/Behavior/GeneralSnarkCloudNative.cs @@ -22,7 +22,7 @@ public class GeneralSnarkCloudNative : Behavior if(Behaver.Instance.Selves.Any(acc => acc.Id == message.Author.Id)) return false; - if(message.Channel.EffectivePermissions.ReactionsPossible == true) + if(message.Channel.EffectivePermissions.ReactionsPossible) return false; if((MeannessFilterLevel)message.Channel.EffectivePermissions.MeannessFilterLevel < MeannessFilterLevel.Medium) diff --git a/Behavior/RoomRead.cs b/Behavior/RoomRead.cs new file mode 100644 index 0000000..893d980 --- /dev/null +++ b/Behavior/RoomRead.cs @@ -0,0 +1,28 @@ +namespace vassago.Behavior; + +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using vassago.Models; + +[StaticPlz] +public class RoomRead : Behavior +{ + public override string Name => "Room Read"; + + public override string Trigger => "roomread"; + + public override async Task ActOn(Message message) + { + var sb = new StringBuilder(); + sb.Append("Channel owned by: "); + sb.Append("๐Ÿคท"); + sb.Append(". Meanness level: "); + sb.Append(message.Channel.EffectivePermissions.MeannessFilterLevel.GetDescription()); + sb.Append(". Lewdness level: "); + sb.Append(message.Channel.EffectivePermissions.LewdnessFilterLevel.GetDescription()); + sb.Append("."); + await message.Channel.SendMessage(sb.ToString()); + return true; + } +} diff --git a/Behavior/TwitchSummon.cs b/Behavior/TwitchSummon.cs index b1a0900..3880762 100644 --- a/Behavior/TwitchSummon.cs +++ b/Behavior/TwitchSummon.cs @@ -11,7 +11,14 @@ public class TwitchSummon : Behavior public override string Trigger => "!twitchsummon"; - //TODO: Permission! + //TODO: Permission! anyone can summon from anywhere... anyone can summon to themselves. + //I think given the bot's (hopeful) ability to play nice with others - anyone can summon it anywhere + //HOWEVER, if not-the-broadcaster summons it, 1) all channel permissions to strict and 2) auto-disconnect on stream end + //i don't know if the twitch *chat* interface has knowledge of if the stream ends. maybe auto-disconnect after like 2 hours? + public override bool ShouldAct(Message message) + { + return true; + } public override async Task ActOn(Message message) { diff --git a/Behavior/TwitchUnsummon.cs b/Behavior/TwitchUnsummon.cs index c123763..9c59e99 100644 --- a/Behavior/TwitchUnsummon.cs +++ b/Behavior/TwitchUnsummon.cs @@ -16,7 +16,10 @@ public class TwitchDismiss : Behavior if(message.MentionsMe && (Regex.IsMatch(message.Content.ToLower(), "\\bbegone\\b") || Regex.IsMatch(message.Content.ToLower(), "\\bfuck off\\b"))) { - //TODO: PERMISSION! + //TODO: PERMISSION! who can dismiss me? pretty simple list: + //1) anyone in the channel with authority* + //2) whoever summoned me + //* i don't know if the twitch *chat* interface will tell me if someone's a mod. return true; } return false; diff --git a/Models/Account.cs b/Models/Account.cs index da64499..bb3eaf3 100644 --- a/Models/Account.cs +++ b/Models/Account.cs @@ -26,8 +26,6 @@ public class Account } public bool IsBot { get; set; } //webhook counts public Channel SeenInChannel { get; set; } - //permissions are per account-in-channel or per-user, and always propagate down. and since protocol will be a channel, I'll set the "is adam" permission on myself 1x/protocol. - public List PermissionTags{get;set;} public string Protocol { get; set; } public User IsUser {get; set;} } \ No newline at end of file diff --git a/Models/Channel.cs b/Models/Channel.cs index 9fbd356..1b391f6 100644 --- a/Models/Channel.cs +++ b/Models/Channel.cs @@ -14,7 +14,7 @@ public class Channel public string ExternalId { get; set; } public string DisplayName { get; set; } public bool IsDM { get; set; } - public PermissionSettings Permissions { get; set; } + public ChannelPermissions Permissions { get; set; } public List SubChannels { get; set; } public Channel ParentChannel { get; set; } public string Protocol { get; set; } @@ -33,11 +33,11 @@ public class Channel { get { - PermissionSettings toReturn = Permissions ?? new PermissionSettings(); + ChannelPermissions toReturn = Permissions ?? new ChannelPermissions(); return GetEffectivePermissions(ref toReturn).Definite(); } } - private PermissionSettings GetEffectivePermissions(ref PermissionSettings settings) + private ChannelPermissions GetEffectivePermissions(ref ChannelPermissions settings) { if(settings == null) throw new ArgumentNullException(); settings.LewdnessFilterLevel = settings.LewdnessFilterLevel ?? Permissions?.LewdnessFilterLevel; diff --git a/Models/ChattingContext.cs b/Models/ChattingContext.cs index 259a005..e1195bb 100644 --- a/Models/ChattingContext.cs +++ b/Models/ChattingContext.cs @@ -9,7 +9,7 @@ public class ChattingContext : DbContext public DbSet Channels { get; set; } //public DbSet Emoji {get;set;} public DbSet Messages { get; set; } - public DbSet PermissionSettings{get;set;} + public DbSet PermissionSettings{get;set;} public DbSet Accounts { get; set; } public DbSet Users { get; set; } diff --git a/Models/Enums.cs b/Models/Enums.cs index 6c04a84..3fdc825 100644 --- a/Models/Enums.cs +++ b/Models/Enums.cs @@ -15,7 +15,7 @@ public static class Enumerations [Description("polite company")] Moderate, [Description(";) ;) ;)")] - unrestricted + Unrestricted } public enum MeannessFilterLevel { @@ -23,13 +23,17 @@ public static class Enumerations Strict, [Description("a bit cheeky")] Medium, - [Description("387.44 million miles of printed circuits, etc")] + [Description("387.44m mi of printed circuits")] Unrestricted } - public enum WellknownPermissions + public enum VerbosityFilterLevel { - Master, //e.g., me. not that I think this would ever be released? - TwitchSummon, + [Description("stfu")] + Quiet, + [Description("pithy")] + Pithy, + [Description("you want text i'll GIVE you text")] + Unrestricted } public static string GetDescription(this T enumerationValue) diff --git a/Models/FeaturePermission.cs b/Models/FeaturePermission.cs new file mode 100644 index 0000000..a4475fc --- /dev/null +++ b/Models/FeaturePermission.cs @@ -0,0 +1,109 @@ +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 RestrictedToUsers { get; set; } + public IEnumerable RestrictedToAccounts { get; set; } + public IEnumerable 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; + } +} diff --git a/Models/PermissionSettings.cs b/Models/PermissionSettings.cs deleted file mode 100644 index 95c9aa4..0000000 --- a/Models/PermissionSettings.cs +++ /dev/null @@ -1,38 +0,0 @@ -namespace vassago.Models; - -using System; -using System.ComponentModel.DataAnnotations.Schema; - -public class PermissionSettings -{ - [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; } -} \ No newline at end of file diff --git a/Models/User.cs b/Models/User.cs index b1db073..92437b4 100644 --- a/Models/User.cs +++ b/Models/User.cs @@ -10,6 +10,16 @@ public class User [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public Guid Id { get; set; } public List Accounts { get; set; } - //permissions are per account-in-channel or per-user, and always propagate down. and since protocol will be a channel, I'll set the "is adam" permission on myself 1x/protocol. - public List PermissionTags{get;set;} + + public string DisplayName + { + get + { + return Accounts.Select(a => a.DisplayName).Distinct() + .MaxBy(distinctName => + Accounts.Select(a => a.DisplayName) + .Where(selectedName => selectedName == distinctName).Count() + ); + } + } } \ No newline at end of file diff --git a/ProtocolInterfaces/DiscordInterface/DiscordInterface.cs b/ProtocolInterfaces/DiscordInterface/DiscordInterface.cs index 51e9194..01be997 100644 --- a/ProtocolInterfaces/DiscordInterface/DiscordInterface.cs +++ b/ProtocolInterfaces/DiscordInterface/DiscordInterface.cs @@ -58,7 +58,7 @@ public class DiscordInterface protocolAsChannel = new Channel() { DisplayName = "discord (itself)", - Permissions = new PermissionSettings() + Permissions = new Models.ChannelPermissions() { MeannessFilterLevel = Enumerations.MeannessFilterLevel.Strict, LewdnessFilterLevel = Enumerations.LewdnessFilterLevel.Moderate, @@ -291,7 +291,7 @@ public class DiscordInterface c.Protocol = protocolAsChannel.Protocol; c.ParentChannel = protocolAsChannel; c.SubChannels = c.SubChannels ?? new List(); - c.Permissions = c.Permissions ?? new PermissionSettings(); + c.Permissions = c.Permissions ?? new Models.ChannelPermissions(); c.Permissions.MaxAttachmentBytes = channel.MaxUploadLimit; c.SendMessage = (t) => { throw new InvalidOperationException($"channel {channel.Name} is guild; cannot accept text"); }; diff --git a/ProtocolInterfaces/TwitchInterface/TwitchInterface.cs b/ProtocolInterfaces/TwitchInterface/TwitchInterface.cs index f70f2aa..9e7ab6d 100644 --- a/ProtocolInterfaces/TwitchInterface/TwitchInterface.cs +++ b/ProtocolInterfaces/TwitchInterface/TwitchInterface.cs @@ -38,7 +38,7 @@ public class TwitchInterface protocolAsChannel = new Channel() { DisplayName = "twitch (itself)", - Permissions = new PermissionSettings() + Permissions = new ChannelPermissions() { MeannessFilterLevel = Enumerations.MeannessFilterLevel.Medium, LewdnessFilterLevel = Enumerations.LewdnessFilterLevel.G, diff --git a/Views/Users/Index.cshtml b/Views/Users/Index.cshtml index 24ebb70..bb35553 100644 --- a/Views/Users/Index.cshtml +++ b/Views/Users/Index.cshtml @@ -10,7 +10,7 @@ @Html.DisplayNameFor(model => model.Id) - @Html.DisplayNameFor(model => model.PermissionTags) + name* number of associated accounts @@ -24,7 +24,7 @@ @Html.DisplayFor(modelItem => item.Id) - @Html.DisplayFor(modelItem => item.PermissionTags) + @Html.DisplayFor(modelItem => item.DisplayName) @Html.DisplayFor(modelItem => item.Accounts.Count)x diff --git a/assets/jokes.txt b/assets/jokes.txt index 7ed9f5e..872917f 100644 --- a/assets/jokes.txt +++ b/assets/jokes.txt @@ -17,7 +17,7 @@ What did the teacher do with the students report on cheese? Grated it. What do you call a man with no legs and arms in a pool? Bob. I was going to tell a joke about hammers but ...I don't think I'll nail it why did the can recycler quit his job? because it was so depressing. -They told me to stop impersonating a flamingo. I had to put my foot down. +They told me to stop impersonating a flamingo. I had to put my foot down. I failed math so many times at school, I canโ€™t even count. When I was a child, I threw a boomerang, but it didn't come back. I live in constant fear. When life gives you melons, you might be dyslexic. @@ -40,7 +40,6 @@ A ghost walked into a bar and ordered a shot of vodka. The bartender said, โ€˜So A blind man walked into a bar. and a table. and a chair. How do you make holy water? You boil the hell out of it. My teachers told me Iโ€™d never amount to much because I procrastinate so much. I told them, โ€œJust you wait!โ€ -ื‘ืจื›ื‘ ืฉื ื•ืกืข ืขืœ 4 ื’ืœื’ืœื™ื, ืื™ื–ื” ื’ืœื’ืœ ืœื ื–ื–? ื’ืœื’ืœ ืจื–ืจื‘ื™ what's the best part about living in switzerland? well the flag is a big plus. I asked my date to meet me at the gym today. She didn't show up. That's when I knew we weren't gonna work out. The CEO of IKEA was elected Prime Minister in Sweden. He should have his cabinet together by the end of the weekend. @@ -76,4 +75,4 @@ Someone broke into my house and stole all my fruits. I'm peachless. Did I tell you guys about that flat earther i got into an argument with? he got so mad he stormed off saying he'd walk to the edge of the earth to prove me wrong, but he'll come around eventually. What do you call your friend who stands in a hole? Phil. What happened when the bear swallowed a clock? He got ticks. -What do you call a wolf who gets lost? A where-wolf. \ No newline at end of file +What do you call a wolf who gets lost? A where-wolf.