@@ -36,18 +36,20 @@ namespace Discord | |||||
public Task Ban(User member) | public Task Ban(User member) | ||||
{ | { | ||||
if (member == null) throw new ArgumentNullException(nameof(member)); | if (member == null) throw new ArgumentNullException(nameof(member)); | ||||
if (member.Server == null) throw new ArgumentException("Unable to ban a user in a private chat."); | |||||
CheckReady(); | CheckReady(); | ||||
return _api.Ban(member.ServerId, member.Id); | |||||
return _api.Ban(member.Server.Id, member.Id); | |||||
} | } | ||||
/// <summary> Unbans a user from the provided server. </summary> | /// <summary> Unbans a user from the provided server. </summary> | ||||
public async Task Unban(User member) | |||||
public async Task Unban(Server server, string userId) | |||||
{ | { | ||||
if (member == null) throw new ArgumentNullException(nameof(member)); | |||||
if (server == null) throw new ArgumentNullException(nameof(server)); | |||||
if (userId == null) throw new ArgumentNullException(nameof(userId)); | |||||
CheckReady(); | CheckReady(); | ||||
try { await _api.Unban(member.ServerId, member.Id).ConfigureAwait(false); } | |||||
try { await _api.Unban(server.Id, userId).ConfigureAwait(false); } | |||||
catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { } | catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { } | ||||
} | } | ||||
} | } |
@@ -10,6 +10,8 @@ namespace Discord | |||||
/// <remarks> Supported formats: inviteCode, xkcdCode, https://discord.gg/inviteCode, https://discord.gg/xkcdCode </remarks> | /// <remarks> Supported formats: inviteCode, xkcdCode, https://discord.gg/inviteCode, https://discord.gg/xkcdCode </remarks> | ||||
public async Task<Invite> GetInvite(string inviteIdOrXkcd) | public async Task<Invite> GetInvite(string inviteIdOrXkcd) | ||||
{ | { | ||||
//This doesn't work well if it's an invite to a different server! | |||||
if (inviteIdOrXkcd == null) throw new ArgumentNullException(nameof(inviteIdOrXkcd)); | if (inviteIdOrXkcd == null) throw new ArgumentNullException(nameof(inviteIdOrXkcd)); | ||||
CheckReady(); | CheckReady(); | ||||
@@ -22,8 +24,8 @@ namespace Discord | |||||
inviteIdOrXkcd = inviteIdOrXkcd.Substring(index + 1); | inviteIdOrXkcd = inviteIdOrXkcd.Substring(index + 1); | ||||
var response = await _api.GetInvite(inviteIdOrXkcd).ConfigureAwait(false); | var response = await _api.GetInvite(inviteIdOrXkcd).ConfigureAwait(false); | ||||
var invite = new Invite(this, response.Code, response.XkcdPass, response.Guild.Id); | |||||
invite.Update(response); | |||||
var invite = new Invite(this, response.Code, response.XkcdPass, response.Guild.Id, response.Inviter?.Id, response.Channel?.Id); | |||||
invite.Cache(); //Builds references | |||||
return invite; | return invite; | ||||
} | } | ||||
@@ -53,8 +55,8 @@ namespace Discord | |||||
var response = await _api.CreateInvite(channel.Id, maxAge: maxAge, maxUses: maxUses, | var response = await _api.CreateInvite(channel.Id, maxAge: maxAge, maxUses: maxUses, | ||||
tempMembership: tempMembership, hasXkcd: hasXkcd).ConfigureAwait(false); | tempMembership: tempMembership, hasXkcd: hasXkcd).ConfigureAwait(false); | ||||
var invite = new Invite(this, response.Code, response.XkcdPass, response.Guild.Id); | |||||
invite.Update(response); | |||||
var invite = new Invite(this, response.Code, response.XkcdPass, response.Guild.Id, response.Inviter?.Id, response.Channel?.Id); | |||||
invite.Cache(); //Builds references | |||||
return invite; | return invite; | ||||
} | } | ||||
@@ -121,7 +121,7 @@ namespace Discord | |||||
if (member == null) throw new ArgumentNullException(nameof(member)); | if (member == null) throw new ArgumentNullException(nameof(member)); | ||||
CheckReady(); | CheckReady(); | ||||
return _api.EditMember(member.ServerId, member.Id, mute: mute, deaf: deaf, roles: roles.Select(x => x.Id)); | |||||
return _api.EditMember(member.Server?.Id, member.Id, mute: mute, deaf: deaf, roles: roles.Select(x => x.Id)); | |||||
} | } | ||||
} | } | ||||
} | } |
@@ -25,7 +25,7 @@ namespace Discord | |||||
else | else | ||||
{ | { | ||||
var msg = new Message(_client, id, channelId, userId); | var msg = new Message(_client, id, channelId, userId); | ||||
msg.Cache(); //Creates references to channel/server | |||||
msg.Cache(); //Builds references | |||||
return msg; | return msg; | ||||
} | } | ||||
} | } | ||||
@@ -81,9 +81,17 @@ namespace Discord | |||||
if (changed) | if (changed) | ||||
{ | { | ||||
if (targetType == PermissionTarget.Role) | if (targetType == PermissionTarget.Role) | ||||
channel.InvalidatePermissionsCache(); | |||||
{ | |||||
var role = _roles[targetId]; | |||||
if (role != null) | |||||
channel.InvalidatePermissionsCache(role); | |||||
} | |||||
else if (targetType == PermissionTarget.User) | else if (targetType == PermissionTarget.User) | ||||
channel.InvalidatePermissionsCache(targetId); | |||||
{ | |||||
var user = _users[targetId, channel.Server?.Id]; | |||||
if (user != null) | |||||
channel.InvalidatePermissionsCache(user); | |||||
} | |||||
} | } | ||||
} | } | ||||
@@ -114,9 +122,16 @@ namespace Discord | |||||
channel.PermissionOverwrites.Where(x => x.TargetType != targetType || x.TargetId != userOrRoleId).ToArray(); | channel.PermissionOverwrites.Where(x => x.TargetType != targetType || x.TargetId != userOrRoleId).ToArray(); | ||||
if (targetType == PermissionTarget.Role) | if (targetType == PermissionTarget.Role) | ||||
channel.InvalidatePermissionsCache(); | |||||
{ | |||||
var role = _roles[userOrRoleId]; | |||||
channel.InvalidatePermissionsCache(role); | |||||
} | |||||
else if (targetType == PermissionTarget.User) | else if (targetType == PermissionTarget.User) | ||||
channel.InvalidatePermissionsCache(userOrRoleId); | |||||
{ | |||||
var user = _users[userOrRoleId, channel.Server?.Id]; | |||||
if (user != null) | |||||
channel.InvalidatePermissionsCache(user); | |||||
} | |||||
} | } | ||||
} | } | ||||
catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { } | catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { } | ||||
@@ -7,11 +7,13 @@ namespace Discord | |||||
{ | { | ||||
internal sealed class Roles : AsyncCollection<Role> | internal sealed class Roles : AsyncCollection<Role> | ||||
{ | { | ||||
private const string VirtualEveryoneId = "[Virtual]"; | |||||
public Role VirtualEveryone { get; private set; } | public Role VirtualEveryone { get; private set; } | ||||
public Roles(DiscordClient client, object writerLock) | public Roles(DiscordClient client, object writerLock) | ||||
: base(client, writerLock, x => x.OnCached(), x => x.OnUncached()) { } | |||||
: base(client, writerLock, x => x.OnCached(), x => x.OnUncached()) | |||||
{ | |||||
VirtualEveryone = new Role(client, "Private", null); | |||||
} | |||||
public Role GetOrAdd(string id, string serverId) | public Role GetOrAdd(string id, string serverId) | ||||
=> GetOrAdd(id, () => new Role(_client, id, serverId)); | => GetOrAdd(id, () => new Role(_client, id, serverId)); | ||||
@@ -57,7 +57,7 @@ namespace Discord | |||||
} | } | ||||
/// <summary> Returns a collection of all servers this client is a member of. </summary> | /// <summary> Returns a collection of all servers this client is a member of. </summary> | ||||
public IEnumerable<Server> AllServers => _servers.Where(x => !x.IsVirtual); | |||||
public IEnumerable<Server> AllServers => _servers; | |||||
internal Servers Servers => _servers; | internal Servers Servers => _servers; | ||||
private readonly Servers _servers; | private readonly Servers _servers; | ||||
@@ -59,12 +59,16 @@ namespace Discord | |||||
VoiceDisconnected += (s, e) => | VoiceDisconnected += (s, e) => | ||||
{ | { | ||||
foreach (var member in _users) | |||||
var server = _servers[e.ServerId]; | |||||
if (server != null) | |||||
{ | { | ||||
if (member.ServerId == e.ServerId && member.IsSpeaking) | |||||
foreach (var member in server.Members) | |||||
{ | { | ||||
member.IsSpeaking = false; | |||||
RaiseUserIsSpeaking(member, _channels[_voiceSocket.CurrentChannelId], false); | |||||
if (member.IsSpeaking) | |||||
{ | |||||
member.IsSpeaking = false; | |||||
RaiseUserIsSpeaking(member, _channels[_voiceSocket.CurrentChannelId], false); | |||||
} | |||||
} | } | ||||
} | } | ||||
}; | }; | ||||
@@ -104,13 +108,13 @@ namespace Discord | |||||
BanRemoved += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, | BanRemoved += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, | ||||
$"Removed Ban: {e.Server?.Name ?? "[Private]"}/{e.UserId}"); | $"Removed Ban: {e.Server?.Name ?? "[Private]"}/{e.UserId}"); | ||||
UserAdded += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, | UserAdded += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, | ||||
$"Added Member: {e.Server?.Name ?? "[Private]"}/{e.UserId}"); | |||||
$"Added Member: {e.Server?.Name ?? "[Private]"}/{e.User.Id}"); | |||||
UserRemoved += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, | UserRemoved += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, | ||||
$"Removed Member: {e.Server?.Name ?? "[Private]"}/{e.UserId}"); | |||||
$"Removed Member: {e.Server?.Name ?? "[Private]"}/{e.User.Id}"); | |||||
MemberUpdated += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, | MemberUpdated += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, | ||||
$"Updated Member: {e.Server?.Name ?? "[Private]"}/{e.UserId}"); | |||||
$"Updated Member: {e.Server?.Name ?? "[Private]"}/{e.User.Id}"); | |||||
UserVoiceStateUpdated += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, | UserVoiceStateUpdated += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, | ||||
$"Updated Member (Voice State): {e.Server?.Name ?? "[Private]"}/{e.UserId}"); | |||||
$"Updated Member (Voice State): {e.Server?.Name ?? "[Private]"}/{e.User.Id}"); | |||||
ProfileUpdated += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, | ProfileUpdated += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, | ||||
"Updated Profile"); | "Updated Profile"); | ||||
} | } | ||||
@@ -281,13 +285,12 @@ namespace Discord | |||||
{ | { | ||||
try | try | ||||
{ | { | ||||
await base.OnReceivedEvent(e); | |||||
switch (e.Type) | switch (e.Type) | ||||
{ | { | ||||
//Global | //Global | ||||
case "READY": //Resync | |||||
case "READY": //Resync | |||||
{ | { | ||||
base.OnReceivedEvent(e).Wait(); //This cannot be an await, or we'll get later messages before we're ready | |||||
var data = e.Payload.ToObject<ReadyEvent>(_serializer); | var data = e.Payload.ToObject<ReadyEvent>(_serializer); | ||||
_currentUser = _users.GetOrAdd(data.User.Id, null); | _currentUser = _users.GetOrAdd(data.User.Id, null); | ||||
_currentUser.Update(data.User); | _currentUser.Update(data.User); | ||||
@@ -576,10 +579,11 @@ namespace Discord | |||||
var member = _users[data.UserId, data.GuildId]; | var member = _users[data.UserId, data.GuildId]; | ||||
if (member != null) | if (member != null) | ||||
{ | { | ||||
if (data.ChannelId != member.VoiceChannelId && member.IsSpeaking) | |||||
var voiceChannel = member.VoiceChannel; | |||||
if (voiceChannel != null && data.ChannelId != voiceChannel.Id && member.IsSpeaking) | |||||
{ | { | ||||
member.IsSpeaking = false; | member.IsSpeaking = false; | ||||
RaiseUserIsSpeaking(member, _channels[member.VoiceChannelId], false); | |||||
RaiseUserIsSpeaking(member, _channels[voiceChannel.Id], false); | |||||
} | } | ||||
member.Update(data); | member.Update(data); | ||||
RaiseUserVoiceStateUpdated(member); | RaiseUserVoiceStateUpdated(member); | ||||
@@ -611,6 +615,7 @@ namespace Discord | |||||
//Internal (handled in DiscordSimpleClient) | //Internal (handled in DiscordSimpleClient) | ||||
case "VOICE_SERVER_UPDATE": | case "VOICE_SERVER_UPDATE": | ||||
await base.OnReceivedEvent(e); | |||||
break; | break; | ||||
//Others | //Others | ||||
@@ -90,7 +90,7 @@ namespace Discord | |||||
} | } | ||||
} | } | ||||
socket.ReceivedEvent += (s, e) => OnReceivedEvent(e); | |||||
socket.ReceivedEvent += async (s, e) => await OnReceivedEvent(e); | |||||
return socket; | return socket; | ||||
} | } | ||||
internal virtual VoiceWebSocket CreateVoiceSocket() | internal virtual VoiceWebSocket CreateVoiceSocket() | ||||
@@ -292,7 +292,7 @@ namespace Discord | |||||
} | } | ||||
} | } | ||||
internal virtual Task OnReceivedEvent(WebSocketEventEventArgs e) | |||||
internal virtual async Task OnReceivedEvent(WebSocketEventEventArgs e) | |||||
{ | { | ||||
try | try | ||||
{ | { | ||||
@@ -309,7 +309,7 @@ namespace Discord | |||||
{ | { | ||||
string token = e.Payload.Value<string>("token"); | string token = e.Payload.Value<string>("token"); | ||||
_voiceSocket.Host = "wss://" + e.Payload.Value<string>("endpoint").Split(':')[0]; | _voiceSocket.Host = "wss://" + e.Payload.Value<string>("endpoint").Split(':')[0]; | ||||
return _voiceSocket.Login(_userId, _dataSocket.SessionId, token, CancelToken); | |||||
await _voiceSocket.Login(_userId, _dataSocket.SessionId, token, CancelToken); | |||||
} | } | ||||
} | } | ||||
break; | break; | ||||
@@ -319,7 +319,6 @@ namespace Discord | |||||
{ | { | ||||
RaiseOnLog(LogMessageSeverity.Error, LogMessageSource.Client, $"Error handling {e.Type} event: {ex.GetBaseException().Message}"); | RaiseOnLog(LogMessageSeverity.Error, LogMessageSource.Client, $"Error handling {e.Type} event: {ex.GetBaseException().Message}"); | ||||
} | } | ||||
return TaskHelper.CompletedTask; | |||||
} | } | ||||
} | } | ||||
} | } |
@@ -53,11 +53,8 @@ namespace Discord | |||||
{ | { | ||||
get | get | ||||
{ | { | ||||
if (!_areMembersStale) | |||||
return _members.Select(x => x.Value); | |||||
_members = Server.Members.Where(x => x.GetPermissions(this)?.ReadMessages ?? false).ToDictionary(x => x.Id, x => x); | |||||
_areMembersStale = false; | |||||
if (_areMembersStale) | |||||
UpdateMembersCache(); | |||||
return _members.Select(x => x.Value); | return _members.Select(x => x.Value); | ||||
} | } | ||||
} | } | ||||
@@ -157,22 +154,32 @@ 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 InvalidMembersCache() | |||||
internal void InvalidateMembersCache() | |||||
{ | { | ||||
_areMembersStale = true; | _areMembersStale = true; | ||||
} | } | ||||
internal void InvalidatePermissionsCache() | |||||
private void UpdateMembersCache() | |||||
{ | |||||
_members = Server.Members.Where(x => x.GetPermissions(this)?.ReadMessages ?? false).ToDictionary(x => x.Id, x => x); | |||||
_areMembersStale = false; | |||||
} | |||||
internal void InvalidatePermissionsCache() | |||||
{ | |||||
UpdateMembersCache(); | |||||
foreach (var member in _members) | |||||
member.Value.UpdateChannelPermissions(this); | |||||
} | |||||
internal void InvalidatePermissionsCache(Role role) | |||||
{ | { | ||||
_areMembersStale = true; | _areMembersStale = true; | ||||
foreach (var member in Members) | |||||
foreach (var member in role.Members) | |||||
member.UpdateChannelPermissions(this); | member.UpdateChannelPermissions(this); | ||||
} | } | ||||
internal void InvalidatePermissionsCache(string userId) | |||||
internal void InvalidatePermissionsCache(User user) | |||||
{ | { | ||||
_areMembersStale = true; | _areMembersStale = true; | ||||
var user = _members[userId] | |||||
if (user != null) | |||||
user.UpdateChannelPermissions(this); | |||||
user.UpdateChannelPermissions(this); | |||||
} | } | ||||
} | } | ||||
} | } |
@@ -34,7 +34,10 @@ namespace Discord | |||||
_users = new ConcurrentDictionary<string, User>(); | _users = new ConcurrentDictionary<string, User>(); | ||||
} | } | ||||
internal override void OnCached() { } | internal override void OnCached() { } | ||||
internal override void OnUncached() { } | |||||
internal override void OnUncached() | |||||
{ | |||||
//Don't need to clean _users - they're considered owned by server | |||||
} | |||||
internal void Update(UserInfo model) | internal void Update(UserInfo model) | ||||
{ | { | ||||
@@ -44,10 +47,10 @@ namespace Discord | |||||
IsVerified = model.IsVerified; | IsVerified = model.IsVerified; | ||||
} | } | ||||
internal void AddUser(User user) => _users.TryAdd(user.Id, user); | |||||
internal void AddUser(User user) => _users.TryAdd(user.UniqueId, user); | |||||
internal void RemoveUser(User user) | internal void RemoveUser(User user) | ||||
{ | { | ||||
if (_users.TryRemove(user.Id, out user)) | |||||
if (_users.TryRemove(user.UniqueId, out user)) | |||||
{ | { | ||||
if (_users.Count == 0) | if (_users.Count == 0) | ||||
_client.GlobalUsers.TryRemove(Id); | _client.GlobalUsers.TryRemove(Id); | ||||
@@ -5,10 +5,11 @@ using Newtonsoft.Json; | |||||
namespace Discord | namespace Discord | ||||
{ | { | ||||
public sealed class Invite : CachedObject | public sealed class Invite : CachedObject | ||||
{ | |||||
private readonly string _serverId; | |||||
private string _inviterId, _channelId; | |||||
{ | |||||
/// <summary> Returns the unique code for this invite. </summary> | |||||
public string Code { get; private set; } | |||||
/// <summary> Returns, if enabled, an alternative human-readable code for URLs. </summary> | |||||
public string XkcdCode { get; } | |||||
/// <summary> Time (in seconds) until the invite expires. Set to 0 to never expire. </summary> | /// <summary> Time (in seconds) until the invite expires. Set to 0 to never expire. </summary> | ||||
public int MaxAge { get; private set; } | public int MaxAge { get; private set; } | ||||
/// <summary> The amount of times this invite has been used. </summary> | /// <summary> The amount of times this invite has been used. </summary> | ||||
@@ -19,47 +20,72 @@ namespace Discord | |||||
public bool IsRevoked { get; private set; } | public bool IsRevoked { get; private set; } | ||||
/// <summary> If true, a user accepting this invite will be kicked from the server after closing their client. </summary> | /// <summary> If true, a user accepting this invite will be kicked from the server after closing their client. </summary> | ||||
public bool IsTemporary { get; private set; } | public bool IsTemporary { get; private set; } | ||||
/// <summary> Returns, if enabled, an alternative human-readable code for URLs. </summary> | |||||
public string XkcdPass { get; } | |||||
/// <summary> Returns a URL for this invite using XkcdPass if available or Id if not. </summary> | |||||
public string Url => API.Endpoints.InviteUrl(XkcdPass ?? Id); | |||||
/// <summary> Returns a URL for this invite using XkcdCode if available or Id if not. </summary> | |||||
public string Url => API.Endpoints.InviteUrl(XkcdCode ?? Code); | |||||
/// <summary> Returns the user that created this invite. </summary> | /// <summary> Returns the user that created this invite. </summary> | ||||
[JsonIgnore] | [JsonIgnore] | ||||
public User Inviter => _client.Users[_inviterId, _serverId]; | |||||
public User Inviter { get; private set; } | |||||
[JsonProperty("InviterId")] | |||||
private readonly string _inviterId; | |||||
/// <summary> Returns the server this invite is to. </summary> | /// <summary> Returns the server this invite is to. </summary> | ||||
[JsonIgnore] | [JsonIgnore] | ||||
public Server Server => _client.Servers[_serverId]; | |||||
public Server Server { get; private set; } | |||||
[JsonProperty("ServerId")] | |||||
private readonly string _serverId; | |||||
/// <summary> Returns the channel this invite is to. </summary> | /// <summary> Returns the channel this invite is to. </summary> | ||||
[JsonIgnore] | [JsonIgnore] | ||||
public Channel Channel => _client.Channels[_channelId]; | |||||
public Channel Channel { get; private set; } | |||||
[JsonProperty("ChannelId")] | |||||
private readonly string _channelId; | |||||
internal Invite(DiscordClient client, string code, string xkcdPass, string serverId) | |||||
internal Invite(DiscordClient client, string code, string xkcdPass, string serverId, string inviterId, string channelId) | |||||
: base(client, code) | : base(client, code) | ||||
{ | { | ||||
XkcdPass = xkcdPass; | |||||
XkcdCode = xkcdPass; | |||||
_serverId = serverId; | _serverId = serverId; | ||||
_inviterId = inviterId; | |||||
_channelId = channelId; | |||||
} | } | ||||
internal override void OnCached() { } | |||||
internal override void OnUncached() { } | |||||
public override string ToString() => XkcdPass ?? Id; | |||||
internal override void OnCached() | |||||
{ | |||||
var server = _client.Servers[_serverId]; | |||||
if (server == null) | |||||
server = new Server(_client, _serverId); | |||||
Server = server; | |||||
if (_inviterId != null) | |||||
{ | |||||
var inviter = _client.Users[_inviterId, _serverId]; | |||||
if (inviter == null) | |||||
inviter = new User(_client, _inviterId, _serverId); | |||||
Inviter = inviter; | |||||
} | |||||
internal void Update(InviteReference model) | |||||
if (_channelId != null) | |||||
{ | |||||
var channel = _client.Channels[_channelId]; | |||||
if (channel == null) | |||||
channel = new Channel(_client, _channelId, _serverId, null); | |||||
Channel = channel; | |||||
} | |||||
} | |||||
internal override void OnUncached() | |||||
{ | { | ||||
if (model.Channel != null) | |||||
_channelId = model.Channel.Id; | |||||
if (model.Inviter != null) | |||||
_inviterId = model.Inviter.Id; | |||||
Server = null; | |||||
Inviter = null; | |||||
Channel = null; | |||||
} | } | ||||
public override string ToString() => XkcdCode ?? Id; | |||||
internal void Update(InviteInfo model) | internal void Update(InviteInfo model) | ||||
{ | { | ||||
Update(model as InviteReference); | |||||
if (model.IsRevoked != null) | if (model.IsRevoked != null) | ||||
IsRevoked = model.IsRevoked.Value; | IsRevoked = model.IsRevoked.Value; | ||||
if (model.IsTemporary != null) | if (model.IsTemporary != null) | ||||
@@ -157,15 +157,18 @@ namespace Discord | |||||
} | } | ||||
internal override void OnCached() | internal override void OnCached() | ||||
{ | { | ||||
//References | |||||
var channel = _client.Channels[_channelId]; | var channel = _client.Channels[_channelId]; | ||||
channel.AddMessage(this); | channel.AddMessage(this); | ||||
Channel = channel; | Channel = channel; | ||||
} | } | ||||
internal override void OnUncached() | internal override void OnUncached() | ||||
{ | { | ||||
//References | |||||
var channel = Channel; | var channel = Channel; | ||||
if (channel != null) | if (channel != null) | ||||
channel.RemoveMessage(this); | channel.RemoveMessage(this); | ||||
Channel = null; | |||||
} | } | ||||
internal void Update(MessageInfo model) | internal void Update(MessageInfo model) | ||||
@@ -28,10 +28,11 @@ namespace Discord | |||||
public Server Server { get; private set; } | public Server Server { get; private set; } | ||||
/// <summary> Returns true if this is the role representing all users in a server. </summary> | /// <summary> Returns true if this is the role representing all users in a server. </summary> | ||||
public bool IsEveryone => Id == _serverId; | |||||
public bool IsEveryone => _serverId == null || Id == _serverId; | |||||
/// <summary> Returns a list of all members in this role. </summary> | /// <summary> Returns a list of all members in this role. </summary> | ||||
[JsonIgnore] | [JsonIgnore] | ||||
public IEnumerable<User> Members => IsEveryone ? Server.Members : Server.Members.Where(x => x.HasRole(this)); | public IEnumerable<User> Members => IsEveryone ? Server.Members : Server.Members.Where(x => x.HasRole(this)); | ||||
//TODO: Add local members cache | |||||
internal Role(DiscordClient client, string id, string serverId) | internal Role(DiscordClient client, string id, string serverId) | ||||
: base(client, id) | : base(client, id) | ||||
@@ -41,18 +42,17 @@ namespace Discord | |||||
Permissions.Lock(); | Permissions.Lock(); | ||||
Color = new Color(0); | Color = new Color(0); | ||||
Color.Lock(); | Color.Lock(); | ||||
if (IsEveryone) | |||||
Position = int.MinValue; | |||||
} | } | ||||
internal override void OnCached() | internal override void OnCached() | ||||
{ | { | ||||
//References | |||||
var server = _client.Servers[_serverId]; | var server = _client.Servers[_serverId]; | ||||
server.AddRole(this); | server.AddRole(this); | ||||
Server = server; | Server = server; | ||||
} | } | ||||
internal override void OnUncached() | internal override void OnUncached() | ||||
{ | { | ||||
//References | |||||
var server = Server; | var server = Server; | ||||
if (server != null) | if (server != null) | ||||
server.RemoveRole(this); | server.RemoveRole(this); | ||||
@@ -8,21 +8,11 @@ using System.Linq; | |||||
namespace Discord | namespace Discord | ||||
{ | { | ||||
public sealed class Server : CachedObject | public sealed class Server : CachedObject | ||||
{ | |||||
private readonly ConcurrentDictionary<string, bool> _bans; | |||||
private readonly ConcurrentDictionary<string, Channel> _channels; | |||||
private readonly ConcurrentDictionary<string, User> _members; | |||||
private readonly ConcurrentDictionary<string, Role> _roles; | |||||
private readonly ConcurrentDictionary<string, Invite> _invites; | |||||
private string _ownerId; | |||||
{ | |||||
/// <summary> Returns the name of this channel. </summary> | /// <summary> Returns the name of this channel. </summary> | ||||
public string Name { get; private set; } | public string Name { get; private set; } | ||||
/// <summary> Returns the current logged-in user's data for this server. </summary> | /// <summary> Returns the current logged-in user's data for this server. </summary> | ||||
public User CurrentMember { get; internal set; } | public User CurrentMember { get; internal set; } | ||||
/// <summary> Returns true if this is a virtual server used by Discord.Net and not a real Discord server. </summary> | |||||
public bool IsVirtual { get; internal set; } | |||||
/// <summary> Returns the amount of time (in seconds) a user must be inactive for until they are automatically moved to the AFK channel (see AFKChannel). </summary> | /// <summary> Returns the amount of time (in seconds) a user must be inactive for until they are automatically moved to the AFK channel (see AFKChannel). </summary> | ||||
public int AFKTimeout { get; private set; } | public int AFKTimeout { get; private set; } | ||||
@@ -38,49 +28,49 @@ namespace Discord | |||||
/// <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 { get; private set; } | public User Owner { get; private set; } | ||||
/// <summary> Returns the id of the AFK voice channel for this server (see AFKTimeout). </summary> | |||||
public string AFKChannelId { get; private set; } | |||||
private string _ownerId; | |||||
/// <summary> Returns the AFK voice channel for this server (see AFKTimeout). </summary> | /// <summary> Returns the AFK voice channel for this server (see AFKTimeout). </summary> | ||||
[JsonIgnore] | [JsonIgnore] | ||||
public Channel AFKChannel => _client.Channels[AFKChannelId]; | |||||
/// <summary> Returns the id of the default channel for this server. </summary> | |||||
public string DefaultChannelId => Id; | |||||
public Channel AFKChannel { get; private set; } | |||||
/// <summary> Returns the default channel for this server. </summary> | /// <summary> Returns the default channel for this server. </summary> | ||||
[JsonIgnore] | [JsonIgnore] | ||||
public Channel DefaultChannel => _client.Channels[DefaultChannelId]; | |||||
public Channel DefaultChannel { get; private set; } | |||||
/// <summary> Returns a collection of the ids of all users banned on this server. </summary> | /// <summary> Returns a collection of the ids of all users banned on this server. </summary> | ||||
[JsonIgnore] | [JsonIgnore] | ||||
public IEnumerable<string> Bans => _bans.Select(x => x.Key); | public IEnumerable<string> Bans => _bans.Select(x => x.Key); | ||||
private ConcurrentDictionary<string, bool> _bans; | |||||
/// <summary> Returns a collection of all channels within this server. </summary> | /// <summary> Returns a collection of all channels within this server. </summary> | ||||
[JsonIgnore] | [JsonIgnore] | ||||
public IEnumerable<Channel> Channels => _channels.Select(x => _client.Channels[x.Key]); | |||||
public IEnumerable<Channel> Channels => _channels.Select(x => x.Value); | |||||
/// <summary> Returns a collection of all channels within this server. </summary> | /// <summary> Returns a collection of all channels within this server. </summary> | ||||
[JsonIgnore] | [JsonIgnore] | ||||
public IEnumerable<Channel> TextChannels => _channels.Select(x => _client.Channels[x.Key]).Where(x => x.Type == ChannelType.Text); | |||||
public IEnumerable<Channel> TextChannels => _channels.Select(x => x.Value).Where(x => x.Type == ChannelType.Text); | |||||
/// <summary> Returns a collection of all channels within this server. </summary> | /// <summary> Returns a collection of all channels within this server. </summary> | ||||
[JsonIgnore] | [JsonIgnore] | ||||
public IEnumerable<Channel> VoiceChannels => _channels.Select(x => _client.Channels[x.Key]).Where(x => x.Type == ChannelType.Voice); | |||||
public IEnumerable<Channel> VoiceChannels => _channels.Select(x => x.Value).Where(x => x.Type == ChannelType.Voice); | |||||
private ConcurrentDictionary<string, Channel> _channels; | |||||
/// <summary> Returns a collection of all invites to this server. </summary> | /// <summary> Returns a collection of all invites to this server. </summary> | ||||
[JsonIgnore] | [JsonIgnore] | ||||
public IEnumerable<Invite> Invites => _invites.Values; | public IEnumerable<Invite> Invites => _invites.Values; | ||||
private ConcurrentDictionary<string, Invite> _invites; | |||||
/// <summary> Returns a collection of all users within this server with their server-specific data. </summary> | /// <summary> Returns a collection of all users within this server with their server-specific data. </summary> | ||||
[JsonIgnore] | [JsonIgnore] | ||||
public IEnumerable<User> Members => _members.Select(x => _client.Users[x.Key, Id]); | |||||
public IEnumerable<User> Members => _members.Select(x => x.Value); | |||||
private ConcurrentDictionary<string, User> _members; | |||||
/// <summary> Return the id of the role representing all users in a server. </summary> | |||||
public string EveryoneRoleId => Id; | |||||
/// <summary> Return the the role representing all users in a server. </summary> | /// <summary> Return the the role representing all users in a server. </summary> | ||||
[JsonIgnore] | [JsonIgnore] | ||||
public Role EveryoneRole => _client.Roles[EveryoneRoleId]; | |||||
public Role EveryoneRole { get; private set; } | |||||
/// <summary> Returns a collection of all roles within this server. </summary> | /// <summary> Returns a collection of all roles within this server. </summary> | ||||
[JsonIgnore] | [JsonIgnore] | ||||
public IEnumerable<Role> Roles => _roles.Select(x => _client.Roles[x.Key]); | |||||
public IEnumerable<Role> Roles => _roles.Select(x => x.Value); | |||||
private ConcurrentDictionary<string, Role> _roles; | |||||
internal Server(DiscordClient client, string id) | internal Server(DiscordClient client, string id) | ||||
: base(client, id) | : base(client, id) | ||||
@@ -98,30 +88,37 @@ namespace Discord | |||||
internal override void OnUncached() | internal override void OnUncached() | ||||
{ | { | ||||
//Global Cache | //Global Cache | ||||
var channels = _client.Channels; | |||||
foreach (var channel in _channels) | |||||
channels.TryRemove(channel.Key); | |||||
var members = _client.Users; | |||||
foreach (var user in _members) | |||||
members.TryRemove(user.Key, Id); | |||||
var roles = _client.Roles; | |||||
foreach (var role in _roles) | |||||
roles.TryRemove(role.Key); | |||||
var globalChannels = _client.Channels; | |||||
var channels = _channels; | |||||
foreach (var channel in channels) | |||||
globalChannels.TryRemove(channel.Key); | |||||
channels.Clear(); | |||||
var globalMembers = _client.Users; | |||||
var members = _members; | |||||
foreach (var user in members) | |||||
globalMembers.TryRemove(user.Key, Id); | |||||
members.Clear(); | |||||
var globalRoles = _client.Roles; | |||||
var roles = _roles; | |||||
foreach (var role in roles) | |||||
globalRoles.TryRemove(role.Key); | |||||
roles.Clear(); | |||||
//Local Cache | //Local Cache | ||||
foreach (var invite in _invites) | |||||
var invites = _invites; | |||||
foreach (var invite in invites) | |||||
invite.Value.Uncache(); | invite.Value.Uncache(); | ||||
_invites.Clear(); | |||||
invites.Clear(); | |||||
_bans.Clear(); | _bans.Clear(); | ||||
} | |||||
} | |||||
internal void Update(GuildInfo model) | internal void Update(GuildInfo model) | ||||
{ | { | ||||
//Can be null | //Can be null | ||||
AFKChannelId = model.AFKChannelId; | |||||
AFKChannel = _client.Channels[model.AFKChannelId]; | |||||
if (model.AFKTimeout != null) | if (model.AFKTimeout != null) | ||||
AFKTimeout = model.AFKTimeout.Value; | AFKTimeout = model.AFKTimeout.Value; | ||||
@@ -139,18 +136,18 @@ namespace Discord | |||||
if (model.Roles != null) | if (model.Roles != null) | ||||
{ | { | ||||
var roles = _client.Roles; | |||||
foreach (var subModel in model.Roles) | |||||
var roleCache = _client.Roles; | |||||
foreach (var x in model.Roles) | |||||
{ | { | ||||
var role = roles.GetOrAdd(subModel.Id, Id); | |||||
role.Update(subModel); | |||||
} | |||||
} | |||||
var role = roleCache.GetOrAdd(x.Id, Id); | |||||
role.Update(x); | |||||
} | |||||
} | |||||
} | } | ||||
internal void Update(ExtendedGuildInfo model) | internal void Update(ExtendedGuildInfo model) | ||||
{ | { | ||||
Update(model as GuildInfo); | Update(model as GuildInfo); | ||||
var channels = _client.Channels; | var channels = _client.Channels; | ||||
foreach (var subModel in model.Channels) | foreach (var subModel in model.Channels) | ||||
{ | { | ||||
@@ -158,22 +155,22 @@ namespace Discord | |||||
channel.Update(subModel); | channel.Update(subModel); | ||||
} | } | ||||
var users = _client.GlobalUsers; | |||||
var members = _client.Users; | |||||
var usersCache = _client.GlobalUsers; | |||||
var membersCache = _client.Users; | |||||
foreach (var subModel in model.Members) | foreach (var subModel in model.Members) | ||||
{ | { | ||||
var member = members.GetOrAdd(subModel.User.Id, Id); | |||||
var member = membersCache.GetOrAdd(subModel.User.Id, Id); | |||||
member.Update(subModel); | member.Update(subModel); | ||||
} | } | ||||
foreach (var subModel in model.VoiceStates) | foreach (var subModel in model.VoiceStates) | ||||
{ | { | ||||
var member = members[subModel.UserId, Id]; | |||||
var member = membersCache[subModel.UserId, Id]; | |||||
if (member != null) | if (member != null) | ||||
member.Update(subModel); | member.Update(subModel); | ||||
} | } | ||||
foreach (var subModel in model.Presences) | foreach (var subModel in model.Presences) | ||||
{ | { | ||||
var member = members[subModel.User.Id, Id]; | |||||
var member = membersCache[subModel.User.Id, Id]; | |||||
if (member != null) | if (member != null) | ||||
member.Update(subModel); | member.Update(subModel); | ||||
} | } | ||||
@@ -193,9 +190,13 @@ namespace Discord | |||||
internal void AddChannel(Channel channel) | internal void AddChannel(Channel channel) | ||||
{ | { | ||||
_channels.TryAdd(channel.Id, channel); | |||||
foreach (var member in Members) | |||||
member.AddChannel(channel); | |||||
if (_channels.TryAdd(channel.Id, channel)) | |||||
{ | |||||
if (channel.Id == Id) | |||||
DefaultChannel = channel; | |||||
foreach (var member in Members) | |||||
member.AddChannel(channel); | |||||
} | |||||
} | } | ||||
internal void RemoveChannel(Channel channel) | internal void RemoveChannel(Channel channel) | ||||
{ | { | ||||
@@ -213,7 +214,7 @@ namespace Discord | |||||
foreach (var channel in Channels) | foreach (var channel in Channels) | ||||
{ | { | ||||
member.AddChannel(channel); | member.AddChannel(channel); | ||||
channel.InvalidatePermissionsCache(member.Id); | |||||
channel.InvalidatePermissionsCache(member); | |||||
} | } | ||||
} | } | ||||
internal void RemoveMember(User member) | internal void RemoveMember(User member) | ||||
@@ -221,13 +222,27 @@ namespace Discord | |||||
foreach (var channel in Channels) | foreach (var channel in Channels) | ||||
{ | { | ||||
member.RemoveChannel(channel); | member.RemoveChannel(channel); | ||||
channel.InvalidatePermissionsCache(member.Id); | |||||
channel.InvalidatePermissionsCache(member); | |||||
} | } | ||||
_members.TryRemove(member.Id, out member); | _members.TryRemove(member.Id, out member); | ||||
} | } | ||||
internal void HasMember(User 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); | |||||
internal void AddRole(Role role) | |||||
{ | |||||
if (_roles.TryAdd(role.Id, role)) | |||||
{ | |||||
if (role.Id == Id) | |||||
EveryoneRole = role; | |||||
} | |||||
} | |||||
internal void RemoveRole(Role role) | |||||
{ | |||||
if (_roles.TryRemove(role.Id, out role)) | |||||
{ | |||||
if (role.Id == Id) | |||||
EveryoneRole = null; | |||||
} | |||||
} | |||||
} | } | ||||
} | } |
@@ -9,17 +9,14 @@ namespace Discord | |||||
{ | { | ||||
public class User : CachedObject | public class User : CachedObject | ||||
{ | { | ||||
private static readonly string[] _initialRoleIds = new string[0]; | |||||
internal static string GetId(string userId, string serverId) => (serverId ?? "Private") + '_' + userId; | internal static string GetId(string userId, string serverId) => (serverId ?? "Private") + '_' + userId; | ||||
private ConcurrentDictionary<string, Channel> _channels; | private ConcurrentDictionary<string, Channel> _channels; | ||||
private ConcurrentDictionary<string, ChannelPermissions> _permissions; | private ConcurrentDictionary<string, ChannelPermissions> _permissions; | ||||
private ServerPermissions _serverPermissions; | private ServerPermissions _serverPermissions; | ||||
private string[] _roleIds; | |||||
/// <summary> Returns a unique identifier combining this user's id with its server's. </summary> | /// <summary> Returns a unique identifier combining this user's id with its server's. </summary> | ||||
internal string UniqueId => GetId(Id, ServerId); | |||||
internal string UniqueId => GetId(Id, _serverId); | |||||
/// <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; private set; } | public string Name { get; private set; } | ||||
/// <summary> Returns a by-name unique identifier separating this user from others with the same name. </summary> | /// <summary> Returns a by-name unique identifier separating this user from others with the same name. </summary> | ||||
@@ -52,48 +49,55 @@ namespace Discord | |||||
private DateTime _lastOnline; | private DateTime _lastOnline; | ||||
[JsonIgnore] | [JsonIgnore] | ||||
internal GlobalUser GlobalUser => _client.GlobalUsers[Id]; | |||||
internal GlobalUser GlobalUser { get; private set; } | |||||
public string ServerId { get; } | |||||
[JsonIgnore] | [JsonIgnore] | ||||
public Server Server => _client.Servers[ServerId]; | |||||
public Server Server { get; private set; } | |||||
private string _serverId; | |||||
public string VoiceChannelId { get; private set; } | |||||
[JsonIgnore] | [JsonIgnore] | ||||
public Channel VoiceChannel => _client.Channels[VoiceChannelId]; | |||||
public Channel VoiceChannel { get; private set; } | |||||
[JsonIgnore] | [JsonIgnore] | ||||
public IEnumerable<Role> Roles => _roleIds.Select(x => _client.Roles[x]); | |||||
public IEnumerable<Role> Roles => _roles.Select(x => x.Value); | |||||
private Dictionary<string, Role> _roles; | |||||
/// <summary> Returns a collection of all messages this user has sent on this server that are still in cache. </summary> | /// <summary> Returns a collection of all messages this user has sent on this server that are still in cache. </summary> | ||||
[JsonIgnore] | [JsonIgnore] | ||||
public IEnumerable<Message> Messages => _client.Messages.Where(x => x.UserId == Id && x.Server.Id == ServerId); | |||||
public IEnumerable<Message> Messages => _client.Messages.Where(x => x.UserId == Id && x.Server.Id == _serverId); | |||||
/// <summary> Returns a collection of all channels this user is a member of. </summary> | /// <summary> Returns a collection of all channels this user is a member of. </summary> | ||||
[JsonIgnore] | [JsonIgnore] | ||||
public IEnumerable<Channel> Channels => _client.Channels.Where(x => x.Server.Id == ServerId && x.Members == this); | |||||
public IEnumerable<Channel> Channels => _channels.Select(x => x.Value); | |||||
internal User(DiscordClient client, string id, string serverId) | internal User(DiscordClient client, string id, string serverId) | ||||
: base(client, id) | : base(client, id) | ||||
{ | { | ||||
ServerId = serverId; | |||||
_serverId = serverId; | |||||
Status = UserStatus.Offline; | Status = UserStatus.Offline; | ||||
_roleIds = _initialRoleIds; | |||||
//_roles = new Dictionary<string, Role>(); | |||||
_channels = new ConcurrentDictionary<string, Channel>(); | _channels = new ConcurrentDictionary<string, Channel>(); | ||||
_permissions = new ConcurrentDictionary<string, ChannelPermissions>(); | _permissions = new ConcurrentDictionary<string, ChannelPermissions>(); | ||||
_serverPermissions = new ServerPermissions(); | _serverPermissions = new ServerPermissions(); | ||||
} | } | ||||
internal override void OnCached() | internal override void OnCached() | ||||
{ | { | ||||
var server = Server; | |||||
server.AddMember(this); | |||||
if (Id == _client.CurrentUserId) | |||||
server.CurrentMember = this; | |||||
var server = _client.Servers[_serverId]; | |||||
if (server != null) | |||||
{ | |||||
server.AddMember(this); | |||||
if (Id == _client.CurrentUserId) | |||||
server.CurrentMember = this; | |||||
Server = server; | |||||
} | |||||
var user = GlobalUser; | |||||
if (server == null || !server.IsVirtual) | |||||
user.AddUser(this); | |||||
var user = _client.GlobalUsers.GetOrAdd(Id); | |||||
user.AddUser(this); | |||||
GlobalUser = user; | |||||
} | } | ||||
internal override void OnUncached() | internal override void OnUncached() | ||||
{ | { | ||||
//References | |||||
var server = Server; | var server = Server; | ||||
if (server != null) | if (server != null) | ||||
{ | { | ||||
@@ -101,9 +105,12 @@ namespace Discord | |||||
if (Id == _client.CurrentUserId) | if (Id == _client.CurrentUserId) | ||||
server.CurrentMember = null; | server.CurrentMember = null; | ||||
} | } | ||||
Server = null; | |||||
var globalUser = GlobalUser; | var globalUser = GlobalUser; | ||||
if (globalUser != null) | if (globalUser != null) | ||||
globalUser.RemoveUser(this); | globalUser.RemoveUser(this); | ||||
GlobalUser = null; | |||||
} | } | ||||
public override string ToString() => Id; | public override string ToString() => Id; | ||||
@@ -124,7 +131,7 @@ namespace Discord | |||||
if (model.JoinedAt.HasValue) | if (model.JoinedAt.HasValue) | ||||
JoinedAt = model.JoinedAt.Value; | JoinedAt = model.JoinedAt.Value; | ||||
if (model.Roles != null) | if (model.Roles != null) | ||||
UpdateRoles(model.Roles); | |||||
UpdateRoles(model.Roles.Select(x => _client.Roles[x])); | |||||
UpdateServerPermissions(); | UpdateServerPermissions(); | ||||
} | } | ||||
@@ -142,7 +149,7 @@ namespace Discord | |||||
Update(model.User as UserReference); | Update(model.User as UserReference); | ||||
if (model.Roles != null) | if (model.Roles != null) | ||||
UpdateRoles(model.Roles); | |||||
UpdateRoles(model.Roles.Select(x => _client.Roles[x])); | |||||
if (model.Status != null && Status != model.Status) | if (model.Status != null && Status != model.Status) | ||||
{ | { | ||||
Status = UserStatus.FromString(model.Status); | Status = UserStatus.FromString(model.Status); | ||||
@@ -165,7 +172,7 @@ namespace Discord | |||||
Token = model.Token; | Token = model.Token; | ||||
if (model.ChannelId != null) | if (model.ChannelId != null) | ||||
VoiceChannelId = model.ChannelId; | |||||
VoiceChannel = _client.Channels[model.ChannelId]; | |||||
if (model.IsSelfDeafened != null) | if (model.IsSelfDeafened != null) | ||||
IsSelfDeafened = model.IsSelfDeafened.Value; | IsSelfDeafened = model.IsSelfDeafened.Value; | ||||
if (model.IsSelfMuted != null) | if (model.IsSelfMuted != null) | ||||
@@ -173,14 +180,16 @@ namespace Discord | |||||
if (model.IsServerSuppressed != null) | if (model.IsServerSuppressed != null) | ||||
IsServerSuppressed = model.IsServerSuppressed.Value; | IsServerSuppressed = model.IsServerSuppressed.Value; | ||||
} | } | ||||
private void UpdateRoles(string[] roleIds) | |||||
private void UpdateRoles(IEnumerable<Role> roles) | |||||
{ | { | ||||
//Set roles, with the everyone role added too | |||||
string[] newRoles = new string[roleIds.Length + 1]; | |||||
newRoles[0] = ServerId; //Everyone | |||||
for (int i = 0; i < roleIds.Length; i++) | |||||
newRoles[i + 1] = roleIds[i]; | |||||
_roleIds = newRoles; | |||||
var newRoles = roles.ToDictionary(x => x.Id, x => x); | |||||
Role everyone; | |||||
if (_serverId != null) | |||||
everyone = Server.EveryoneRole; | |||||
else | |||||
everyone = _client.Roles.VirtualEveryone; | |||||
newRoles.Add(everyone.Id, everyone); | |||||
_roles = newRoles; | |||||
} | } | ||||
internal void UpdateActivity(DateTime? activity = null) | internal void UpdateActivity(DateTime? activity = null) | ||||
@@ -191,7 +200,7 @@ namespace Discord | |||||
internal void UpdateChannelPermissions(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 | |||||
if (_roles == null) return; // We don't have all our data processed yet, this will be called again soon | |||||
var server = Server; | var server = Server; | ||||
if (server == null || channel.Server != server) return; | if (server == null || channel.Server != server) return; | ||||
@@ -225,14 +234,14 @@ namespace Discord | |||||
if (newPermissions != oldPermissions) | if (newPermissions != oldPermissions) | ||||
{ | { | ||||
permissions.SetRawValueInternal(newPermissions); | permissions.SetRawValueInternal(newPermissions); | ||||
channel.InvalidMembersCache(); | |||||
channel.InvalidateMembersCache(); | |||||
} | } | ||||
permissions.SetRawValueInternal(newPermissions); | permissions.SetRawValueInternal(newPermissions); | ||||
} | } | ||||
internal void UpdateServerPermissions() | internal void UpdateServerPermissions() | ||||
{ | { | ||||
if (_roleIds == null) return; // We don't have all our data processed yet, this will be called again soon | |||||
if (_roles == null) return; // We don't have all our data processed yet, this will be called again soon | |||||
var server = Server; | var server = Server; | ||||
if (server == null) return; | if (server == null) return; | ||||
@@ -290,7 +299,7 @@ namespace Discord | |||||
{ | { | ||||
if (role == null) throw new ArgumentNullException(nameof(role)); | if (role == null) throw new ArgumentNullException(nameof(role)); | ||||
return _roleIds.Contains(role.Id); | |||||
return _roles.ContainsKey(role.Id); | |||||
} | } | ||||
} | } | ||||
} | } |
@@ -53,21 +53,21 @@ namespace Discord.Net.WebSockets | |||||
_connectedEvent = new ManualResetEventSlim(false); | _connectedEvent = new ManualResetEventSlim(false); | ||||
_engine = new WebSocketSharpEngine(this, client.Config); | _engine = new WebSocketSharpEngine(this, client.Config); | ||||
_engine.BinaryMessage += async (s, e) => | |||||
_engine.BinaryMessage += (s, e) => | |||||
{ | { | ||||
using (var compressed = new MemoryStream(e.Data, 2, e.Data.Length - 2)) | using (var compressed = new MemoryStream(e.Data, 2, e.Data.Length - 2)) | ||||
using (var decompressed = new MemoryStream()) | using (var decompressed = new MemoryStream()) | ||||
{ | { | ||||
using (var zlib = new DeflateStream(compressed, CompressionMode.Decompress)) | using (var zlib = new DeflateStream(compressed, CompressionMode.Decompress)) | ||||
await zlib.CopyToAsync(decompressed); | |||||
zlib.CopyTo(decompressed); | |||||
decompressed.Position = 0; | decompressed.Position = 0; | ||||
using (var reader = new StreamReader(decompressed)) | using (var reader = new StreamReader(decompressed)) | ||||
await ProcessMessage(await reader.ReadToEndAsync()); | |||||
ProcessMessage(reader.ReadToEnd()).Wait(); | |||||
} | } | ||||
}; | }; | ||||
_engine.TextMessage += async (s, e) => | |||||
_engine.TextMessage += (s, e) => | |||||
{ | { | ||||
await ProcessMessage(e.Message); | |||||
/*await*/ ProcessMessage(e.Message).Wait(); | |||||
}; | }; | ||||
} | } | ||||