clearer definition of the concept of permission

This commit is contained in:
Adam R Grey 2023-11-30 12:50:51 -05:00
parent 47f382df19
commit e7b70468ae
16 changed files with 183 additions and 63 deletions

View File

@ -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. ///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. ///As opposed to LaughAtOwnJoke, which only needs to be created to wait for 1 punchline one time.
///</summary> ///</summary>
public class StaticPlzAttribute : Attribute {} public class StaticPlzAttribute : Attribute {}

View File

@ -22,7 +22,7 @@ public class GeneralSnarkCloudNative : Behavior
if(Behaver.Instance.Selves.Any(acc => acc.Id == message.Author.Id)) if(Behaver.Instance.Selves.Any(acc => acc.Id == message.Author.Id))
return false; return false;
if(message.Channel.EffectivePermissions.ReactionsPossible == true) if(message.Channel.EffectivePermissions.ReactionsPossible)
return false; return false;
if((MeannessFilterLevel)message.Channel.EffectivePermissions.MeannessFilterLevel < MeannessFilterLevel.Medium) if((MeannessFilterLevel)message.Channel.EffectivePermissions.MeannessFilterLevel < MeannessFilterLevel.Medium)

28
Behavior/RoomRead.cs Normal file
View File

@ -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<bool> ActOn(Message message)
{
var sb = new StringBuilder();
sb.Append("Channel owned by: ");
sb.Append("🤷");
sb.Append(". Meanness level: ");
sb.Append(message.Channel.EffectivePermissions.MeannessFilterLevel.GetDescription());
sb.Append(". Lewdness level: ");
sb.Append(message.Channel.EffectivePermissions.LewdnessFilterLevel.GetDescription());
sb.Append(".");
await message.Channel.SendMessage(sb.ToString());
return true;
}
}

View File

@ -11,7 +11,14 @@ public class TwitchSummon : Behavior
public override string Trigger => "!twitchsummon"; 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<bool> ActOn(Message message) public override async Task<bool> ActOn(Message message)
{ {

View File

@ -16,7 +16,10 @@ public class TwitchDismiss : Behavior
if(message.MentionsMe && if(message.MentionsMe &&
(Regex.IsMatch(message.Content.ToLower(), "\\bbegone\\b") || Regex.IsMatch(message.Content.ToLower(), "\\bfuck off\\b"))) (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 true;
} }
return false; return false;

View File

@ -26,8 +26,6 @@ public class Account
} }
public bool IsBot { get; set; } //webhook counts public bool IsBot { get; set; } //webhook counts
public Channel SeenInChannel { get; set; } 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<Enumerations.WellknownPermissions> PermissionTags{get;set;}
public string Protocol { get; set; } public string Protocol { get; set; }
public User IsUser {get; set;} public User IsUser {get; set;}
} }

View File

@ -14,7 +14,7 @@ public class Channel
public string ExternalId { get; set; } public string ExternalId { get; set; }
public string DisplayName { get; set; } public string DisplayName { get; set; }
public bool IsDM { get; set; } public bool IsDM { get; set; }
public PermissionSettings Permissions { 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; }
@ -33,11 +33,11 @@ public class Channel
{ {
get get
{ {
PermissionSettings toReturn = Permissions ?? new PermissionSettings(); ChannelPermissions toReturn = Permissions ?? new ChannelPermissions();
return GetEffectivePermissions(ref toReturn).Definite(); return GetEffectivePermissions(ref toReturn).Definite();
} }
} }
private PermissionSettings GetEffectivePermissions(ref PermissionSettings settings) private ChannelPermissions GetEffectivePermissions(ref ChannelPermissions settings)
{ {
if(settings == null) throw new ArgumentNullException(); if(settings == null) throw new ArgumentNullException();
settings.LewdnessFilterLevel = settings.LewdnessFilterLevel ?? Permissions?.LewdnessFilterLevel; settings.LewdnessFilterLevel = settings.LewdnessFilterLevel ?? Permissions?.LewdnessFilterLevel;

View File

@ -9,7 +9,7 @@ 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<PermissionSettings> PermissionSettings{get;set;} public DbSet<ChannelPermissions> PermissionSettings{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; }

View File

@ -15,7 +15,7 @@ public static class Enumerations
[Description("polite company")] [Description("polite company")]
Moderate, Moderate,
[Description(";) ;) ;)")] [Description(";) ;) ;)")]
unrestricted Unrestricted
} }
public enum MeannessFilterLevel public enum MeannessFilterLevel
{ {
@ -23,13 +23,17 @@ public static class Enumerations
Strict, Strict,
[Description("a bit cheeky")] [Description("a bit cheeky")]
Medium, Medium,
[Description("387.44 million miles of printed circuits, etc")] [Description("387.44m mi of printed circuits")]
Unrestricted Unrestricted
} }
public enum WellknownPermissions public enum VerbosityFilterLevel
{ {
Master, //e.g., me. not that I think this would ever be released? [Description("stfu")]
TwitchSummon, Quiet,
[Description("pithy")]
Pithy,
[Description("you want text i'll GIVE you text")]
Unrestricted
} }
public static string GetDescription<T>(this T enumerationValue) public static string GetDescription<T>(this T enumerationValue)

