| @@ -5,64 +5,64 @@ using System.Threading.Tasks; | |||||
| namespace Discord | namespace Discord | ||||
| { | { | ||||
| internal static class TaskExtensions | internal static class TaskExtensions | ||||
| { | |||||
| public static async Task Timeout(this Task task, int milliseconds) | |||||
| { | |||||
| Task timeoutTask = Task.Delay(milliseconds); | |||||
| Task finishedTask = await Task.WhenAny(task, timeoutTask).ConfigureAwait(false); | |||||
| if (finishedTask == timeoutTask) | |||||
| throw new TimeoutException(); | |||||
| else | |||||
| await task.ConfigureAwait(false); | |||||
| } | |||||
| public static async Task<T> Timeout<T>(this Task<T> task, int milliseconds) | |||||
| { | |||||
| Task timeoutTask = Task.Delay(milliseconds); | |||||
| Task finishedTask = await Task.WhenAny(task, timeoutTask).ConfigureAwait(false); | |||||
| if (finishedTask == timeoutTask) | |||||
| throw new TimeoutException(); | |||||
| else | |||||
| return await task.ConfigureAwait(false); | |||||
| } | |||||
| public static async Task Timeout(this Task task, int milliseconds, CancellationTokenSource timeoutToken) | |||||
| { | |||||
| try | |||||
| { | |||||
| timeoutToken.CancelAfter(milliseconds); | |||||
| await task.ConfigureAwait(false); | |||||
| } | |||||
| catch (OperationCanceledException) | |||||
| { | |||||
| if (timeoutToken.IsCancellationRequested) | |||||
| throw new TimeoutException(); | |||||
| throw; | |||||
| } | |||||
| } | |||||
| public static async Task<T> Timeout<T>(this Task<T> task, int milliseconds, CancellationTokenSource timeoutToken) | |||||
| { | |||||
| try | |||||
| { | |||||
| timeoutToken.CancelAfter(milliseconds); | |||||
| return await task.ConfigureAwait(false); | |||||
| } | |||||
| catch (OperationCanceledException) | |||||
| { | |||||
| if (timeoutToken.IsCancellationRequested) | |||||
| throw new TimeoutException(); | |||||
| throw; | |||||
| } | |||||
| } | |||||
| { | |||||
| public static async Task Timeout(this Task task, int milliseconds) | |||||
| { | |||||
| Task timeoutTask = Task.Delay(milliseconds); | |||||
| Task finishedTask = await Task.WhenAny(task, timeoutTask).ConfigureAwait(false); | |||||
| if (finishedTask == timeoutTask) | |||||
| throw new TimeoutException(); | |||||
| else | |||||
| await task.ConfigureAwait(false); | |||||
| } | |||||
| public static async Task<T> Timeout<T>(this Task<T> task, int milliseconds) | |||||
| { | |||||
| Task timeoutTask = Task.Delay(milliseconds); | |||||
| Task finishedTask = await Task.WhenAny(task, timeoutTask).ConfigureAwait(false); | |||||
| if (finishedTask == timeoutTask) | |||||
| throw new TimeoutException(); | |||||
| else | |||||
| return await task.ConfigureAwait(false); | |||||
| } | |||||
| public static async Task Timeout(this Task task, int milliseconds, CancellationTokenSource timeoutToken) | |||||
| { | |||||
| try | |||||
| { | |||||
| timeoutToken.CancelAfter(milliseconds); | |||||
| await task.ConfigureAwait(false); | |||||
| } | |||||
| catch (OperationCanceledException) | |||||
| { | |||||
| if (timeoutToken.IsCancellationRequested) | |||||
| throw new TimeoutException(); | |||||
| throw; | |||||
| } | |||||
| } | |||||
| public static async Task<T> Timeout<T>(this Task<T> task, int milliseconds, CancellationTokenSource timeoutToken) | |||||
| { | |||||
| try | |||||
| { | |||||
| timeoutToken.CancelAfter(milliseconds); | |||||
| return await task.ConfigureAwait(false); | |||||
| } | |||||
| catch (OperationCanceledException) | |||||
| { | |||||
| if (timeoutToken.IsCancellationRequested) | |||||
| throw new TimeoutException(); | |||||
| throw; | |||||
| } | |||||
| } | |||||
| public static async Task Wait(this CancellationTokenSource tokenSource) | |||||
| { | |||||
| var token = tokenSource.Token; | |||||
| try { await Task.Delay(-1, token).ConfigureAwait(false); } | |||||
| catch (OperationCanceledException) { } //Expected | |||||
| } | |||||
| public static async Task Wait(this CancellationToken token) | |||||
| { | |||||
| try { await Task.Delay(-1, token).ConfigureAwait(false); } | |||||
| catch (OperationCanceledException) { } //Expected | |||||
| } | |||||
| } | |||||
| public static async Task Wait(this CancellationTokenSource tokenSource) | |||||
| { | |||||
| var token = tokenSource.Token; | |||||
| try { await Task.Delay(-1, token).ConfigureAwait(false); } | |||||
| catch (OperationCanceledException) { } //Expected | |||||
| } | |||||
| public static async Task Wait(this CancellationToken token) | |||||
| { | |||||
| try { await Task.Delay(-1, token).ConfigureAwait(false); } | |||||
| catch (OperationCanceledException) { } //Expected | |||||
| } | |||||
| } | |||||
| } | } | ||||
| @@ -2,12 +2,12 @@ | |||||
| namespace Discord | namespace Discord | ||||
| { | { | ||||
| internal static class TaskHelper | |||||
| { | |||||
| public static Task CompletedTask { get; } | |||||
| static TaskHelper() | |||||
| { | |||||
| CompletedTask = Task.Delay(0); | |||||
| } | |||||
| } | |||||
| internal static class TaskHelper | |||||
| { | |||||
| public static Task CompletedTask { get; } | |||||
| static TaskHelper() | |||||
| { | |||||
| CompletedTask = Task.Delay(0); | |||||
| } | |||||
| } | |||||
| } | } | ||||
| @@ -83,8 +83,11 @@ namespace Discord | |||||
| /// <summary> Gets or sets the number of messages per channel that should be kept in cache. Setting this to zero disables the message cache entirely. </summary> | /// <summary> Gets or sets the number of messages per channel that should be kept in cache. Setting this to zero disables the message cache entirely. </summary> | ||||
| public int MessageCacheSize { get { return _messageCacheSize; } set { SetValue(ref _messageCacheSize, value); } } | public int MessageCacheSize { get { return _messageCacheSize; } set { SetValue(ref _messageCacheSize, value); } } | ||||
| private int _messageCacheSize = 100; | private int _messageCacheSize = 100; | ||||
| /// <summary> Maintains the LastActivity property for users, showing when they last made an action (sent message, joined server, typed, etc). </summary> | |||||
| public bool TrackActivity { get { return _trackActivity; } set { SetValue(ref _trackActivity, value); } } | |||||
| /// <summary> Gets or sets whether the permissions cache should be used. This makes operations such as User.GetPermissions(Channel), User.ServerPermissions and Channel.Members </summary> | |||||
| public bool UsePermissionsCache { get { return _usePermissionsCache; } set { SetValue(ref _usePermissionsCache, value); } } | |||||
| private bool _usePermissionsCache = true; | |||||
| /// <summary> Maintains the LastActivity property for users, showing when they last made an action (sent message, joined server, typed, etc). </summary> | |||||
| public bool TrackActivity { get { return _trackActivity; } set { SetValue(ref _trackActivity, value); } } | |||||
| private bool _trackActivity = true; | private bool _trackActivity = true; | ||||
| } | } | ||||
| } | } | ||||
| @@ -68,12 +68,30 @@ namespace Discord | |||||
| { | { | ||||
| get | get | ||||
| { | { | ||||
| if (Type == ChannelType.Text) | |||||
| return _members.Values.Where(x => x.Permissions.ReadMessages == true).Select(x => x.User); | |||||
| else if (Type == ChannelType.Voice) | |||||
| return _members.Values.Select(x => x.User).Where(x => x.VoiceChannel == this); | |||||
| else | |||||
| return Enumerable.Empty<User>(); | |||||
| if (IsPrivate) | |||||
| return _members.Values.Select(x => x.User); | |||||
| if (_client.Config.UsePermissionsCache) | |||||
| { | |||||
| if (Type == ChannelType.Text) | |||||
| return _members.Values.Where(x => x.Permissions.ReadMessages == true).Select(x => x.User); | |||||
| else if (Type == ChannelType.Voice) | |||||
| return _members.Values.Select(x => x.User).Where(x => x.VoiceChannel == this); | |||||
| } | |||||
| else | |||||
| { | |||||
| if (Type == ChannelType.Text) | |||||
| { | |||||
| ChannelPermissions perms = new ChannelPermissions(); | |||||
| return Server.Members.Where(x => | |||||
| { | |||||
| UpdatePermissions(x, perms); | |||||
| return perms.ReadMessages == true; | |||||
| }); | |||||
| } | |||||
| else if (Type == ChannelType.Voice) | |||||
| return Server.Members.Where(x => x.VoiceChannel == this); | |||||
| } | |||||
| return Enumerable.Empty<User>(); | |||||
| } | } | ||||
| } | } | ||||
| [JsonProperty] | [JsonProperty] | ||||
| @@ -115,13 +133,13 @@ namespace Discord | |||||
| x.Global.PrivateChannel = null; | x.Global.PrivateChannel = null; | ||||
| }); | }); | ||||
| _permissionOverwrites = new PermissionOverwrite[0]; | _permissionOverwrites = new PermissionOverwrite[0]; | ||||
| _members = new ConcurrentDictionary<long, ChannelMember>(); | |||||
| _members = new ConcurrentDictionary<long, ChannelMember>(); | |||||
| if (recipientId != null) | |||||
| { | |||||
| AddMember(client.PrivateUser); | |||||
| AddMember(Recipient); | |||||
| } | |||||
| if (recipientId != null) | |||||
| { | |||||
| AddMember(client.PrivateUser); | |||||
| AddMember(Recipient); | |||||
| } | |||||
| //Local Cache | //Local Cache | ||||
| if (client.Config.MessageCacheSize > 0) | if (client.Config.MessageCacheSize > 0) | ||||
| @@ -190,40 +208,61 @@ namespace Discord | |||||
| internal void RemoveMessage(Message message) => _messages.TryRemove(message.Id, out message); | internal void RemoveMessage(Message message) => _messages.TryRemove(message.Id, out message); | ||||
| internal void AddMember(User user) | internal void AddMember(User user) | ||||
| { | |||||
| var member = new ChannelMember(user); | |||||
| { | |||||
| if (!_client.Config.UsePermissionsCache) | |||||
| return; | |||||
| var member = new ChannelMember(user); | |||||
| if (_members.TryAdd(user.Id, member)) | if (_members.TryAdd(user.Id, member)) | ||||
| UpdatePermissions(user, member.Permissions); | UpdatePermissions(user, member.Permissions); | ||||
| } | } | ||||
| internal void RemoveMember(User user) | internal void RemoveMember(User user) | ||||
| { | |||||
| ChannelMember ignored; | |||||
| { | |||||
| if (!_client.Config.UsePermissionsCache) | |||||
| return; | |||||
| ChannelMember ignored; | |||||
| _members.TryRemove(user.Id, out ignored); | _members.TryRemove(user.Id, out ignored); | ||||
| } | } | ||||
| internal ChannelPermissions GetPermissions(User user) | |||||
| { | |||||
| ChannelMember member; | |||||
| if (_members.TryGetValue(user.Id, out member)) | |||||
| return member.Permissions; | |||||
| else | |||||
| return null; | |||||
| internal ChannelPermissions GetPermissions(User user) | |||||
| { | |||||
| if (_client.Config.UsePermissionsCache) | |||||
| { | |||||
| ChannelMember member; | |||||
| if (_members.TryGetValue(user.Id, out member)) | |||||
| return member.Permissions; | |||||
| else | |||||
| return null; | |||||
| } | |||||
| else | |||||
| { | |||||
| ChannelPermissions perms = new ChannelPermissions(); | |||||
| UpdatePermissions(user, perms); | |||||
| return perms; | |||||
| } | |||||
| } | } | ||||
| internal void UpdatePermissions() | internal void UpdatePermissions() | ||||
| { | |||||
| foreach (var pair in _members) | |||||
| { | |||||
| ChannelMember member = pair.Value; | |||||
| UpdatePermissions(member.User, member.Permissions); | |||||
| } | |||||
| { | |||||
| if (!_client.Config.UsePermissionsCache) | |||||
| return; | |||||
| foreach (var pair in _members) | |||||
| { | |||||
| ChannelMember member = pair.Value; | |||||
| UpdatePermissions(member.User, member.Permissions); | |||||
| } | |||||
| } | } | ||||
| internal void UpdatePermissions(User user) | |||||
| { | |||||
| ChannelMember member; | |||||
| if (_members.TryGetValue(user.Id, out member)) | |||||
| UpdatePermissions(member.User, member.Permissions); | |||||
| internal void UpdatePermissions(User user) | |||||
| { | |||||
| if (!_client.Config.UsePermissionsCache) | |||||
| return; | |||||
| ChannelMember member; | |||||
| if (_members.TryGetValue(user.Id, out member)) | |||||
| UpdatePermissions(member.User, member.Permissions); | |||||
| } | } | ||||
| private void UpdatePermissions(User user, ChannelPermissions permissions) | |||||
| internal void UpdatePermissions(User user, ChannelPermissions permissions) | |||||
| { | { | ||||
| uint newPermissions = 0; | uint newPermissions = 0; | ||||
| var server = Server; | var server = Server; | ||||
| @@ -236,7 +275,7 @@ namespace Discord | |||||
| //Start with this user's server permissions | //Start with this user's server permissions | ||||
| newPermissions = server.GetPermissions(user).RawValue; | newPermissions = server.GetPermissions(user).RawValue; | ||||
| if (IsPrivate || server.Owner == user) | |||||
| if (IsPrivate || user.IsOwner) | |||||
| newPermissions = mask; //Owners always have all permissions | newPermissions = mask; //Owners always have all permissions | ||||
| else | else | ||||
| { | { | ||||
| @@ -263,7 +302,8 @@ namespace Discord | |||||
| else | else | ||||
| newPermissions = mask; //Private messages always have all permissions | newPermissions = mask; //Private messages always have all permissions | ||||
| permissions.SetRawValueInternal(newPermissions); | |||||
| if (newPermissions != permissions.RawValue) | |||||
| permissions.SetRawValueInternal(newPermissions); | |||||
| } | } | ||||
| public override bool Equals(object obj) => obj is Channel && (obj as Channel).Id == Id; | public override bool Equals(object obj) => obj is Channel && (obj as Channel).Id == Id; | ||||
| @@ -38,14 +38,11 @@ namespace Discord | |||||
| /// <summary> Returns the URL to this user's current avatar. </summary> | /// <summary> Returns the URL to this user's current avatar. </summary> | ||||
| public string IconUrl => IconId != null ? Endpoints.ServerIcon(Id, IconId) : null; | public string IconUrl => IconId != null ? Endpoints.ServerIcon(Id, IconId) : null; | ||||
| /// <summary> Returns true if the current user created this server. </summary> | |||||
| public bool IsOwner => _client.CurrentUser.Id == _owner.Id; | |||||
| /// <summary> Returns the user that first created this server. </summary> | /// <summary> Returns the user that first created this server. </summary> | ||||
| [JsonIgnore] | [JsonIgnore] | ||||
| public User Owner => _owner.Value; | public User Owner => _owner.Value; | ||||
| [JsonProperty] | [JsonProperty] | ||||
| private long? OwnerId => _owner.Id; | |||||
| internal long? OwnerId => _owner.Id; | |||||
| private Reference<User> _owner; | private Reference<User> _owner; | ||||
| /// <summary> Returns the AFK voice channel for this server (see AFKTimeout). </summary> | /// <summary> Returns the AFK voice channel for this server (see AFKTimeout). </summary> | ||||
| @@ -110,6 +107,7 @@ namespace Discord | |||||
| internal override bool LoadReferences() | internal override bool LoadReferences() | ||||
| { | { | ||||
| _afkChannel.Load(); | _afkChannel.Load(); | ||||
| _owner.Load(); | |||||
| return true; | return true; | ||||
| } | } | ||||
| internal override void UnloadReferences() | internal override void UnloadReferences() | ||||
| @@ -185,7 +183,7 @@ namespace Discord | |||||
| } | } | ||||
| var usersCache = _client.Users; | var usersCache = _client.Users; | ||||
| foreach (var subModel in model.Members) | |||||
| foreach (var subModel in model.Members) | |||||
| { | { | ||||
| var user = usersCache.GetOrAdd(subModel.User.Id, Id); | var user = usersCache.GetOrAdd(subModel.User.Id, Id); | ||||
| user.Update(subModel); | user.Update(subModel); | ||||
| @@ -279,10 +277,9 @@ namespace Discord | |||||
| } | } | ||||
| private void UpdatePermissions(User user, ServerPermissions permissions) | private void UpdatePermissions(User user, ServerPermissions permissions) | ||||
| { | { | ||||
| uint oldPermissions = permissions.RawValue; | |||||
| uint newPermissions = 0; | uint newPermissions = 0; | ||||
| if (Owner == user) | |||||
| if (user.IsOwner) | |||||
| newPermissions = ServerPermissions.All.RawValue; | newPermissions = ServerPermissions.All.RawValue; | ||||
| else | else | ||||
| { | { | ||||
| @@ -294,7 +291,7 @@ namespace Discord | |||||
| if (BitHelper.GetBit(newPermissions, (int)PermissionsBits.ManageRolesOrPermissions)) | if (BitHelper.GetBit(newPermissions, (int)PermissionsBits.ManageRolesOrPermissions)) | ||||
| newPermissions = ServerPermissions.All.RawValue; | newPermissions = ServerPermissions.All.RawValue; | ||||
| if (newPermissions != oldPermissions) | |||||
| if (newPermissions != permissions.RawValue) | |||||
| { | { | ||||
| permissions.SetRawValueInternal(newPermissions); | permissions.SetRawValueInternal(newPermissions); | ||||
| foreach (var channel in _channels) | foreach (var channel in _channels) | ||||