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