From 01f299e0308d731e51c11b36adb0de4cbf6e4184 Mon Sep 17 00:00:00 2001 From: RogueException Date: Sat, 24 Oct 2015 22:16:38 -0300 Subject: [PATCH] Started rename of member -> user, added server permission resolving, added channel message cache options. --- .../CommandsPlugin.Events.cs | 2 +- src/Discord.Net.Commands/CommandsPlugin.cs | 4 +- src/Discord.Net.Net45/Discord.Net.csproj | 3 + src/Discord.Net/DiscordClient.Bans.cs | 4 +- src/Discord.Net/DiscordClient.Channels.cs | 2 +- src/Discord.Net/DiscordClient.Members.cs | 36 +++--- src/Discord.Net/DiscordClient.Messages.cs | 48 ++++---- src/Discord.Net/DiscordClient.Permissions.cs | 12 +- src/Discord.Net/DiscordClient.Users.cs | 10 +- src/Discord.Net/DiscordClient.cs | 9 +- src/Discord.Net/DiscordClientConfig.cs | 3 + src/Discord.Net/Helpers/BitHelper.cs | 20 ++++ src/Discord.Net/Helpers/Mention.cs | 2 +- src/Discord.Net/Models/Channel.cs | 103 +++++++++--------- src/Discord.Net/Models/GlobalUser.cs | 2 +- src/Discord.Net/Models/Invite.cs | 2 +- src/Discord.Net/Models/Message.cs | 8 +- src/Discord.Net/Models/Permissions.cs | 10 +- src/Discord.Net/Models/Role.cs | 4 +- src/Discord.Net/Models/Server.cs | 16 +-- src/Discord.Net/Models/User.cs | 79 +++++++++----- 21 files changed, 219 insertions(+), 160 deletions(-) create mode 100644 src/Discord.Net/Helpers/BitHelper.cs diff --git a/src/Discord.Net.Commands/CommandsPlugin.Events.cs b/src/Discord.Net.Commands/CommandsPlugin.Events.cs index 30d6c8e1c..df2575cbf 100644 --- a/src/Discord.Net.Commands/CommandsPlugin.Events.cs +++ b/src/Discord.Net.Commands/CommandsPlugin.Events.cs @@ -12,7 +12,7 @@ namespace Discord.Commands public int? Permissions { get; } public string[] Args { get; } - public Member Member => Message.Member; + public User Member => Message.Member; public Channel Channel => Message.Channel; public Server Server => Message.Channel.Server; diff --git a/src/Discord.Net.Commands/CommandsPlugin.cs b/src/Discord.Net.Commands/CommandsPlugin.cs index 0da770ab2..6a0986cf8 100644 --- a/src/Discord.Net.Commands/CommandsPlugin.cs +++ b/src/Discord.Net.Commands/CommandsPlugin.cs @@ -8,7 +8,7 @@ namespace Discord.Commands { private readonly DiscordClient _client; private List _commands; - private Func _getPermissions; + private Func _getPermissions; public IEnumerable Commands => _commands; @@ -17,7 +17,7 @@ namespace Discord.Commands public bool RequireCommandCharInPublic { get; set; } public bool RequireCommandCharInPrivate { get; set; } - public CommandsPlugin(DiscordClient client, Func getPermissions = null) + public CommandsPlugin(DiscordClient client, Func getPermissions = null) { _client = client; _getPermissions = getPermissions; diff --git a/src/Discord.Net.Net45/Discord.Net.csproj b/src/Discord.Net.Net45/Discord.Net.csproj index cea87d328..6ad222b82 100644 --- a/src/Discord.Net.Net45/Discord.Net.csproj +++ b/src/Discord.Net.Net45/Discord.Net.csproj @@ -208,6 +208,9 @@ Helpers\AsyncCollection.cs + + Helpers\BitHelper.cs + Helpers\EpochTime.cs diff --git a/src/Discord.Net/DiscordClient.Bans.cs b/src/Discord.Net/DiscordClient.Bans.cs index b9fa351aa..2fd5df60a 100644 --- a/src/Discord.Net/DiscordClient.Bans.cs +++ b/src/Discord.Net/DiscordClient.Bans.cs @@ -33,7 +33,7 @@ namespace Discord } /// Bans a user from the provided server. - public Task Ban(Member member) + public Task Ban(User member) { if (member == null) throw new ArgumentNullException(nameof(member)); CheckReady(); @@ -42,7 +42,7 @@ namespace Discord } /// Unbans a user from the provided server. - public async Task Unban(Member member) + public async Task Unban(User member) { if (member == null) throw new ArgumentNullException(nameof(member)); CheckReady(); diff --git a/src/Discord.Net/DiscordClient.Channels.cs b/src/Discord.Net/DiscordClient.Channels.cs index 541182141..6ad7068bf 100644 --- a/src/Discord.Net/DiscordClient.Channels.cs +++ b/src/Discord.Net/DiscordClient.Channels.cs @@ -95,7 +95,7 @@ namespace Discord } /// Returns the private channel with the provided user, creating one if it does not currently exist. - public async Task CreatePMChannel(Member member) + public async Task CreatePMChannel(User member) { if (member == null) throw new ArgumentNullException(nameof(member)); CheckReady(); diff --git a/src/Discord.Net/DiscordClient.Members.cs b/src/Discord.Net/DiscordClient.Members.cs index 66edc1c3b..76350100b 100644 --- a/src/Discord.Net/DiscordClient.Members.cs +++ b/src/Discord.Net/DiscordClient.Members.cs @@ -5,36 +5,36 @@ using System.Threading.Tasks; namespace Discord { - internal sealed class Members : AsyncCollection + internal sealed class Members : AsyncCollection { public Members(DiscordClient client, object writerLock) : base(client, writerLock, x => x.OnCached(), x => x.OnUncached()) { } private string GetKey(string userId, string serverId) => serverId + '_' + userId; - public Member this[string userId, string serverId] + public User this[string userId, string serverId] => this[GetKey(userId, serverId)]; - public Member GetOrAdd(string userId, string serverId) - => GetOrAdd(GetKey(userId, serverId), () => new Member(_client, userId, serverId)); - public Member TryRemove(string userId, string serverId) + public User GetOrAdd(string userId, string serverId) + => GetOrAdd(GetKey(userId, serverId), () => new User(_client, userId, serverId)); + public User TryRemove(string userId, string serverId) => TryRemove(GetKey(userId, serverId)); } public class MemberEventArgs : EventArgs { - public Member Member { get; } + public User Member { get; } public string UserId => Member.Id; public Server Server => Member.Server; public string ServerId => Member.ServerId; - internal MemberEventArgs(Member member) { Member = member; } + internal MemberEventArgs(User member) { Member = member; } } public class MemberChannelEventArgs : MemberEventArgs { public Channel Channel { get; } public string ChannelId => Channel.Id; - internal MemberChannelEventArgs(Member member, Channel channel) + internal MemberChannelEventArgs(User member, Channel channel) : base(member) { Channel = channel; @@ -44,7 +44,7 @@ namespace Discord { public bool IsSpeaking { get; } - internal MemberIsSpeakingEventArgs(Member member, Channel channel, bool isSpeaking) + internal MemberIsSpeakingEventArgs(User member, Channel channel, bool isSpeaking) : base(member, channel) { IsSpeaking = isSpeaking; @@ -54,25 +54,25 @@ namespace Discord public partial class DiscordClient { public event EventHandler UserIsTyping; - private void RaiseUserIsTyping(Member member, Channel channel) + private void RaiseUserIsTyping(User member, Channel channel) { if (UserIsTyping != null) RaiseEvent(nameof(UserIsTyping), () => UserIsTyping(this, new MemberChannelEventArgs(member, channel))); } public event EventHandler UserIsSpeaking; - private void RaiseUserIsSpeaking(Member member, Channel channel, bool isSpeaking) + private void RaiseUserIsSpeaking(User member, Channel channel, bool isSpeaking) { if (UserIsSpeaking != null) RaiseEvent(nameof(UserIsSpeaking), () => UserIsSpeaking(this, new MemberIsSpeakingEventArgs(member, channel, isSpeaking))); } - private Member _currentUser; + private User _currentUser; internal Members Members => _members; private readonly Members _members; /// Returns the user with the specified id, along with their server-specific data, or null if none was found. - public Member GetMember(Server server, string userId) + public User GetMember(Server server, string userId) { if (server == null) throw new ArgumentNullException(nameof(server)); if (userId == null) throw new ArgumentNullException(nameof(userId)); @@ -82,26 +82,26 @@ namespace Discord } /// Returns the user with the specified name and discriminator, along withtheir server-specific data, or null if they couldn't be found. /// Name formats supported: Name and @Name. Search is case-insensitive. - public Member GetMember(Server server, string username, string discriminator) + public User GetMember(Server server, string username, string discriminator) { if (server == null) throw new ArgumentNullException(nameof(server)); if (username == null) throw new ArgumentNullException(nameof(username)); if (discriminator == null) throw new ArgumentNullException(nameof(discriminator)); CheckReady(); - Member member = FindMembers(server, username, discriminator, true).FirstOrDefault(); + User member = FindMembers(server, username, discriminator, true).FirstOrDefault(); return _members[member?.Id, server.Id]; } /// Returns all users in with the specified server and name, along with their server-specific data. /// Name formats supported: Name and @Name. Search is case-insensitive. - public IEnumerable FindMembers(Server server, string name, string discriminator = null, bool exactMatch = false) + public IEnumerable FindMembers(Server server, string name, string discriminator = null, bool exactMatch = false) { if (server == null) throw new ArgumentNullException(nameof(server)); if (name == null) throw new ArgumentNullException(nameof(name)); CheckReady(); - IEnumerable query; + IEnumerable query; if (!exactMatch && name.StartsWith("@")) { string name2 = name.Substring(1); @@ -118,7 +118,7 @@ namespace Discord return query; } - public Task EditMember(Member member, bool? mute = null, bool? deaf = null, IEnumerable roles = null) + public Task EditMember(User member, bool? mute = null, bool? deaf = null, IEnumerable roles = null) { if (member == null) throw new ArgumentNullException(nameof(member)); CheckReady(); diff --git a/src/Discord.Net/DiscordClient.Messages.cs b/src/Discord.Net/DiscordClient.Messages.cs index e97ef97c9..79ccd7400 100644 --- a/src/Discord.Net/DiscordClient.Messages.cs +++ b/src/Discord.Net/DiscordClient.Messages.cs @@ -20,7 +20,7 @@ namespace Discord public class MessageEventArgs : EventArgs { public Message Message { get; } - public Member Member => Message.Member; + public User Member => Message.Member; public Channel Channel => Message.Channel; public Server Server => Message.Server; @@ -87,7 +87,7 @@ namespace Discord return SendMessage(channel, text, false); } /// Sends a private message to the provided user. - public async Task SendPrivateMessage(Member member, string text) + public async Task SendPrivateMessage(User member, string text) { if (member == null) throw new ArgumentNullException(nameof(member)); if (text == null) throw new ArgumentNullException(nameof(text)); @@ -103,8 +103,11 @@ namespace Discord if (Config.UseMessageQueue) { var nonce = GenerateNonce(); - msg = _messages.GetOrAdd("nonce_" + nonce, channel.Id, _userId); - var currentUser = msg.Member; + if (_messages != null) + msg = _messages.GetOrAdd("nonce_" + nonce, channel.Id, _userId); + else + msg = new Message(this, "nonce_" + nonce, channel.Id, _userId); + var currentUser = msg.Member; msg.Update(new MessageInfo { Content = text, @@ -121,7 +124,10 @@ namespace Discord else { var model = await _api.SendMessage(channel.Id, text, userIds, null, isTextToSpeech).ConfigureAwait(false); - msg = _messages.GetOrAdd(model.Id, channel.Id, model.Author.Id); + if (_messages != null) + msg = _messages.GetOrAdd(model.Id, channel.Id, model.Author.Id); + else + msg = new Message(this, model.Id, channel.Id, _userId); msg.Update(model); RaiseMessageSent(msg); } @@ -197,27 +203,29 @@ namespace Discord var msgs = await _api.GetMessages(channel.Id, count).ConfigureAwait(false); return msgs.Select(x => { - Message msg; - if (cache) - msg = _messages.GetOrAdd(x.Id, x.ChannelId, x.Author.Id); - else - msg = _messages[x.Id] ?? new Message(this, x.Id, x.ChannelId, x.Author.Id); - if (msg != null) + Message msg = null; + if (_messages != null) { - msg.Update(x); - if (Config.TrackActivity) + if (cache && _messages != null) + msg = _messages.GetOrAdd(x.Id, x.ChannelId, x.Author.Id); + else + msg = _messages[x.Id]; + } + if (msg == null) + msg = new Message(this, x.Id, x.ChannelId, x.Author.Id); + msg.Update(x); + if (Config.TrackActivity) + { + if (!channel.IsPrivate) { - if (!channel.IsPrivate) - { - var member = msg.Member; - if (member != null) - member.UpdateActivity(msg.EditedTimestamp ?? msg.Timestamp); - } + var member = msg.Member; + if (member != null) + member.UpdateActivity(msg.EditedTimestamp ?? msg.Timestamp); } } return msg; }) - .ToArray(); + .ToArray(); } catch (HttpException) { } //Bad Permissions? } diff --git a/src/Discord.Net/DiscordClient.Permissions.cs b/src/Discord.Net/DiscordClient.Permissions.cs index 29010c607..687308302 100644 --- a/src/Discord.Net/DiscordClient.Permissions.cs +++ b/src/Discord.Net/DiscordClient.Permissions.cs @@ -7,7 +7,7 @@ namespace Discord { public partial class DiscordClient { - public Task SetChannelUserPermissions(Channel channel, Member member, ChannelPermissions allow = null, ChannelPermissions deny = null) + public Task SetChannelUserPermissions(Channel channel, User member, ChannelPermissions allow = null, ChannelPermissions deny = null) { if (channel == null) throw new ArgumentNullException(nameof(channel)); if (member == null) throw new ArgumentNullException(nameof(member)); @@ -15,7 +15,7 @@ namespace Discord return SetChannelPermissions(channel, member?.Id, PermissionTarget.Member, allow, deny); } - public Task SetChannelUserPermissions(Channel channel, Member member, DualChannelPermissions permissions = null) + public Task SetChannelUserPermissions(Channel channel, User member, DualChannelPermissions permissions = null) { if (channel == null) throw new ArgumentNullException(nameof(channel)); if (member == null) throw new ArgumentNullException(nameof(member)); @@ -56,11 +56,11 @@ namespace Discord } else { - var oldPerms = channel._permissionOverwrites; + var oldPerms = channel.PermissionOverwrites.ToArray(); var newPerms = new Channel.PermissionOverwrite[oldPerms.Length + 1]; Array.Copy(oldPerms, newPerms, oldPerms.Length); newPerms[oldPerms.Length] = new Channel.PermissionOverwrite(targetType, targetId, allowValue, denyValue); - channel._permissionOverwrites = newPerms; + channel.PermissionOverwrites = newPerms; } changed = true; } @@ -71,7 +71,7 @@ namespace Discord await _api.DeleteChannelPermissions(channel.Id, targetId); if (perms != null) { - channel._permissionOverwrites = channel.PermissionOverwrites.Where(x => x.TargetType != targetType || x.TargetId != targetId).ToArray(); + channel.PermissionOverwrites = channel.PermissionOverwrites.Where(x => x.TargetType != targetType || x.TargetId != targetId).ToArray(); changed = true; } } @@ -87,7 +87,7 @@ namespace Discord } } - public Task RemoveChannelUserPermissions(Channel channel, Member member) + public Task RemoveChannelUserPermissions(Channel channel, User member) { if (channel == null) throw new ArgumentNullException(nameof(channel)); if (member == null) throw new ArgumentNullException(nameof(member)); diff --git a/src/Discord.Net/DiscordClient.Users.cs b/src/Discord.Net/DiscordClient.Users.cs index 3daa0592e..7033f2b82 100644 --- a/src/Discord.Net/DiscordClient.Users.cs +++ b/src/Discord.Net/DiscordClient.Users.cs @@ -17,13 +17,13 @@ namespace Discord public partial class DiscordClient { public event EventHandler UserAdded; - private void RaiseUserAdded(Member member) + private void RaiseUserAdded(User member) { if (UserAdded != null) RaiseEvent(nameof(UserAdded), () => UserAdded(this, new MemberEventArgs(member))); } public event EventHandler UserRemoved; - private void RaiseUserRemoved(Member member) + private void RaiseUserRemoved(User member) { if (UserRemoved != null) RaiseEvent(nameof(UserRemoved), () => UserRemoved(this, new MemberEventArgs(member))); @@ -35,19 +35,19 @@ namespace Discord RaiseEvent(nameof(ProfileUpdated), () => ProfileUpdated(this, EventArgs.Empty)); } public event EventHandler MemberUpdated; - private void RaiseMemberUpdated(Member member) + private void RaiseMemberUpdated(User member) { if (MemberUpdated != null) RaiseEvent(nameof(MemberUpdated), () => MemberUpdated(this, new MemberEventArgs(member))); } public event EventHandler UserPresenceUpdated; - private void RaiseUserPresenceUpdated(Member member) + private void RaiseUserPresenceUpdated(User member) { if (UserPresenceUpdated != null) RaiseEvent(nameof(UserPresenceUpdated), () => UserPresenceUpdated(this, new MemberEventArgs(member))); } public event EventHandler UserVoiceStateUpdated; - private void RaiseUserVoiceStateUpdated(Member member) + private void RaiseUserVoiceStateUpdated(User member) { if (UserVoiceStateUpdated != null) RaiseEvent(nameof(UserVoiceStateUpdated), () => UserVoiceStateUpdated(this, new MemberEventArgs(member))); diff --git a/src/Discord.Net/DiscordClient.cs b/src/Discord.Net/DiscordClient.cs index cf2b219c9..bd9c7fee0 100644 --- a/src/Discord.Net/DiscordClient.cs +++ b/src/Discord.Net/DiscordClient.cs @@ -28,7 +28,7 @@ namespace Discord public DiscordAPIClient API => _api; /// Returns the current logged-in user. - public Member CurrentUser => _currentUser; + public User CurrentUser => _currentUser; /// Initializes a new instance of the DiscordClient class. public DiscordClient(DiscordClientConfig config = null) @@ -495,7 +495,12 @@ namespace Discord } if (msg == null) - msg = _messages.GetOrAdd(data.Id, data.ChannelId, data.Author.Id); + { + if (_messages != null) + msg = _messages.GetOrAdd(data.Id, data.ChannelId, data.Author.Id); + else + msg = new Message(this, data.Id, data.ChannelId, data.Author.Id); + } msg.Update(data); if (Config.TrackActivity) { diff --git a/src/Discord.Net/DiscordClientConfig.cs b/src/Discord.Net/DiscordClientConfig.cs index 9c81fd5c8..d341f8508 100644 --- a/src/Discord.Net/DiscordClientConfig.cs +++ b/src/Discord.Net/DiscordClientConfig.cs @@ -5,6 +5,9 @@ /// Gets or sets the time (in milliseconds) to wait when the message queue is empty before checking again. public int MessageQueueInterval { get { return _messageQueueInterval; } set { SetValue(ref _messageQueueInterval, value); } } private int _messageQueueInterval = 100; + /// Gets or sets the number of messages per channel that should be kept in cache. Setting this to zero disables the message cache entirely. + public int MessageCacheLength { get { return _messageCacheLength; } set { SetValue(ref _messageCacheLength, value); } } + private int _messageCacheLength = 100; //Experimental Features /// (Experimental) Enables the client to be simultaneously connected to multiple channels at once (Discord still limits you to one channel per server). diff --git a/src/Discord.Net/Helpers/BitHelper.cs b/src/Discord.Net/Helpers/BitHelper.cs new file mode 100644 index 000000000..471cec59a --- /dev/null +++ b/src/Discord.Net/Helpers/BitHelper.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Discord +{ + internal static class BitHelper + { + public static bool GetBit(uint value, int pos) => ((value >> (byte)pos) & 1U) == 1; + public static void SetBit(ref uint value, int pos, bool bitValue) + { + if (bitValue) + value |= (1U << pos); + else + value &= ~(1U << pos); + } + } +} diff --git a/src/Discord.Net/Helpers/Mention.cs b/src/Discord.Net/Helpers/Mention.cs index 6288ab2f0..13e239a03 100644 --- a/src/Discord.Net/Helpers/Mention.cs +++ b/src/Discord.Net/Helpers/Mention.cs @@ -10,7 +10,7 @@ namespace Discord private static readonly Regex _channelRegex = new Regex(@"<#(\d+?)>", RegexOptions.Compiled); /// Returns the string used to create a user mention. - public static string User(Member member) + public static string User(User member) => $"<@{member.Id}>"; /// Returns the string used to create a channel mention. public static string Channel(Channel channel) diff --git a/src/Discord.Net/Models/Channel.cs b/src/Discord.Net/Models/Channel.cs index 434b6de50..80bdd94b4 100644 --- a/src/Discord.Net/Models/Channel.cs +++ b/src/Discord.Net/Models/Channel.cs @@ -1,6 +1,5 @@ using Discord.API; using Newtonsoft.Json; -using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; @@ -26,14 +25,7 @@ namespace Discord Deny.Lock(); } } - - private readonly ConcurrentDictionary _messages; - private bool _areMembersStale; - - private readonly string _serverId, _recipientId; - private Server _server; - private Member _recipient; - + /// Returns the name of this channel. public string Name { get; private set; } /// Returns the topic associated with this channel. @@ -47,41 +39,40 @@ namespace Discord /// Returns the server containing this channel. [JsonIgnore] - public Server Server => _client.Servers[_serverId]; - + public Server Server { get; private set; } + private readonly string _serverId; + /// For private chats, returns the target user, otherwise null. [JsonIgnore] - public Member Recipient => _client.Members[_recipientId, _serverId]; - - /// Returns a collection of the IDs of all users with read access to this channel. - public IEnumerable UserIds + public User Recipient { get; private set; } + private readonly string _recipientId; + + /// Returns a collection of all users with read access to this channel. + [JsonIgnore] + public IEnumerable Members { get { if (!_areMembersStale) - return _userIds; - - _userIds = Server.Members.Where(x => x.GetPermissions(this)?.ReadMessages ?? false).Select(x => x.Id).ToArray(); + return _members; + + _members = Server.Members.Where(x => x.GetPermissions(this)?.ReadMessages ?? false).ToArray(); _areMembersStale = false; - return _userIds; - } + return _members; + } } - private string[] _userIds; - /// Returns a collection of all users with read access to this channel. - [JsonIgnore] - public IEnumerable Members => UserIds.Select(x => _client.Members[x, _serverId]); + private User[] _members; + private bool _areMembersStale; - /// Returns a collection of the ids of all messages the client has seen posted in this channel. This collection does not guarantee any ordering. - [JsonIgnore] - public IEnumerable MessageIds => _messages.Select(x => x.Key); /// Returns a collection of all messages the client has seen posted in this channel. This collection does not guarantee any ordering. [JsonIgnore] - public IEnumerable Messages => _messages.Select(x => _client.Messages[x.Key]); + public IEnumerable Messages => _messages.Values; + private readonly ConcurrentDictionary _messages; /// Returns a collection of all custom permissions used for this channel. private static readonly PermissionOverwrite[] _initialPermissionsOverwrites = new PermissionOverwrite[0]; - internal PermissionOverwrite[] _permissionOverwrites; - public IEnumerable PermissionOverwrites => _permissionOverwrites; + private PermissionOverwrite[] _permissionOverwrites; + public IEnumerable PermissionOverwrites { get { return _permissionOverwrites; } internal set { _permissionOverwrites = value.ToArray(); } } internal Channel(DiscordClient client, string id, string serverId, string recipientId) : base(client, id) @@ -92,31 +83,35 @@ namespace Discord _areMembersStale = true; //Local Cache - _messages = new ConcurrentDictionary(); + _messages = new ConcurrentDictionary(); } internal override void OnCached() { if (IsPrivate) { - _recipient = _client.Members[_recipientId, _serverId]; - Name = "@" + _recipient.Name; + var recipient = _client.Members[_recipientId, _serverId]; + Name = "@" + recipient.Name; + Recipient = recipient; } else { - _server = _client.Servers[_serverId]; - _server.AddChannel(this); + var server = _client.Servers[_serverId]; + server.AddChannel(this); + Server = server; } } internal override void OnUncached() { - if (_server != null) - _server.RemoveChannel(this); - _server = null; - - if (_recipient != null) - _recipient.GlobalUser.PrivateChannel = null; - _recipient = null; + var server = Server; + if (server != null) + server.RemoveChannel(this); + Server = null; + + var recipient = Recipient; + if (recipient != null) + recipient.GlobalUser.PrivateChannel = null; + Recipient = recipient; } internal void Update(ChannelReference model) @@ -146,15 +141,21 @@ namespace Discord public override string ToString() => Name; - internal void AddMessage(string messageId) - { - _messages.TryAdd(messageId, true); - } - internal bool RemoveMessage(string messageId) + internal void AddMessage(Message message) { - bool ignored; - return _messages.TryRemove(messageId, out ignored); + var cacheLength = _client.Config.MessageCacheLength; + if (cacheLength > 0) + { + while (_messages.Count > cacheLength - 1) + { + var oldest = _messages.Select(x => x.Value.Id).OrderBy(x => x).FirstOrDefault(); + if (oldest != null) + _client.Messages.TryRemove(oldest); + } + _messages.TryAdd(message.Id, message); + } } + internal void RemoveMessage(Message message) => _messages.TryRemove(message.Id, out message); internal void InvalidMembersCache() { @@ -164,14 +165,14 @@ namespace Discord { _areMembersStale = true; foreach (var member in Members) - member.UpdatePermissions(this); + member.UpdateChannelPermissions(this); } internal void InvalidatePermissionsCache(string userId) { _areMembersStale = true; var member = _client.Members[userId, _serverId]; if (member != null) - member.UpdatePermissions(this); + member.UpdateChannelPermissions(this); } } } diff --git a/src/Discord.Net/Models/GlobalUser.cs b/src/Discord.Net/Models/GlobalUser.cs index f025fe85c..65cc7e543 100644 --- a/src/Discord.Net/Models/GlobalUser.cs +++ b/src/Discord.Net/Models/GlobalUser.cs @@ -27,7 +27,7 @@ namespace Discord /// Returns a collection of all server-specific data for every server this user is a member of. [JsonIgnore] - public IEnumerable Memberships => _servers.Select(x => _client.Members[Id, x.Key]); + public IEnumerable Memberships => _servers.Select(x => _client.Members[Id, x.Key]); /// Returns a collection of all servers this user is a member of. [JsonIgnore] public IEnumerable Servers => _servers.Select(x => _client.Servers[x.Key]); diff --git a/src/Discord.Net/Models/Invite.cs b/src/Discord.Net/Models/Invite.cs index 27c1817c2..cc3e38444 100644 --- a/src/Discord.Net/Models/Invite.cs +++ b/src/Discord.Net/Models/Invite.cs @@ -27,7 +27,7 @@ namespace Discord /// Returns the user that created this invite. [JsonIgnore] - public Member Inviter => _client.Members[_inviterId, _serverId]; + public User Inviter => _client.Members[_inviterId, _serverId]; /// Returns the server this invite is to. [JsonIgnore] diff --git a/src/Discord.Net/Models/Message.cs b/src/Discord.Net/Models/Message.cs index 158278885..094d1494f 100644 --- a/src/Discord.Net/Models/Message.cs +++ b/src/Discord.Net/Models/Message.cs @@ -129,7 +129,7 @@ namespace Discord public string[] MentionIds { get; private set; } /// Returns a collection of all users mentioned in this message. [JsonIgnore] - public IEnumerable Mentions { get; internal set; } + public IEnumerable Mentions { get; internal set; } /// Returns the server containing the channel this message was sent to. [JsonIgnore] @@ -144,7 +144,7 @@ namespace Discord public string UserId { get; } /// Returns the author of this message. [JsonIgnore] - public Member Member => _client.Members[_userId, Channel.Server.Id]; + public User Member => _client.Members[_userId, Channel.Server.Id]; internal Message(DiscordClient client, string id, string channelId, string userId) : base(client, id) @@ -158,14 +158,14 @@ namespace Discord internal override void OnCached() { var channel = _client.Channels[_channelId]; - channel.AddMessage(Id); + channel.AddMessage(this); Channel = channel; } internal override void OnUncached() { var channel = Channel; if (channel != null) - channel.RemoveMessage(Id); + channel.RemoveMessage(this); } internal void Update(MessageInfo model) diff --git a/src/Discord.Net/Models/Permissions.cs b/src/Discord.Net/Models/Permissions.cs index dd7b29a53..bd65436e1 100644 --- a/src/Discord.Net/Models/Permissions.cs +++ b/src/Discord.Net/Models/Permissions.cs @@ -127,15 +127,9 @@ namespace Discord _rawValue = rawValue; } - internal bool GetBit(PermissionsBits pos) => ((_rawValue >> (byte)pos) & 1U) == 1; + internal bool GetBit(PermissionsBits pos) => BitHelper.GetBit(_rawValue, (int)pos); internal void SetBit(PermissionsBits pos, bool value) { CheckLock(); SetBitInternal((byte)pos, value); } - internal void SetBitInternal(int pos, bool value) - { - if (value) - _rawValue |= (1U << pos); - else - _rawValue &= ~(1U << pos); - } + internal void SetBitInternal(int pos, bool value) => BitHelper.SetBit(ref _rawValue, pos, value); internal void Lock() => _isLocked = true; protected void CheckLock() diff --git a/src/Discord.Net/Models/Role.cs b/src/Discord.Net/Models/Role.cs index ac2861031..0d0ff9835 100644 --- a/src/Discord.Net/Models/Role.cs +++ b/src/Discord.Net/Models/Role.cs @@ -31,7 +31,7 @@ namespace Discord public bool IsEveryone => Id == _serverId; /// Returns a list of all members in this role. [JsonIgnore] - public IEnumerable Members => IsEveryone ? Server.Members : Server.Members.Where(x => x.HasRole(this)); + public IEnumerable Members => IsEveryone ? Server.Members : Server.Members.Where(x => x.HasRole(this)); internal Role(DiscordClient client, string id, string serverId) : base(client, id) @@ -75,7 +75,7 @@ namespace Discord Permissions.SetRawValueInternal(model.Permissions.Value); foreach (var member in Members) - member.UpdatePermissions(); + member.UpdateServerPermissions(); } public override string ToString() => Name; diff --git a/src/Discord.Net/Models/Server.cs b/src/Discord.Net/Models/Server.cs index 168f55e90..822ca6ff6 100644 --- a/src/Discord.Net/Models/Server.cs +++ b/src/Discord.Net/Models/Server.cs @@ -11,7 +11,7 @@ namespace Discord { private readonly ConcurrentDictionary _bans; private readonly ConcurrentDictionary _channels; - private readonly ConcurrentDictionary _members; + private readonly ConcurrentDictionary _members; private readonly ConcurrentDictionary _roles; private readonly ConcurrentDictionary _invites; @@ -20,7 +20,7 @@ namespace Discord /// Returns the name of this channel. public string Name { get; private set; } /// Returns the current logged-in user's data for this server. - public Member CurrentMember { get; internal set; } + public User CurrentMember { get; internal set; } /// Returns true if this is a virtual server used by Discord.Net and not a real Discord server. public bool IsVirtual { get; internal set; } @@ -37,7 +37,7 @@ namespace Discord public bool IsOwner => _client.CurrentUserId == _ownerId; /// Returns the user that first created this server. [JsonIgnore] - public Member Owner { get; private set; } + public User Owner { get; private set; } /// Returns the id of the AFK voice channel for this server (see AFKTimeout). public string AFKChannelId { get; private set; } @@ -71,7 +71,7 @@ namespace Discord /// Returns a collection of all users within this server with their server-specific data. [JsonIgnore] - public IEnumerable Members => _members.Select(x => _client.Members[x.Key, Id]); + public IEnumerable Members => _members.Select(x => _client.Members[x.Key, Id]); /// Return the id of the role representing all users in a server. public string EveryoneRoleId => Id; @@ -87,7 +87,7 @@ namespace Discord { //Global Cache _channels = new ConcurrentDictionary(); - _members = new ConcurrentDictionary(); + _members = new ConcurrentDictionary(); _roles = new ConcurrentDictionary(); //Local Cache @@ -207,7 +207,7 @@ namespace Discord internal void AddInvite(Invite invite) => _invites.TryAdd(invite.Id, invite); internal void RemoveInvite(Invite invite) => _invites.TryRemove(invite.Id, out invite); - internal void AddMember(Member member) + internal void AddMember(User member) { _members.TryAdd(member.Id, member); foreach (var channel in Channels) @@ -216,7 +216,7 @@ namespace Discord channel.InvalidatePermissionsCache(member.Id); } } - internal void RemoveMember(Member member) + internal void RemoveMember(User member) { foreach (var channel in Channels) { @@ -225,7 +225,7 @@ namespace Discord } _members.TryRemove(member.Id, out member); } - internal void HasMember(Member user) => _members.ContainsKey(user.Id); + internal void HasMember(User user) => _members.ContainsKey(user.Id); internal void AddRole(Role role) => _roles.TryAdd(role.Id, role); internal void RemoveRole(Role role) => _roles.TryRemove(role.Id, out role); diff --git a/src/Discord.Net/Models/User.cs b/src/Discord.Net/Models/User.cs index e18f93ce7..16013404f 100644 --- a/src/Discord.Net/Models/User.cs +++ b/src/Discord.Net/Models/User.cs @@ -7,13 +7,14 @@ using System.Linq; namespace Discord { - public class Member : CachedObject + public class User : CachedObject { private static readonly string[] _initialRoleIds = new string[0]; private ConcurrentDictionary _channels; private ConcurrentDictionary _permissions; - private bool _hasRef; + private ServerPermissions _serverPermissions; + private bool _hasRef; private string[] _roleIds; /// Returns the name of this user on this server. @@ -65,9 +66,9 @@ namespace Discord public IEnumerable Messages => _client.Messages.Where(x => x.UserId == Id && x.Server.Id == ServerId); /// Returns a collection of all channels this user is a member of. [JsonIgnore] - public IEnumerable Channels => _client.Channels.Where(x => x.Server.Id == ServerId && x.UserIds.Contains(Id)); + public IEnumerable Channels => _client.Channels.Where(x => x.Server.Id == ServerId && x.Members == this); - internal Member(DiscordClient client, string id, string serverId) + internal User(DiscordClient client, string id, string serverId) : base(client, id) { ServerId = serverId; @@ -75,7 +76,8 @@ namespace Discord _roleIds = _initialRoleIds; _channels = new ConcurrentDictionary(); _permissions = new ConcurrentDictionary(); - } + _serverPermissions = new ServerPermissions(); + } internal override void OnCached() { var server = Server; @@ -133,7 +135,7 @@ namespace Discord if (model.Roles != null) UpdateRoles(model.Roles); - UpdatePermissions(); + UpdateServerPermissions(); } internal void Update(ExtendedMemberInfo model) { @@ -195,35 +197,27 @@ namespace Discord if (LastActivityAt == null || activity > LastActivityAt.Value) LastActivityAt = activity ?? DateTime.UtcNow; } - - internal void UpdatePermissions() - { - foreach (var channel in _channels) - UpdatePermissions(channel.Value); - } - internal void UpdatePermissions(Channel channel) + + internal void UpdateChannelPermissions(Channel channel) { if (_roleIds == null) return; // We don't have all our data processed yet, this will be called again soon var server = Server; if (server == null || channel.Server != server) return; - + ChannelPermissions permissions; if (!_permissions.TryGetValue(channel.Id, out permissions)) return; - uint newPermissions = 0x0; + uint newPermissions = _serverPermissions.RawValue; uint oldPermissions = permissions.RawValue; if (server.Owner == this) newPermissions = ChannelPermissions.All(channel).RawValue; else { - if (channel == null) return; var channelOverwrites = channel.PermissionOverwrites; //var roles = Roles.OrderBy(x => x.Id); var roles = Roles; - foreach (var serverRole in roles) - newPermissions |= serverRole.Permissions.RawValue; foreach (var denyRole in channelOverwrites.Where(x => x.TargetType == PermissionTarget.Role && x.Deny.RawValue != 0 && roles.Any(y => y.Id == x.TargetId))) newPermissions &= ~denyRole.Deny.RawValue; foreach (var allowRole in channelOverwrites.Where(x => x.TargetType == PermissionTarget.Role && x.Allow.RawValue != 0 && roles.Any(y => y.Id == x.TargetId))) @@ -234,18 +228,49 @@ namespace Discord newPermissions |= allowMembers.Allow.RawValue; } + if (BitHelper.GetBit(newPermissions, (int)PermissionsBits.ManageRolesOrPermissions)) + newPermissions = ChannelPermissions.All(channel).RawValue; + + if (newPermissions != oldPermissions) + { + permissions.SetRawValueInternal(newPermissions); + channel.InvalidMembersCache(); + } + permissions.SetRawValueInternal(newPermissions); + } + internal void UpdateServerPermissions() + { + if (_roleIds == null) return; // We don't have all our data processed yet, this will be called again soon - if (permissions.ManagePermissions) - permissions.SetRawValueInternal(ChannelPermissions.All(channel).RawValue); - /*else if (server.DefaultChannelId == channelId) - permissions.SetBitInternal(PackedPermissions.Text_ReadMessagesBit, true);*/ + var server = Server; + if (server == null) return; + + uint newPermissions = 0x0; + uint oldPermissions = _serverPermissions.RawValue; - if (permissions.RawValue != oldPermissions) - channel.InvalidMembersCache(); + if (server.Owner == this) + newPermissions = ServerPermissions.All.RawValue; + else + { + //var roles = Roles.OrderBy(x => x.Id); + var roles = Roles; + foreach (var serverRole in roles) + newPermissions |= serverRole.Permissions.RawValue; + } + + if (BitHelper.GetBit(newPermissions, (int)PermissionsBits.ManageRolesOrPermissions)) + newPermissions = ServerPermissions.All.RawValue; + + if (newPermissions != oldPermissions) + { + _serverPermissions.SetRawValueInternal(newPermissions); + foreach (var channel in _channels) + UpdateChannelPermissions(channel.Value); + } } - //TODO: Add GetServerPermissions - public ChannelPermissions GetPermissions(Channel channel) + public ServerPermissions GetPermissions() => _serverPermissions; + public ChannelPermissions GetPermissions(Channel channel) { if (channel == null) throw new ArgumentNullException(nameof(channel)); @@ -261,7 +286,7 @@ namespace Discord perms.Lock(); _channels.TryAdd(channel.Id, channel); _permissions.TryAdd(channel.Id, perms); - UpdatePermissions(channel); + UpdateChannelPermissions(channel); } internal void RemoveChannel(Channel channel) {