Added groups & permissions. Fixed 7tv reconnection. Added more subcommands for refresh.

This commit is contained in:
Tom
2024-07-12 17:36:09 +00:00
parent af3763a837
commit 9fb966474f
37 changed files with 806 additions and 159 deletions

View File

@ -10,17 +10,22 @@ using TwitchChatTTS.Seven;
using TwitchChatTTS.Chat.Commands;
using TwitchChatTTS.Hermes.Socket;
using HermesSocketLibrary.Socket.Data;
using TwitchChatTTS.Chat.Groups.Permissions;
using TwitchChatTTS.Chat.Groups;
public class ChatMessageHandler
{
private readonly User _user;
private readonly Configuration _configuration;
private readonly EmoteDatabase _emotes;
private readonly TTSPlayer _player;
private readonly ChatCommandManager _commands;
private readonly IGroupPermissionManager _permissionManager;
private readonly IChatterGroupManager _chatterGroupManager;
private readonly EmoteDatabase _emotes;
private readonly OBSSocketClient? _obsClient;
private readonly HermesSocketClient? _hermesClient;
private readonly Configuration _configuration;
private readonly ILogger _logger;
private Regex sfxRegex;
@ -33,6 +38,8 @@ public class ChatMessageHandler
User user,
TTSPlayer player,
ChatCommandManager commands,
IGroupPermissionManager permissionManager,
IChatterGroupManager chatterGroupManager,
EmoteDatabase emotes,
[FromKeyedServices("obs")] SocketClient<WebSocketMessage> obsClient,
[FromKeyedServices("hermes")] SocketClient<WebSocketMessage> hermesClient,
@ -43,13 +50,15 @@ public class ChatMessageHandler
_user = user;
_player = player;
_commands = commands;
_permissionManager = permissionManager;
_chatterGroupManager = chatterGroupManager;
_emotes = emotes;
_obsClient = obsClient as OBSSocketClient;
_hermesClient = hermesClient as HermesSocketClient;
_configuration = configuration;
_logger = logger;
_chatters = null;
_chatters = new HashSet<long>();
sfxRegex = new Regex(@"\(([A-Za-z0-9_-]+)\)");
}
@ -66,18 +75,28 @@ public class ChatMessageHandler
var chatterId = long.Parse(m.UserId);
var tasks = new List<Task>();
var blocked = _user.ChatterFilters.TryGetValue(m.Username, out TTSUsernameFilter? filter) && filter.Tag == "blacklisted";
var permissionPath = "tts.chat.messages.read";
if (!string.IsNullOrWhiteSpace(m.CustomRewardId))
permissionPath = "tts.chat.redemptions.read";
var checks = new bool[] { true, m.IsSubscriber, m.IsVip, m.IsModerator, m.IsBroadcaster };
var defaultGroups = new string[] { "everyone", "subscribers", "vip", "moderators", "broadcaster" };
var customGroups = _chatterGroupManager.GetGroupNamesFor(chatterId);
var groups = defaultGroups.Where((e, i) => checks[i]).Union(customGroups);
var permission = chatterId == _user.OwnerId ? true : _permissionManager.CheckIfAllowed(groups, permissionPath);
var blocked = permission != true;
if (!blocked || m.IsBroadcaster)
{
try
{
var commandResult = await _commands.Execute(msg, m);
var commandResult = await _commands.Execute(msg, m, groups);
if (commandResult != ChatCommandResult.Unknown)
return new MessageResult(MessageStatus.Command, -1, -1);
}
catch (Exception ex)
{
_logger.Error(ex, $"Failed executing a chat command [message: {msg}][chatter: {m.Username}][cid: {m.UserId}][mid: {m.Id}]");
_logger.Error(ex, $"Failed executing a chat command [message: {msg}][chatter: {m.Username}][chatter id: {m.UserId}][message id: {m.Id}]");
}
}
@ -100,7 +119,7 @@ public class ChatMessageHandler
// Filter highly repetitive words (like emotes) from the message.
int totalEmoteUsed = 0;
var emotesUsed = new HashSet<string>();
var words = msg.Split(" ");
var words = msg.Split(' ');
var wordCounter = new Dictionary<string, int>();
string filteredMsg = string.Empty;
var newEmotes = new Dictionary<string, string>();
@ -164,26 +183,13 @@ public class ChatMessageHandler
}
// Determine the priority of this message
int priority = 0;
if (m.IsStaff)
priority = int.MinValue;
else if (filter?.Tag == "priority")
priority = int.MinValue + 1;
else if (m.IsModerator)
priority = -100;
else if (m.IsVip)
priority = -10;
else if (m.IsPartner)
priority = -5;
else if (m.IsHighlighted)
priority = -1;
priority = Math.Min(priority, -m.SubscribedMonthCount * (m.IsSubscriber ? 2 : 1));
int priority = _chatterGroupManager.GetPriorityFor(groups) + m.SubscribedMonthCount * (m.IsSubscriber ? 10 : 5);
// Determine voice selected.
string voiceSelected = _user.DefaultTTSVoice;
if (long.TryParse(e.ChatMessage.UserId, out long userId) && _user.VoicesSelected?.ContainsKey(userId) == true)
if (_user.VoicesSelected?.ContainsKey(chatterId) == true)
{
var voiceId = _user.VoicesSelected[userId];
var voiceId = _user.VoicesSelected[chatterId];
if (_user.VoicesAvailable.TryGetValue(voiceId, out string? voiceName) && voiceName != null)
{
if (_user.VoicesEnabled.Contains(voiceName) || chatterId == _user.OwnerId || m.IsStaff)
@ -230,7 +236,7 @@ public class ChatMessageHandler
if (parts.Length == 1)
{
_logger.Information($"Username: {m.Username}; User ID: {m.UserId}; Voice: {voice}; Priority: {priority}; Message: {message}; Month: {m.SubscribedMonthCount}; {badgesString}");
_logger.Information($"Username: {m.Username}; User ID: {m.UserId}; Voice: {voice}; Priority: {priority}; Message: {message}; Month: {m.SubscribedMonthCount}; Reward Id: {m.CustomRewardId}; {badgesString}");
_player.Add(new TTSMessage()
{
Voice = voice,

View File

@ -14,6 +14,8 @@ namespace TwitchChatTTS.Chat.Commands
private readonly SocketClient<WebSocketMessage> _hermesClient;
private readonly ILogger _logger;
public new bool DefaultPermissionsOverwrite { get => true; }
public AddTTSVoiceCommand(
User user,
[FromKeyedServices("parameter-unvalidated")] ChatCommandParameter ttsVoiceParameter,
@ -28,14 +30,13 @@ namespace TwitchChatTTS.Chat.Commands
AddParameter(ttsVoiceParameter);
}
public override async Task<bool> CheckPermissions(ChatMessage message, long broadcasterId)
public override async Task<bool> CheckDefaultPermissions(ChatMessage message, long broadcasterId)
{
return false;
}
public override async Task Execute(IList<string> args, ChatMessage message, long broadcasterId)
{
//var HermesClient = _serviceProvider.GetRequiredKeyedService<SocketClient<WebSocketMessage>>("hermes");
if (_hermesClient == null)
return;
if (_user == null || _user.VoicesAvailable == null)

View File

@ -8,12 +8,15 @@ namespace TwitchChatTTS.Chat.Commands
public string Name { get; }
public string Description { get; }
public IList<ChatCommandParameter> Parameters { get => _parameters.AsReadOnly(); }
public bool DefaultPermissionsOverwrite { get; }
private IList<ChatCommandParameter> _parameters;
public ChatCommand(string name, string description)
{
Name = name;
Description = description;
DefaultPermissionsOverwrite = false;
_parameters = new List<ChatCommandParameter>();
}
@ -24,7 +27,7 @@ namespace TwitchChatTTS.Chat.Commands
}
}
public abstract Task<bool> CheckPermissions(ChatMessage message, long broadcasterId);
public abstract Task<bool> CheckDefaultPermissions(ChatMessage message, long broadcasterId);
public abstract Task Execute(IList<string> args, ChatMessage message, long broadcasterId);
}
}

View File

@ -1,6 +1,8 @@
using System.Text.RegularExpressions;
using Microsoft.Extensions.DependencyInjection;
using Serilog;
using TwitchChatTTS.Chat.Groups;
using TwitchChatTTS.Chat.Groups.Permissions;
using TwitchLib.Client.Models;
namespace TwitchChatTTS.Chat.Commands
@ -10,15 +12,26 @@ namespace TwitchChatTTS.Chat.Commands
private IDictionary<string, ChatCommand> _commands;
private readonly TwitchBotAuth _token;
private readonly User _user;
private readonly IGroupPermissionManager _permissionManager;
private readonly IChatterGroupManager _chatterGroupManager;
private readonly IServiceProvider _serviceProvider;
private readonly ILogger _logger;
private string CommandStartSign { get; } = "!";
public ChatCommandManager(TwitchBotAuth token, User user, IServiceProvider serviceProvider, ILogger logger)
public ChatCommandManager(
TwitchBotAuth token,
User user,
IGroupPermissionManager permissionManager,
IChatterGroupManager chatterGroupManager,
IServiceProvider serviceProvider,
ILogger logger
)
{
_token = token;
_user = user;
_permissionManager = permissionManager;
_chatterGroupManager = chatterGroupManager;
_serviceProvider = serviceProvider;
_logger = logger;
@ -56,7 +69,7 @@ namespace TwitchChatTTS.Chat.Commands
}
}
public async Task<ChatCommandResult> Execute(string arg, ChatMessage message)
public async Task<ChatCommandResult> Execute(string arg, ChatMessage message, IEnumerable<string> groups)
{
if (_token.BroadcasterId == null)
return ChatCommandResult.Unknown;
@ -80,19 +93,31 @@ namespace TwitchChatTTS.Chat.Commands
if (!_commands.TryGetValue(com, out ChatCommand? command) || command == null)
{
// Could be for another bot or just misspelled.
_logger.Debug($"Failed to find command named '{com}' [args: {arg}][chatter: {message.Username}][cid: {message.UserId}]");
_logger.Debug($"Failed to find command named '{com}' [args: {arg}][chatter: {message.Username}][chatter id: {message.UserId}]");
return ChatCommandResult.Missing;
}
if (!await command.CheckPermissions(message, broadcasterId) && message.UserId != _user.OwnerId?.ToString() && !message.IsStaff)
// Check if command can be executed by this chatter.
long chatterId = long.Parse(message.UserId);
if (chatterId != _user.OwnerId)
{
_logger.Warning($"Chatter is missing permission to execute command named '{com}' [args: {arg}][chatter: {message.Username}][cid: {message.UserId}]");
return ChatCommandResult.Permission;
var executable = command.DefaultPermissionsOverwrite ? false : CanExecute(chatterId, groups, com);
if (executable == false)
{
_logger.Debug($"Denied permission to use command [chatter id: {chatterId}][command: {com}]");
return ChatCommandResult.Permission;
}
else if (executable == null && !await command.CheckDefaultPermissions(message, broadcasterId))
{
_logger.Debug($"Chatter is missing default permission to execute command named '{com}' [args: {arg}][chatter: {message.Username}][chatter id: {message.UserId}]");
return ChatCommandResult.Permission;
}
}
// Check if the syntax is correct.
if (command.Parameters.Count(p => !p.Optional) > args.Length)
{
_logger.Warning($"Command syntax issue when executing command named '{com}' [args: {arg}][chatter: {message.Username}][cid: {message.UserId}]");
_logger.Debug($"Command syntax issue when executing command named '{com}' [args: {arg}][chatter: {message.Username}][chatter id: {message.UserId}]");
return ChatCommandResult.Syntax;
}
@ -111,12 +136,18 @@ namespace TwitchChatTTS.Chat.Commands
}
catch (Exception e)
{
_logger.Error(e, $"Command '{arg}' failed.");
_logger.Error(e, $"Command '{arg}' failed [args: {arg}][chatter: {message.Username}][chatter id: {message.UserId}]");
return ChatCommandResult.Fail;
}
_logger.Information($"Executed the {com} command [arguments: {arg}]");
_logger.Information($"Executed the {com} command [args: {arg}][chatter: {message.Username}][chatter id: {message.UserId}]");
return ChatCommandResult.Success;
}
private bool? CanExecute(long chatterId, IEnumerable<string> groups, string path)
{
_logger.Debug($"Checking for permission [chatter id: {chatterId}][group: {string.Join(", ", groups)}][path: {path}]");
return _permissionManager.CheckIfAllowed(groups, path);
}
}
}

