From 716e0ee6a4344d0c67d7ca1720d4d1b710487932 Mon Sep 17 00:00:00 2001 From: RogueException Date: Thu, 1 Oct 2015 13:12:59 -0300 Subject: [PATCH] Added permission resolving and Channel.Members caching --- src/Discord.Net/Models/Channel.cs | 41 ++++----------------- src/Discord.Net/Models/Member.cs | 59 +++++++++++++++++++++++++++++++ src/Discord.Net/Models/Role.cs | 3 ++ src/Discord.Net/Models/Server.cs | 10 +++++- 4 files changed, 78 insertions(+), 35 deletions(-) diff --git a/src/Discord.Net/Models/Channel.cs b/src/Discord.Net/Models/Channel.cs index 0e7e5a457..47b16c963 100644 --- a/src/Discord.Net/Models/Channel.cs +++ b/src/Discord.Net/Models/Channel.cs @@ -17,6 +17,7 @@ namespace Discord private readonly DiscordClient _client; private ConcurrentDictionary _messages; + internal bool _areMembersStale; /// Returns the unique identifier for this channel. public string Id { get; } @@ -46,45 +47,17 @@ namespace Discord [JsonIgnore] public User Recipient => _client.Users[RecipientId]; + private string[] userIds; public IEnumerable UserIds { get { - if (IsPrivate) - return new string[] { RecipientId }; - - var server = Server; - string everyoneId = server.EveryoneRoleId; - - //Is this channel Opt-In or Opt-Out? - IEnumerable everyones = PermissionOverwrites.Where(x => x.Type == PermissionTarget.Role && x.Id == server.EveryoneRoleId); - bool isOptIn = everyones.Any(x => x.Deny.Text_ReadMessages) && !everyones.Any(x => x.Allow.Text_ReadMessages); + if (!_areMembersStale) + return userIds; - var denyMembers = PermissionOverwrites - .Where(x => x.Deny.Text_ReadMessages && x.Type == PermissionTarget.Member) - .Select(x => x.Id); - var allowRoles = PermissionOverwrites - .Where(x => x.Allow.Text_ReadMessages && x.Type == PermissionTarget.Role && x.Id != server.EveryoneRoleId) - .SelectMany(x => _client.Roles[x.Id].MemberIds); - var allowMembers = PermissionOverwrites - .Where(x => x.Allow.Text_ReadMessages && x.Type == PermissionTarget.Member) - .Select(x => x.Id); - - if (isOptIn) - { - //AllowRole -> DenyMember -> AllowMember -> AllowOwner - return allowRoles.Except(denyMembers).Concat(allowMembers).Concat(new string[] { server.OwnerId }).Distinct(); - } - else - { - var denyRoles = PermissionOverwrites - .Where(x => x.Deny.Text_ReadMessages && x.Type == PermissionTarget.Role && x.Id != server.EveryoneRoleId) - .SelectMany(x => _client.Roles[x.Id].MemberIds); - - //DenyRole -> AllowRole -> DenyMember -> AllowMember -> AllowOwner - var optOut = denyRoles.Except(allowRoles).Concat(denyMembers).Except(allowMembers).Except(new string[] { server.OwnerId }); - return Server.UserIds.Except(optOut); - } + _areMembersStale = false; + userIds = Members.Where(x => x.Permissions.Text_ReadMessages).Select(x => x.UserId).ToArray(); + return userIds; } } public IEnumerable Members => UserIds.Select(x => _client.Members[x, ServerId]); diff --git a/src/Discord.Net/Models/Member.cs b/src/Discord.Net/Models/Member.cs index 1cf8c9e93..7e98622a7 100644 --- a/src/Discord.Net/Models/Member.cs +++ b/src/Discord.Net/Models/Member.cs @@ -1,5 +1,6 @@ using Newtonsoft.Json; using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; @@ -8,6 +9,7 @@ namespace Discord public class Member { private readonly DiscordClient _client; + private ConcurrentDictionary _permissions; /// Returns the name of this user on this server. public string Name { get; internal set; } @@ -29,6 +31,7 @@ namespace Discord public string SessionId { get; internal set; } public string Token { get; internal set; } + public PackedPermissions Permissions { get; internal set; } /// Returns the id for the game this user is currently playing. public string GameId { get; internal set; } @@ -65,6 +68,7 @@ namespace Discord UserId = userId; ServerId = serverId; Status = UserStatus.Offline; + _permissions = new ConcurrentDictionary(); } public override string ToString() => UserId; @@ -91,6 +95,8 @@ namespace Discord for (int i = 0; i < model.Roles.Length; i++) newRoles[i + 1] = model.Roles[i]; RoleIds = newRoles; + + UpdatePermissions(); } internal void Update(API.ExtendedMemberInfo model) { @@ -129,5 +135,58 @@ namespace Discord if (LastActivityAt == null || activity > LastActivityAt.Value) LastActivityAt = activity ?? DateTime.UtcNow; } + + internal void AddChannel(string channelId) + { + _permissions.TryAdd(channelId, new PackedPermissions()); + UpdatePermissions(channelId); + } + internal bool RemoveChannel(string channelId) + { + PackedPermissions ignored; + return _permissions.TryRemove(channelId, out ignored); + } + internal void UpdatePermissions() + { + foreach (var channel in _permissions) + UpdatePermissions(channel.Key); + } + internal void UpdatePermissions(string channelId) + { + var server = Server; + if (server == null) return; + var channel = _client.Channels[channelId]; + if (channel == null) return; + var serverOverwrites = channel.PermissionOverwrites; + var channelOverwrites = channel.PermissionOverwrites; + + PackedPermissions permissions; + if (!_permissions.TryGetValue(channelId, out permissions)) return; + uint newPermissions = 0x0; + + foreach (var serverRole in Roles) + newPermissions |= serverRole.Permissions.RawValue; + foreach (var denyRole in channelOverwrites.Where(x => x.Type == PermissionTarget.Role && x.Deny.RawValue != 0 && RoleIds.Contains(x.Id))) + newPermissions &= ~denyRole.Deny.RawValue; + foreach (var allowRole in channelOverwrites.Where(x => x.Type == PermissionTarget.Role && x.Allow.RawValue != 0 && RoleIds.Contains(x.Id))) + newPermissions |= allowRole.Allow.RawValue; + foreach (var denyMembers in channelOverwrites.Where(x => x.Type == PermissionTarget.Member && x.Id == UserId && x.Deny.RawValue != 0)) + newPermissions &= ~denyMembers.Deny.RawValue; + foreach (var allowMembers in channelOverwrites.Where(x => x.Type == PermissionTarget.Member && x.Id == UserId && x.Allow.RawValue != 0)) + newPermissions |= allowMembers.Allow.RawValue; + + if (permissions.RawValue != newPermissions) + { + permissions.RawValue = newPermissions; + channel._areMembersStale = true; + } + } + public PackedPermissions GetPermissions(string channelId) + { + PackedPermissions perms; + if (_permissions.TryGetValue(channelId, out perms)) + return perms; + return null; + } } } diff --git a/src/Discord.Net/Models/Role.cs b/src/Discord.Net/Models/Role.cs index 327a2ec06..345da5773 100644 --- a/src/Discord.Net/Models/Role.cs +++ b/src/Discord.Net/Models/Role.cs @@ -42,6 +42,9 @@ namespace Discord { Name = model.Name; Permissions.RawValue = (uint)model.Permissions; + + foreach (var member in Members) + member.UpdatePermissions(); } public override string ToString() => Name; diff --git a/src/Discord.Net/Models/Server.cs b/src/Discord.Net/Models/Server.cs index aefc7798e..636d813ed 100644 --- a/src/Discord.Net/Models/Server.cs +++ b/src/Discord.Net/Models/Server.cs @@ -173,10 +173,14 @@ namespace Discord internal void AddChannel(string channelId) { _channels.TryAdd(channelId, true); + foreach (var member in Members) + member.AddChannel(channelId); } internal bool RemoveChannel(string channelId) { bool ignored; + foreach (var member in Members) + member.RemoveChannel(channelId); return _channels.TryRemove(channelId, out ignored); } @@ -193,10 +197,14 @@ namespace Discord internal void AddMember(string userId) { _members.TryAdd(userId, true); - } + foreach (var channel in Channels) + channel._areMembersStale = true; + } internal bool RemoveMember(string userId) { bool ignored; + foreach (var channel in Channels) + channel._areMembersStale = true; return _members.TryRemove(userId, out ignored); } internal bool HasMember(string userId)