109
Models/FeaturePermission.cs Normal file
View File

@ -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<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;
}
}

View File

@ -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; }
}

View File

@ -10,6 +10,16 @@ public class User
[DatabaseGenerated(DatabaseGeneratedOption.Identity)] [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; } public Guid Id { get; set; }
public List<Account> Accounts { get; set; } public List<Account> 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<Enumerations.WellknownPermissions> 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()
);
}
}
} }

View File

@ -58,7 +58,7 @@ public class DiscordInterface
protocolAsChannel = new Channel() protocolAsChannel = new Channel()
{ {
DisplayName = "discord (itself)", DisplayName = "discord (itself)",
Permissions = new PermissionSettings() Permissions = new Models.ChannelPermissions()
{ {
MeannessFilterLevel = Enumerations.MeannessFilterLevel.Strict, MeannessFilterLevel = Enumerations.MeannessFilterLevel.Strict,
LewdnessFilterLevel = Enumerations.LewdnessFilterLevel.Moderate, LewdnessFilterLevel = Enumerations.LewdnessFilterLevel.Moderate,
@ -291,7 +291,7 @@ 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 PermissionSettings(); c.Permissions = c.Permissions ?? new Models.ChannelPermissions();
c.Permissions.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"); };

View File

@ -38,7 +38,7 @@ public class TwitchInterface
protocolAsChannel = new Channel() protocolAsChannel = new Channel()
{ {
DisplayName = "twitch (itself)", DisplayName = "twitch (itself)",
Permissions = new PermissionSettings() Permissions = new ChannelPermissions()
{ {
MeannessFilterLevel = Enumerations.MeannessFilterLevel.Medium, MeannessFilterLevel = Enumerations.MeannessFilterLevel.Medium,
LewdnessFilterLevel = Enumerations.LewdnessFilterLevel.G, LewdnessFilterLevel = Enumerations.LewdnessFilterLevel.G,

View File

@ -10,7 +10,7 @@
@Html.DisplayNameFor(model => model.Id) @Html.DisplayNameFor(model => model.Id)
</th> </th>
<th> <th>
@Html.DisplayNameFor(model => model.PermissionTags) name*
</th> </th>
<th> <th>
number of associated accounts number of associated accounts
@ -24,7 +24,7 @@
@Html.DisplayFor(modelItem => item.Id) @Html.DisplayFor(modelItem => item.Id)
</td> </td>
<td> <td>
@Html.DisplayFor(modelItem => item.PermissionTags) @Html.DisplayFor(modelItem => item.DisplayName)
</td> </td>
<td> <td>
@Html.DisplayFor(modelItem => item.Accounts.Count)x @Html.DisplayFor(modelItem => item.Accounts.Count)x

View File

@ -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. 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 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. 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 cant even count. I failed math so many times at school, I cant even count.
When I was a child, I threw a boomerang, but it didn't come back. I live in constant fear. 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. 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. 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. How do you make holy water? You boil the hell out of it.
My teachers told me Id never amount to much because I procrastinate so much. I told them, “Just you wait!” My teachers told me Id 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. 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. 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. 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. 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 do you call your friend who stands in a hole? Phil.
What happened when the bear swallowed a clock? He got ticks. What happened when the bear swallowed a clock? He got ticks.
What do you call a wolf who gets lost? A where-wolf. What do you call a wolf who gets lost? A where-wolf.