View File

@ -30,7 +30,7 @@ namespace TwitchChatTTS.Chat.Commands
AddParameter(unvalidatedParameter);
}
public override async Task<bool> CheckPermissions(ChatMessage message, long broadcasterId)
public override async Task<bool> CheckDefaultPermissions(ChatMessage message, long broadcasterId)
{
return message.IsModerator || message.IsBroadcaster;
}

View File

@ -1,4 +1,7 @@
using Serilog;
using TwitchChatTTS.Chat.Groups;
using TwitchChatTTS.Chat.Groups.Permissions;
using TwitchChatTTS.OBS.Socket.Manager;
using TwitchChatTTS.Twitch.Redemptions;
using TwitchLib.Client.Models;
@ -8,30 +11,43 @@ namespace TwitchChatTTS.Chat.Commands
{
private readonly User _user;
private readonly RedemptionManager _redemptionManager;
private readonly IGroupPermissionManager _permissionManager;
private readonly IChatterGroupManager _chatterGroupManager;
private readonly OBSManager _obsManager;
private readonly HermesApiClient _hermesApi;
private readonly ILogger _logger;
public RefreshTTSDataCommand(User user, RedemptionManager redemptionManager, HermesApiClient hermesApi, ILogger logger)
: base("refresh", "Refreshes certain TTS related data on the client.")
public RefreshTTSDataCommand(
User user,
RedemptionManager redemptionManager,
IGroupPermissionManager permissionManager,
IChatterGroupManager chatterGroupManager,
OBSManager obsManager,
HermesApiClient hermesApi,
ILogger logger
) : base("refresh", "Refreshes certain TTS related data on the client.")
{
_user = user;
_redemptionManager = redemptionManager;
_permissionManager = permissionManager;
_chatterGroupManager = chatterGroupManager;
_obsManager = obsManager;
_hermesApi = hermesApi;
_logger = logger;
}
public override async Task<bool> CheckPermissions(ChatMessage message, long broadcasterId)
public override async Task<bool> CheckDefaultPermissions(ChatMessage message, long broadcasterId)
{
return message.IsModerator || message.IsBroadcaster;
}
public override async Task Execute(IList<string> args, ChatMessage message, long broadcasterId)
{
var service = args.FirstOrDefault();
if (service == null)
var value = args.FirstOrDefault();
if (value == null)
return;
switch (service)
switch (value.ToLower())
{
case "tts_voice_enabled":
var voicesEnabled = await _hermesApi.FetchTTSEnabledVoices();
@ -52,6 +68,13 @@ namespace TwitchChatTTS.Chat.Commands
_logger.Information($"{_user.ChatterFilters.Where(f => f.Value.Tag == "blacklisted").Count()} username(s) have been blocked.");
_logger.Information($"{_user.ChatterFilters.Where(f => f.Value.Tag == "priority").Count()} user(s) have been prioritized.");
break;
case "selected_voices":
{
var voicesSelected = await _hermesApi.FetchTTSChatterSelectedVoices();
_user.VoicesSelected = voicesSelected.ToDictionary(s => s.ChatterId, s => s.Voice);
_logger.Information($"{_user.VoicesSelected.Count} TTS voices have been selected for specific chatters.");
break;
}
case "default_voice":
_user.DefaultTTSVoice = await _hermesApi.FetchTTSDefaultVoice();
_logger.Information("TTS Default Voice: " + _user.DefaultTTSVoice);
@ -62,6 +85,58 @@ namespace TwitchChatTTS.Chat.Commands
_redemptionManager.Initialize(redemptions, redemptionActions.ToDictionary(a => a.Name, a => a));
_logger.Information($"Redemption Manager has been refreshed with {redemptionActions.Count()} actions & {redemptions.Count()} redemptions.");
break;
case "obs_cache":
{
try
{
_obsManager.ClearCache();
await _obsManager.GetGroupList(async groups => await _obsManager.GetGroupSceneItemList(groups));
}
catch (Exception e)
{
_logger.Error(e, "Failed to load OBS group info via command.");
}
break;
}
case "permissions":
{
_chatterGroupManager.Clear();
_permissionManager.Clear();
var groups = await _hermesApi.FetchGroups();
var groupsById = groups.ToDictionary(g => g.Id, g => g);
foreach (var group in groups)
_chatterGroupManager.Add(group);
_logger.Information($"{groups.Count()} groups have been loaded.");
var groupChatters = await _hermesApi.FetchGroupChatters();
_logger.Debug($"{groupChatters.Count()} group users have been fetched.");
var permissions = await _hermesApi.FetchGroupPermissions();
foreach (var permission in permissions)
{
_logger.Debug($"Adding group permission [id: {permission.Id}][group id: {permission.GroupId}][path: {permission.Path}][allow: {permission.Allow?.ToString() ?? "null"}]");
if (groupsById.TryGetValue(permission.GroupId, out var group))
{
_logger.Warning($"Failed to find group by id [id: {permission.Id}][group id: {permission.GroupId}][path: {permission.Path}]");
continue;
}
var path = $"{group.Name}.{permission.Path}";
_permissionManager.Set(path, permission.Allow);
_logger.Debug($"Added group permission [id: {permission.Id}][group id: {permission.GroupId}][path: {permission.Path}]");
}
_logger.Information($"{permissions.Count()} group permissions have been loaded.");
foreach (var chatter in groupChatters)
if (groupsById.TryGetValue(chatter.GroupId, out var group))
_chatterGroupManager.Add(chatter.ChatterId, group.Name);
_logger.Information($"Users in each group have been loaded.");
break;
}
default:
_logger.Warning($"Unknown refresh value given [value: {value}]");
break;
}
}
}

