| @@ -17,6 +17,7 @@ namespace Discord | |||||
| private readonly DiscordClient _client; | private readonly DiscordClient _client; | ||||
| private ConcurrentDictionary<string, bool> _messages; | private ConcurrentDictionary<string, bool> _messages; | ||||
| internal bool _areMembersStale; | |||||
| /// <summary> Returns the unique identifier for this channel. </summary> | /// <summary> Returns the unique identifier for this channel. </summary> | ||||
| public string Id { get; } | public string Id { get; } | ||||
| @@ -46,45 +47,17 @@ namespace Discord | |||||
| [JsonIgnore] | [JsonIgnore] | ||||
| public User Recipient => _client.Users[RecipientId]; | public User Recipient => _client.Users[RecipientId]; | ||||
| private string[] userIds; | |||||
| public IEnumerable<string> UserIds | public IEnumerable<string> UserIds | ||||
| { | { | ||||
| get | get | ||||
| { | { | ||||
| if (IsPrivate) | |||||
| return new string[] { RecipientId }; | |||||
| var server = Server; | |||||
| string everyoneId = server.EveryoneRoleId; | |||||
| //Is this channel Opt-In or Opt-Out? | |||||
| IEnumerable<PermissionOverwrite> 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<Member> Members => UserIds.Select(x => _client.Members[x, ServerId]); | public IEnumerable<Member> Members => UserIds.Select(x => _client.Members[x, ServerId]); | ||||
| @@ -1,5 +1,6 @@ | |||||
| using Newtonsoft.Json; | using Newtonsoft.Json; | ||||
| using System; | using System; | ||||
| using System.Collections.Concurrent; | |||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||
| using System.Linq; | using System.Linq; | ||||
| @@ -8,6 +9,7 @@ namespace Discord | |||||
| public class Member | public class Member | ||||
| { | { | ||||
| private readonly DiscordClient _client; | private readonly DiscordClient _client; | ||||
| private ConcurrentDictionary<string, PackedPermissions> _permissions; | |||||
| /// <summary> Returns the name of this user on this server. </summary> | /// <summary> Returns the name of this user on this server. </summary> | ||||
| public string Name { get; internal set; } | public string Name { get; internal set; } | ||||
| @@ -29,6 +31,7 @@ namespace Discord | |||||
| public string SessionId { get; internal set; } | public string SessionId { get; internal set; } | ||||
| public string Token { get; internal set; } | public string Token { get; internal set; } | ||||
| public PackedPermissions Permissions { get; internal set; } | |||||
| /// <summary> Returns the id for the game this user is currently playing. </summary> | /// <summary> Returns the id for the game this user is currently playing. </summary> | ||||
| public string GameId { get; internal set; } | public string GameId { get; internal set; } | ||||
| @@ -65,6 +68,7 @@ namespace Discord | |||||
| UserId = userId; | UserId = userId; | ||||
| ServerId = serverId; | ServerId = serverId; | ||||
| Status = UserStatus.Offline; | Status = UserStatus.Offline; | ||||
| _permissions = new ConcurrentDictionary<string, PackedPermissions>(); | |||||
| } | } | ||||
| public override string ToString() => UserId; | public override string ToString() => UserId; | ||||
| @@ -91,6 +95,8 @@ namespace Discord | |||||
| for (int i = 0; i < model.Roles.Length; i++) | for (int i = 0; i < model.Roles.Length; i++) | ||||
| newRoles[i + 1] = model.Roles[i]; | newRoles[i + 1] = model.Roles[i]; | ||||
| RoleIds = newRoles; | RoleIds = newRoles; | ||||
| UpdatePermissions(); | |||||
| } | } | ||||
| internal void Update(API.ExtendedMemberInfo model) | internal void Update(API.ExtendedMemberInfo model) | ||||
| { | { | ||||
| @@ -129,5 +135,58 @@ namespace Discord | |||||
| if (LastActivityAt == null || activity > LastActivityAt.Value) | if (LastActivityAt == null || activity > LastActivityAt.Value) | ||||
| LastActivityAt = activity ?? DateTime.UtcNow; | 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; | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| @@ -42,6 +42,9 @@ namespace Discord | |||||
| { | { | ||||
| Name = model.Name; | Name = model.Name; | ||||
| Permissions.RawValue = (uint)model.Permissions; | Permissions.RawValue = (uint)model.Permissions; | ||||
| foreach (var member in Members) | |||||
| member.UpdatePermissions(); | |||||
| } | } | ||||
| public override string ToString() => Name; | public override string ToString() => Name; | ||||
| @@ -173,10 +173,14 @@ namespace Discord | |||||
| internal void AddChannel(string channelId) | internal void AddChannel(string channelId) | ||||
| { | { | ||||
| _channels.TryAdd(channelId, true); | _channels.TryAdd(channelId, true); | ||||
| foreach (var member in Members) | |||||
| member.AddChannel(channelId); | |||||
| } | } | ||||
| internal bool RemoveChannel(string channelId) | internal bool RemoveChannel(string channelId) | ||||
| { | { | ||||
| bool ignored; | bool ignored; | ||||
| foreach (var member in Members) | |||||
| member.RemoveChannel(channelId); | |||||
| return _channels.TryRemove(channelId, out ignored); | return _channels.TryRemove(channelId, out ignored); | ||||
| } | } | ||||
| @@ -193,10 +197,14 @@ namespace Discord | |||||
| internal void AddMember(string userId) | internal void AddMember(string userId) | ||||
| { | { | ||||
| _members.TryAdd(userId, true); | _members.TryAdd(userId, true); | ||||
| } | |||||
| foreach (var channel in Channels) | |||||
| channel._areMembersStale = true; | |||||
| } | |||||
| internal bool RemoveMember(string userId) | internal bool RemoveMember(string userId) | ||||
| { | { | ||||
| bool ignored; | bool ignored; | ||||
| foreach (var channel in Channels) | |||||
| channel._areMembersStale = true; | |||||
| return _members.TryRemove(userId, out ignored); | return _members.TryRemove(userId, out ignored); | ||||
| } | } | ||||
| internal bool HasMember(string userId) | internal bool HasMember(string userId) | ||||