| @@ -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(); | |||||
| }; | }; | ||||
| } | } | ||||