View File

@ -14,6 +14,8 @@ namespace TwitchChatTTS.Chat.Commands
private readonly SocketClient<WebSocketMessage> _hermesClient;
private ILogger _logger;
public new bool DefaultPermissionsOverwrite { get => true; }
public RemoveTTSVoiceCommand(
[FromKeyedServices("parameter-unvalidated")] ChatCommandParameter ttsVoiceParameter,
User user,
@ -28,7 +30,7 @@ namespace TwitchChatTTS.Chat.Commands
AddParameter(ttsVoiceParameter);
}
public override async Task<bool> CheckPermissions(ChatMessage message, long broadcasterId)
public override async Task<bool> CheckDefaultPermissions(ChatMessage message, long broadcasterId)
{
return false;
}

View File

@ -15,7 +15,7 @@ namespace TwitchChatTTS.Chat.Commands
_logger = logger;
}
public override async Task<bool> CheckPermissions(ChatMessage message, long broadcasterId)
public override async Task<bool> CheckDefaultPermissions(ChatMessage message, long broadcasterId)
{
return message.IsModerator || message.IsVip || message.IsBroadcaster;
}

View File

@ -15,7 +15,7 @@ namespace TwitchChatTTS.Chat.Commands
_logger = logger;
}
public override async Task<bool> CheckPermissions(ChatMessage message, long broadcasterId)
public override async Task<bool> CheckDefaultPermissions(ChatMessage message, long broadcasterId)
{
return message.IsModerator || message.IsVip || message.IsBroadcaster;
}

