@@ -5,64 +5,64 @@ using System.Threading.Tasks; | |||
namespace Discord | |||
{ | |||
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 | |||
{ | |||
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> | |||
public int MessageCacheSize { get { return _messageCacheSize; } set { SetValue(ref _messageCacheSize, value); } } | |||
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; | |||
} | |||
} |
@@ -68,12 +68,30 @@ namespace Discord | |||
{ | |||
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] | |||
@@ -115,13 +133,13 @@ namespace Discord | |||
x.Global.PrivateChannel = null; | |||
}); | |||
_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 | |||
if (client.Config.MessageCacheSize > 0) | |||
@@ -190,40 +208,61 @@ namespace Discord | |||
internal void RemoveMessage(Message message) => _messages.TryRemove(message.Id, out message); | |||
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)) | |||
UpdatePermissions(user, member.Permissions); | |||
} | |||
internal void RemoveMember(User user) | |||
{ | |||
ChannelMember ignored; | |||
{ | |||
if (!_client.Config.UsePermissionsCache) | |||
return; | |||
ChannelMember 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() | |||
{ | |||
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; | |||
var server = Server; | |||
@@ -236,7 +275,7 @@ namespace Discord | |||
//Start with this user's server permissions | |||
newPermissions = server.GetPermissions(user).RawValue; | |||
if (IsPrivate || server.Owner == user) | |||
if (IsPrivate || user.IsOwner) | |||
newPermissions = mask; //Owners always have all permissions | |||
else | |||
{ | |||
@@ -263,7 +302,8 @@ namespace Discord | |||
else | |||
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; | |||
@@ -38,14 +38,11 @@ namespace Discord | |||
/// <summary> Returns the URL to this user's current avatar. </summary> | |||
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> | |||
[JsonIgnore] | |||
public User Owner => _owner.Value; | |||
[JsonProperty] | |||
private long? OwnerId => _owner.Id; | |||
internal long? OwnerId => _owner.Id; | |||
private Reference<User> _owner; | |||
/// <summary> Returns the AFK voice channel for this server (see AFKTimeout). </summary> | |||
@@ -110,6 +107,7 @@ namespace Discord | |||
internal override bool LoadReferences() | |||
{ | |||
_afkChannel.Load(); | |||
_owner.Load(); | |||
return true; | |||
} | |||
internal override void UnloadReferences() | |||
@@ -185,7 +183,7 @@ namespace Discord | |||
} | |||
var usersCache = _client.Users; | |||
foreach (var subModel in model.Members) | |||
foreach (var subModel in model.Members) | |||
{ | |||
var user = usersCache.GetOrAdd(subModel.User.Id, Id); | |||
user.Update(subModel); | |||
@@ -279,10 +277,9 @@ namespace Discord | |||
} | |||
private void UpdatePermissions(User user, ServerPermissions permissions) | |||
{ | |||
uint oldPermissions = permissions.RawValue; | |||
uint newPermissions = 0; | |||
if (Owner == user) | |||
if (user.IsOwner) | |||
newPermissions = ServerPermissions.All.RawValue; | |||
else | |||
{ | |||
@@ -294,7 +291,7 @@ namespace Discord | |||
if (BitHelper.GetBit(newPermissions, (int)PermissionsBits.ManageRolesOrPermissions)) | |||
newPermissions = ServerPermissions.All.RawValue; | |||
if (newPermissions != oldPermissions) | |||
if (newPermissions != permissions.RawValue) | |||
{ | |||
permissions.SetRawValueInternal(newPermissions); | |||
foreach (var channel in _channels) | |||