Compare commits

..

2 Commits

Author SHA1 Message Date
e7b70468ae clearer definition of the concept of permission 2023-11-30 12:50:51 -05:00
47f382df19 more permission definition 2023-11-30 12:49:40 -05:00
16 changed files with 186 additions and 26 deletions

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

@ -3,7 +3,7 @@ namespace vassago.Models;
using System; using System;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
public class PermissionSettings public class ChannelPermissions
{ {
[DatabaseGenerated(DatabaseGeneratedOption.Identity)] [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; } public int Id { get; set; }
@ -13,6 +13,7 @@ public class PermissionSettings
public bool? ReactionsPossible { get; set; } public bool? ReactionsPossible { get; set; }
public Enumerations.LewdnessFilterLevel? LewdnessFilterLevel { get; set; } public Enumerations.LewdnessFilterLevel? LewdnessFilterLevel { get; set; }
public Enumerations.MeannessFilterLevel? MeannessFilterLevel { get; set; } public Enumerations.MeannessFilterLevel? MeannessFilterLevel { get; set; }
public Enumerations.VerbosityFilterLevel? VerbosityFilterLevel { get; set; }
internal DefinitePermissionSettings Definite() internal DefinitePermissionSettings Definite()
{ {
@ -23,6 +24,7 @@ public class PermissionSettings
LinksAllowed = this.LinksAllowed ?? false, LinksAllowed = this.LinksAllowed ?? false,
LewdnessFilterLevel = this.LewdnessFilterLevel ?? Enumerations.LewdnessFilterLevel.G, LewdnessFilterLevel = this.LewdnessFilterLevel ?? Enumerations.LewdnessFilterLevel.G,
MeannessFilterLevel = this.MeannessFilterLevel ?? Enumerations.MeannessFilterLevel.Strict, MeannessFilterLevel = this.MeannessFilterLevel ?? Enumerations.MeannessFilterLevel.Strict,
VerbosityFilterLevel = this.VerbosityFilterLevel ?? Enumerations.VerbosityFilterLevel.Pithy,
ReactionsPossible = this.ReactionsPossible ?? false ReactionsPossible = this.ReactionsPossible ?? false
}; };
} }

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

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

@ -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.