View File

@ -30,7 +30,7 @@ namespace TwitchChatTTS.Chat.Commands
AddParameter(unvalidatedParameter);
}
public override async Task<bool> CheckPermissions(ChatMessage message, long broadcasterId)
public override async Task<bool> CheckDefaultPermissions(ChatMessage message, long broadcasterId)
{
return message.IsModerator || message.IsBroadcaster;
}

View File

@ -13,7 +13,7 @@ namespace TwitchChatTTS.Chat.Commands
_logger = logger;
}
public override async Task<bool> CheckPermissions(ChatMessage message, long broadcasterId)
public override async Task<bool> CheckDefaultPermissions(ChatMessage message, long broadcasterId)
{
return message.IsBroadcaster;
}

View File

@ -28,7 +28,7 @@ namespace TwitchChatTTS.Chat.Commands
AddParameter(ttsVoiceParameter);
}
public override async Task<bool> CheckPermissions(ChatMessage message, long broadcasterId)
public override async Task<bool> CheckDefaultPermissions(ChatMessage message, long broadcasterId)
{
return message.IsModerator || message.IsBroadcaster || message.IsSubscriber || message.Bits >= 100;
}

View File

@ -0,0 +1,75 @@
using System.Collections.Concurrent;
using Serilog;
namespace TwitchChatTTS.Chat.Groups
{
public class ChatterGroupManager : IChatterGroupManager
{
private readonly IDictionary<string, Group> _groups;
private readonly IDictionary<long, ICollection<string>> _chatters;
private readonly ILogger _logger;
public ChatterGroupManager(ILogger logger) {
_logger = logger;
_groups = new ConcurrentDictionary<string, Group>();
_chatters = new ConcurrentDictionary<long, ICollection<string>>();
}
public void Add(Group group) {
_groups.Add(group.Name, group);
}
public void Add(long chatter, string groupName) {
_chatters.Add(chatter, new List<string>() { groupName });
}
public void Add(long chatter, ICollection<string> groupNames) {
if (_chatters.TryGetValue(chatter, out var list)) {
foreach (var group in groupNames)
list.Add(group);
} else
_chatters.Add(chatter, groupNames);
}
public void Clear() {
_groups.Clear();
_chatters.Clear();
}
public Group? Get(string groupName) {
if (_groups.TryGetValue(groupName, out var group))
return group;
return null;
}
public IEnumerable<string> GetGroupNamesFor(long chatter) {
if (_chatters.TryGetValue(chatter, out var groups))
return groups.Select(g => _groups[g].Name);
return Array.Empty<string>();
}
public int GetPriorityFor(long chatter) {
if (!_chatters.TryGetValue(chatter, out var groups))
return 0;
return GetPriorityFor(groups);
}
public int GetPriorityFor(IEnumerable<string> groupNames) {
return groupNames.Select(g => _groups.TryGetValue(g, out var group) ? group : null).Where(g => g != null).Max(g => g.Priority);
}
public bool Remove(long chatterId, string groupId) {
if (_chatters.TryGetValue(chatterId, out var groups)) {
groups.Remove(groupId);
_logger.Debug($"Removed chatter from group [chatter id: {chatterId}][group name: {_groups[groupId]}][group id: {groupId}]");
return true;
}
_logger.Debug($"Failed to remove chatter from group [chatter id: {chatterId}][group name: {_groups[groupId]}][group id: {groupId}]");
return false;
}
}
}

9
Chat/Groups/Group.cs Normal file
View File

@ -0,0 +1,9 @@
namespace TwitchChatTTS.Chat.Groups
{
public class Group
{
public string Id { get; set; }
public string Name { get; set; }
public int Priority { get; set; }
}
}

View File

@ -0,0 +1,8 @@
namespace TwitchChatTTS.Chat.Groups
{
public class GroupChatter
{
public string GroupId { get; set; }
public long ChatterId { get; set;}
}
}

View File

@ -0,0 +1,15 @@
namespace TwitchChatTTS.Chat.Groups
{
public interface IChatterGroupManager
{
void Add(Group group);
void Add(long chatter, string group);
void Add(long chatter, ICollection<string> groupIds);
void Clear();
Group? Get(string groupId);
IEnumerable<string> GetGroupNamesFor(long chatter);
int GetPriorityFor(long chatter);
int GetPriorityFor(IEnumerable<string> groupIds);
bool Remove(long chatter, string groupId);
}
}

View File

@ -0,0 +1,10 @@
namespace TwitchChatTTS.Chat.Groups.Permissions
{
public class GroupPermission
{
public string Id { get; set; }
public string GroupId { get; set; }
public string Path { get; set; }
public bool? Allow { get; set; }
}
}

View File

@ -0,0 +1,145 @@
using System.Collections.ObjectModel;
using Serilog;
namespace TwitchChatTTS.Chat.Groups.Permissions
{
public class GroupPermissionManager : IGroupPermissionManager
{
private PermissionNode _root;
private ILogger _logger;
public GroupPermissionManager(ILogger logger)
{
_logger = logger;
_root = new PermissionNode(string.Empty, null, null);
}
public bool? CheckIfAllowed(string path)
{
var res = Get(path)?.Allow;
_logger.Debug($"Permission Node GET {path} = {res?.ToString() ?? "null"}");
return res;
}
public bool? CheckIfAllowed(IEnumerable<string> groups, string path) {
bool overall = false;
foreach (var group in groups) {
var result = CheckIfAllowed($"{group}.{path}");
if (result == false)
return false;
if (result == true)
overall = true;
}
return overall ? true : null;
}
public void Clear()
{
if (_root.Children != null)
_root.Children.Clear();
}
public bool Remove(string path)
{
var node = Get(path);
if (node == null || node.Parent == null)
return false;
var parts = path.Split('.');
var last = parts.Last();
if (parts.Length > 1 && parts[parts.Length - 1] == node.Parent.Name || parts.Length == 1 && node.Parent.Name == null)
{
node.Parent.Remove(last);
_logger.Debug($"Permission Node REMOVE priv {path}");
return true;
}
return false;
}
public void Set(string path, bool? allow)
{
var node = Get(path, true);
node.Allow = allow;
_logger.Debug($"Permission Node ADD {path} = {allow?.ToString() ?? "null"}");
}
private PermissionNode Get(string path, bool edit = false)
{
return Get(_root, path.ToLower(), edit);
}
private PermissionNode Get(PermissionNode node, string path, bool edit)
{
if (path.Length == 0)
return node;
var parts = path.Split('.');
var name = parts.First();
var next = node.Children?.FirstOrDefault(n => n.Name == name);
if (next == null)
{
if (!edit)
return node;
next = new PermissionNode(name, node, null);
node.Add(next);
}
return Get(next, string.Join('.', parts.Skip(1)), edit);
}
}
internal class PermissionNode
{
public string Name { get; }
public bool? Allow
{
get
{
var current = this;
while (current._allow == null && current._parent != null)
current = current._parent;
return current._allow;
}
set => _allow = value;
}
public int Priority;
internal PermissionNode? Parent { get => _parent; }
public IList<PermissionNode>? Children { get => _children == null ? null : new ReadOnlyCollection<PermissionNode>(_children); }
private bool? _allow;
private PermissionNode? _parent;
private IList<PermissionNode>? _children;
public PermissionNode(string name, PermissionNode? parent, bool? allow)
{
Name = name;
_parent = parent;
_allow = allow;
}
internal void Add(PermissionNode child)
{
if (_children == null)
_children = new List<PermissionNode>();
_children.Add(child);
}
public void Remove(string name)
{
if (_children == null || !_children.Any())
return;
for (var i = 0; i < _children.Count; i++)
{
if (_children[i].Name == name)
{
_children.RemoveAt(i);
break;
}
}
}
}
}

View File

@ -0,0 +1,11 @@
namespace TwitchChatTTS.Chat.Groups.Permissions
{
public interface IGroupPermissionManager
{
void Set(string path, bool? allow);
bool? CheckIfAllowed(string path);
bool? CheckIfAllowed(IEnumerable<string> groups, string path);
void Clear();
bool Remove(string path);
}
}

View File

@ -11,8 +11,8 @@ public class TTSPlayer
public TTSPlayer()
{
_messages = new PriorityQueue<TTSMessage, int>();
_buffer = new PriorityQueue<TTSMessage, int>();
_messages = new PriorityQueue<TTSMessage, int>(new DescendingOrder());
_buffer = new PriorityQueue<TTSMessage, int>(new DescendingOrder());
_mutex = new Mutex();
_mutex2 = new Mutex();
}
@ -104,6 +104,10 @@ public class TTSPlayer
{
return _messages.Count == 0;
}
private class DescendingOrder : IComparer<int> {
public int Compare(int x, int y) => y.CompareTo(x);
}
}
public class TTSMessage