| @@ -148,27 +148,6 @@ | |||
| <Compile Include="..\Discord.Net\Audio\VoiceBuffer.cs"> | |||
| <Link>Audio\VoiceBuffer.cs</Link> | |||
| </Compile> | |||
| <Compile Include="..\Discord.Net\Collections\AsyncCollection.cs"> | |||
| <Link>Collections\AsyncCollection.cs</Link> | |||
| </Compile> | |||
| <Compile Include="..\Discord.Net\Collections\Channels.cs"> | |||
| <Link>Collections\Channels.cs</Link> | |||
| </Compile> | |||
| <Compile Include="..\Discord.Net\Collections\Members.cs"> | |||
| <Link>Collections\Members.cs</Link> | |||
| </Compile> | |||
| <Compile Include="..\Discord.Net\Collections\Messages.cs"> | |||
| <Link>Collections\Messages.cs</Link> | |||
| </Compile> | |||
| <Compile Include="..\Discord.Net\Collections\Roles.cs"> | |||
| <Link>Collections\Roles.cs</Link> | |||
| </Compile> | |||
| <Compile Include="..\Discord.Net\Collections\Servers.cs"> | |||
| <Link>Collections\Servers.cs</Link> | |||
| </Compile> | |||
| <Compile Include="..\Discord.Net\Collections\Users.cs"> | |||
| <Link>Collections\Users.cs</Link> | |||
| </Compile> | |||
| <Compile Include="..\Discord.Net\DiscordAPIClient.cs"> | |||
| <Link>DiscordAPIClient.cs</Link> | |||
| </Compile> | |||
| @@ -250,6 +229,9 @@ | |||
| <Compile Include="..\Discord.Net\HttpException.cs"> | |||
| <Link>HttpException.cs</Link> | |||
| </Compile> | |||
| <Compile Include="..\Discord.Net\Models\AsyncCollection.cs"> | |||
| <Link>Models\AsyncCollection.cs</Link> | |||
| </Compile> | |||
| <Compile Include="..\Discord.Net\Models\Channel.cs"> | |||
| <Link>Models\Channel.cs</Link> | |||
| </Compile> | |||
| @@ -1,47 +0,0 @@ | |||
| using System; | |||
| namespace Discord.Collections | |||
| { | |||
| internal sealed class Channels : AsyncCollection<Channel> | |||
| { | |||
| public Channels(DiscordClient client, object writerLock) | |||
| : base(client, writerLock) { } | |||
| public Channel GetOrAdd(string id, string serverId, string recipientId = null) | |||
| => GetOrAdd(id, () => new Channel(_client, id, serverId, recipientId)); | |||
| protected override void OnCreated(Channel item) | |||
| { | |||
| if (!item.IsPrivate) | |||
| item.Server.AddChannel(item.Id); | |||
| if (item.RecipientId != null) | |||
| { | |||
| var user = item.Recipient; | |||
| if (user.PrivateChannelId != null) | |||
| throw new Exception("User already has a private channel."); | |||
| user.PrivateChannelId = item.Id; | |||
| user.AddRef(); | |||
| } | |||
| } | |||
| protected override void OnRemoved(Channel item) | |||
| { | |||
| if (!item.IsPrivate) | |||
| { | |||
| var server = item.Server; | |||
| if (server != null) | |||
| item.Server.RemoveChannel(item.Id); | |||
| } | |||
| if (item.RecipientId != null) | |||
| { | |||
| var user = item.Recipient; | |||
| if (user != null) | |||
| { | |||
| if (user.PrivateChannelId != item.Id) | |||
| throw new Exception("User has a different private channel."); | |||
| user.PrivateChannelId = null; | |||
| user.RemoveRef(); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -1,42 +0,0 @@ | |||
| namespace Discord.Collections | |||
| { | |||
| internal sealed class Members : AsyncCollection<Member> | |||
| { | |||
| public Members(DiscordClient client, object writerLock) | |||
| : base(client, writerLock) { } | |||
| private string GetKey(string userId, string serverId) | |||
| => serverId + '_' + userId; | |||
| public Member this[string userId, string serverId] | |||
| => this[GetKey(userId, serverId)]; | |||
| public Member GetOrAdd(string userId, string serverId) | |||
| => GetOrAdd(GetKey(userId, serverId), () => new Member(_client, userId, serverId)); | |||
| public Member TryRemove(string userId, string serverId) | |||
| => TryRemove(GetKey(userId, serverId)); | |||
| protected override void OnCreated(Member item) | |||
| { | |||
| item.Server.AddMember(item); | |||
| item.User.AddServer(item.ServerId); | |||
| item.User.AddRef(); | |||
| if (item.UserId == _client.CurrentUserId) | |||
| item.Server.CurrentMember = item; | |||
| } | |||
| protected override void OnRemoved(Member item) | |||
| { | |||
| var server = item.Server; | |||
| if (server != null) | |||
| { | |||
| server.RemoveMember(item); | |||
| if (item.UserId == _client.CurrentUserId) | |||
| server.CurrentMember = null; | |||
| } | |||
| var user = item.User; | |||
| if (user != null) | |||
| { | |||
| user.RemoveServer(item.ServerId); | |||
| user.RemoveRef(); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -1,26 +0,0 @@ | |||
| namespace Discord.Collections | |||
| { | |||
| internal sealed class Messages : AsyncCollection<Message> | |||
| { | |||
| public Messages(DiscordClient client, object writerLock) | |||
| : base(client, writerLock) { } | |||
| public Message GetOrAdd(string id, string channelId, string userId) | |||
| => GetOrAdd(id, () => new Message(_client, id, channelId, userId)); | |||
| protected override void OnCreated(Message item) | |||
| { | |||
| item.Channel.AddMessage(item.Id); | |||
| item.User.AddRef(); | |||
| } | |||
| protected override void OnRemoved(Message item) | |||
| { | |||
| var channel = item.Channel; | |||
| if (channel != null) | |||
| channel.RemoveMessage(item.Id); | |||
| var user = item.User; | |||
| if (user != null) | |||
| user.RemoveRef(); | |||
| } | |||
| } | |||
| } | |||
| @@ -1,22 +0,0 @@ | |||
| namespace Discord.Collections | |||
| { | |||
| internal sealed class Roles : AsyncCollection<Role> | |||
| { | |||
| public Roles(DiscordClient client, object writerLock) | |||
| : base(client, writerLock) { } | |||
| public Role GetOrAdd(string id, string serverId) | |||
| => GetOrAdd(id, () => new Role(_client, id, serverId)); | |||
| protected override void OnCreated(Role item) | |||
| { | |||
| item.Server.AddRole(item.Id); | |||
| } | |||
| protected override void OnRemoved(Role item) | |||
| { | |||
| var server = item.Server; | |||
| if (server != null) | |||
| item.Server.RemoveRole(item.Id); | |||
| } | |||
| } | |||
| } | |||
| @@ -1,30 +0,0 @@ | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.Linq; | |||
| namespace Discord.Collections | |||
| { | |||
| internal sealed class Servers : AsyncCollection<Server> | |||
| { | |||
| public Servers(DiscordClient client, object writerLock) | |||
| : base(client, writerLock) { } | |||
| public Server GetOrAdd(string id) | |||
| => base.GetOrAdd(id, () => new Server(_client, id)); | |||
| protected override void OnRemoved(Server item) | |||
| { | |||
| var channels = _client.Channels; | |||
| foreach (var channelId in item.ChannelIds) | |||
| channels.TryRemove(channelId); | |||
| var members = _client.Members; | |||
| foreach (var userId in item.UserIds) | |||
| members.TryRemove(userId, item.Id); | |||
| var roles = _client.Roles; | |||
| foreach (var roleId in item.RoleIds) | |||
| roles.TryRemove(roleId); | |||
| } | |||
| } | |||
| } | |||
| @@ -1,10 +0,0 @@ | |||
| namespace Discord.Collections | |||
| { | |||
| internal sealed class Users : AsyncCollection<User> | |||
| { | |||
| public Users(DiscordClient client, object writerLock) | |||
| : base(client, writerLock) { } | |||
| public User GetOrAdd(string id) => GetOrAdd(id, () => new User(_client, id)); | |||
| } | |||
| } | |||
| @@ -5,6 +5,21 @@ using System.Threading.Tasks; | |||
| namespace Discord | |||
| { | |||
| public class BanEventArgs : EventArgs | |||
| { | |||
| public User User { get; } | |||
| public string UserId { get; } | |||
| public Server Server { get; } | |||
| public string ServerId => Server.Id; | |||
| internal BanEventArgs(User user, string userId, Server server) | |||
| { | |||
| User = user; | |||
| UserId = userId; | |||
| Server = server; | |||
| } | |||
| } | |||
| public partial class DiscordClient | |||
| { | |||
| public event EventHandler<BanEventArgs> BanAdded; | |||
| @@ -1,4 +1,3 @@ | |||
| using Discord.Collections; | |||
| using Discord.Net; | |||
| using System; | |||
| using System.Collections.Generic; | |||
| @@ -8,7 +7,16 @@ using System.Threading.Tasks; | |||
| namespace Discord | |||
| { | |||
| public sealed class ChannelEventArgs : EventArgs | |||
| internal sealed class Channels : AsyncCollection<Channel> | |||
| { | |||
| public Channels(DiscordClient client, object writerLock) | |||
| : base(client, writerLock, x => x.OnCached(), x => x.OnUncached()) { } | |||
| public Channel GetOrAdd(string id, string serverId, string recipientId = null) | |||
| => GetOrAdd(id, () => new Channel(_client, id, serverId, recipientId)); | |||
| } | |||
| public class ChannelEventArgs : EventArgs | |||
| { | |||
| public Channel Channel { get; } | |||
| public string ChannelId => Channel.Id; | |||
| @@ -1,4 +1,3 @@ | |||
| using Discord.Collections; | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.Linq; | |||
| @@ -6,54 +5,66 @@ using System.Threading.Tasks; | |||
| namespace Discord | |||
| { | |||
| public sealed class MemberTypingEventArgs : EventArgs | |||
| internal sealed class Members : AsyncCollection<Member> | |||
| { | |||
| public Members(DiscordClient client, object writerLock) | |||
| : base(client, writerLock, x => x.OnCached(), x => x.OnUncached()) { } | |||
| private string GetKey(string userId, string serverId) | |||
| => serverId + '_' + userId; | |||
| public Member this[string userId, string serverId] | |||
| => this[GetKey(userId, serverId)]; | |||
| public Member GetOrAdd(string userId, string serverId) | |||
| => GetOrAdd(GetKey(userId, serverId), () => new Member(_client, userId, serverId)); | |||
| public Member TryRemove(string userId, string serverId) | |||
| => TryRemove(GetKey(userId, serverId)); | |||
| } | |||
| public class MemberEventArgs : EventArgs | |||
| { | |||
| public Channel Channel { get; } | |||
| public string ChannelId => Channel.Id; | |||
| public Server Server => Channel.Server; | |||
| public string ServerId => Channel.ServerId; | |||
| public Member Member { get; } | |||
| public string UserId => User.Id; | |||
| public User User => Member.User; | |||
| public string UserId => Member.UserId; | |||
| public Server Server => Member.Server; | |||
| public string ServerId => Member.ServerId; | |||
| internal MemberEventArgs(Member member) { Member = member; } | |||
| } | |||
| public class MemberChannelEventArgs : MemberEventArgs | |||
| { | |||
| public Channel Channel { get; } | |||
| public string ChannelId => Channel.Id; | |||
| internal MemberTypingEventArgs(Member member, Channel channel) | |||
| internal MemberChannelEventArgs(Member member, Channel channel) | |||
| : base(member) | |||
| { | |||
| Member = member; | |||
| Channel = channel; | |||
| } | |||
| } | |||
| public sealed class MemberIsSpeakingEventArgs : EventArgs | |||
| public class MemberIsSpeakingEventArgs : MemberChannelEventArgs | |||
| { | |||
| public Channel Channel => Member.VoiceChannel; | |||
| public string ChannelId => Member.VoiceChannelId; | |||
| public Server Server => Member.Server; | |||
| public string ServerId => Member.ServerId; | |||
| public User User => Member.User; | |||
| public string UserId => Member.UserId; | |||
| public Member Member { get; } | |||
| public bool IsSpeaking { get; } | |||
| internal MemberIsSpeakingEventArgs(Member member, bool isSpeaking) | |||
| internal MemberIsSpeakingEventArgs(Member member, Channel channel, bool isSpeaking) | |||
| : base(member, channel) | |||
| { | |||
| Member = member; | |||
| IsSpeaking = isSpeaking; | |||
| } | |||
| } | |||
| public partial class DiscordClient | |||
| { | |||
| public event EventHandler<MemberTypingEventArgs> UserIsTyping; | |||
| public event EventHandler<MemberChannelEventArgs> UserIsTyping; | |||
| private void RaiseUserIsTyping(Member member, Channel channel) | |||
| { | |||
| if (UserIsTyping != null) | |||
| RaiseEvent(nameof(UserIsTyping), () => UserIsTyping(this, new MemberTypingEventArgs(member, channel))); | |||
| RaiseEvent(nameof(UserIsTyping), () => UserIsTyping(this, new MemberChannelEventArgs(member, channel))); | |||
| } | |||
| public event EventHandler<MemberIsSpeakingEventArgs> UserIsSpeaking; | |||
| private void RaiseUserIsSpeaking(Member member, bool isSpeaking) | |||
| private void RaiseUserIsSpeaking(Member member, Channel channel, bool isSpeaking) | |||
| { | |||
| if (UserIsSpeaking != null) | |||
| RaiseEvent(nameof(UserIsSpeaking), () => UserIsSpeaking(this, new MemberIsSpeakingEventArgs(member, isSpeaking))); | |||
| RaiseEvent(nameof(UserIsSpeaking), () => UserIsSpeaking(this, new MemberIsSpeakingEventArgs(member, channel, isSpeaking))); | |||
| } | |||
| internal Members Members => _members; | |||
| @@ -1,5 +1,4 @@ | |||
| using Discord.API; | |||
| using Discord.Collections; | |||
| using Discord.Net; | |||
| using System; | |||
| using System.Collections.Generic; | |||
| @@ -9,6 +8,30 @@ using System.Threading.Tasks; | |||
| namespace Discord | |||
| { | |||
| internal sealed class Messages : AsyncCollection<Message> | |||
| { | |||
| public Messages(DiscordClient client, object writerLock) | |||
| : base(client, writerLock, x => x.OnCached(), x => x.OnUncached()) { } | |||
| public Message GetOrAdd(string id, string channelId, string userId) | |||
| => GetOrAdd(id, () => new Message(_client, id, channelId, userId)); | |||
| } | |||
| public class MessageEventArgs : EventArgs | |||
| { | |||
| public Message Message { get; } | |||
| public string MessageId => Message.Id; | |||
| public Member Member => Message.Member; | |||
| public Channel Channel => Message.Channel; | |||
| public string ChannelId => Message.ChannelId; | |||
| public Server Server => Message.Server; | |||
| public string ServerId => Message.ServerId; | |||
| public User User => Member.User; | |||
| public string UserId => Message.UserId; | |||
| internal MessageEventArgs(Message msg) { Message = msg; } | |||
| } | |||
| public partial class DiscordClient | |||
| { | |||
| public const int MaxMessageSize = 2000; | |||
| @@ -1,4 +1,3 @@ | |||
| using Discord.Collections; | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.Linq; | |||
| @@ -6,6 +5,25 @@ using System.Threading.Tasks; | |||
| namespace Discord | |||
| { | |||
| internal sealed class Roles : AsyncCollection<Role> | |||
| { | |||
| public Roles(DiscordClient client, object writerLock) | |||
| : base(client, writerLock, x => x.OnCached(), x => x.OnUncached()) { } | |||
| public Role GetOrAdd(string id, string serverId) | |||
| => GetOrAdd(id, () => new Role(_client, id, serverId)); | |||
| } | |||
| public class RoleEventArgs : EventArgs | |||
| { | |||
| public Role Role { get; } | |||
| public string RoleId => Role.Id; | |||
| public Server Server => Role.Server; | |||
| public string ServerId => Role.ServerId; | |||
| internal RoleEventArgs(Role role) { Role = role; } | |||
| } | |||
| public partial class DiscordClient | |||
| { | |||
| public event EventHandler<RoleEventArgs> RoleCreated; | |||
| @@ -1,4 +1,3 @@ | |||
| using Discord.Collections; | |||
| using Discord.Net; | |||
| using System; | |||
| using System.Collections.Generic; | |||
| @@ -8,7 +7,16 @@ using System.Threading.Tasks; | |||
| namespace Discord | |||
| { | |||
| public sealed class ServerEventArgs : EventArgs | |||
| internal sealed class Servers : AsyncCollection<Server> | |||
| { | |||
| public Servers(DiscordClient client, object writerLock) | |||
| : base(client, writerLock, x => x.OnCached(), x => x.OnUncached()) { } | |||
| public Server GetOrAdd(string id) | |||
| => base.GetOrAdd(id, () => new Server(_client, id)); | |||
| } | |||
| public class ServerEventArgs : EventArgs | |||
| { | |||
| public Server Server { get; } | |||
| public string ServerId => Server.Id; | |||
| @@ -1,5 +1,4 @@ | |||
| using Discord.API; | |||
| using Discord.Collections; | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.Linq; | |||
| @@ -7,6 +6,14 @@ using System.Threading.Tasks; | |||
| namespace Discord | |||
| { | |||
| internal sealed class Users : AsyncCollection<User> | |||
| { | |||
| public Users(DiscordClient client, object writerLock) | |||
| : base(client, writerLock, x => x.OnCached(), x => x.OnUncached()) { } | |||
| public User GetOrAdd(string id) => GetOrAdd(id, () => new User(_client, id)); | |||
| } | |||
| public sealed class UserEventArgs : EventArgs | |||
| { | |||
| public User User { get; } | |||
| @@ -1,5 +1,6 @@ | |||
| using Discord.Audio; | |||
| using System; | |||
| using System.Threading.Tasks; | |||
| namespace Discord | |||
| { | |||
| @@ -1,5 +1,4 @@ | |||
| using Discord.API; | |||
| using Discord.Collections; | |||
| using Discord.Net.WebSockets; | |||
| using Newtonsoft.Json; | |||
| using System; | |||
| @@ -10,54 +9,6 @@ using System.Threading.Tasks; | |||
| namespace Discord | |||
| { | |||
| public sealed class MessageEventArgs : EventArgs | |||
| { | |||
| public Message Message { get; } | |||
| public string MessageId => Message.Id; | |||
| public Member Member => Message.Member; | |||
| public Channel Channel => Message.Channel; | |||
| public string ChannelId => Message.ChannelId; | |||
| public Server Server => Message.Server; | |||
| public string ServerId => Message.ServerId; | |||
| public User User => Member.User; | |||
| public string UserId => Message.UserId; | |||
| internal MessageEventArgs(Message msg) { Message = msg; } | |||
| } | |||
| public sealed class RoleEventArgs : EventArgs | |||
| { | |||
| public Role Role { get; } | |||
| public string RoleId => Role.Id; | |||
| public Server Server => Role.Server; | |||
| public string ServerId => Role.ServerId; | |||
| internal RoleEventArgs(Role role) { Role = role; } | |||
| } | |||
| public sealed class BanEventArgs : EventArgs | |||
| { | |||
| public User User { get; } | |||
| public string UserId { get; } | |||
| public Server Server { get; } | |||
| public string ServerId => Server.Id; | |||
| internal BanEventArgs(User user, string userId, Server server) | |||
| { | |||
| User = user; | |||
| UserId = userId; | |||
| Server = server; | |||
| } | |||
| } | |||
| public sealed class MemberEventArgs : EventArgs | |||
| { | |||
| public Member Member { get; } | |||
| public User User => Member.User; | |||
| public string UserId => Member.UserId; | |||
| public Server Server => Member.Server; | |||
| public string ServerId => Member.ServerId; | |||
| internal MemberEventArgs(Member member) { Member = member; } | |||
| } | |||
| /// <summary> Provides a connection to the DiscordApp service. </summary> | |||
| public partial class DiscordClient : DiscordWSClient | |||
| { | |||
| @@ -106,7 +57,7 @@ namespace Discord | |||
| if (member.ServerId == e.ServerId && member.IsSpeaking) | |||
| { | |||
| member.IsSpeaking = false; | |||
| RaiseUserIsSpeaking(member, false); | |||
| RaiseUserIsSpeaking(member, _channels[_voiceSocket.CurrentChannelId], false); | |||
| } | |||
| } | |||
| }; | |||
| @@ -157,10 +108,10 @@ namespace Discord | |||
| $"Deleted Role: {e.Server?.Name ?? "[Private]"}/{e.Role.Name}" + | |||
| (showIDs ? $" ({e.ServerId ?? "[Private]"}/{e.RoleId})." : "")); | |||
| BanAdded += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, | |||
| $"Added Ban: {e.Server?.Name ?? "[Private]"}/{e.User?.Name ?? "Unknown"}" + | |||
| $"Added Ban: {e.Server?.Name ?? "[Private]"}/{e.User?.Name ?? e.UserId}" + | |||
| (showIDs ? $" ({e.ServerId ?? "[Private]"}/{e.UserId})." : "")); | |||
| BanRemoved += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, | |||
| $"Removed Ban: {e.Server?.Name ?? "[Private]"}/{e.User?.Name ?? "Unknown"}" + | |||
| $"Removed Ban: {e.Server?.Name ?? "[Private]"}/{e.User?.Name ?? e.UserId}" + | |||
| (showIDs ? $" ({e.ServerId ?? "[Private]"}/{e.UserId})." : "")); | |||
| UserAdded += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, | |||
| $"Added Member: {e.Server?.Name ?? "[Private]"}/{e.User.Name}" + | |||
| @@ -246,7 +197,8 @@ namespace Discord | |||
| if (member.IsSpeaking != value) | |||
| { | |||
| member.IsSpeaking = value; | |||
| RaiseUserIsSpeaking(member, value); | |||
| var channel = _channels[_voiceSocket.CurrentChannelId]; | |||
| RaiseUserIsSpeaking(member, channel, value); | |||
| if (Config.TrackActivity) | |||
| member.UpdateActivity(); | |||
| } | |||
| @@ -665,7 +617,7 @@ namespace Discord | |||
| if (data.ChannelId != member.VoiceChannelId && member.IsSpeaking) | |||
| { | |||
| member.IsSpeaking = false; | |||
| RaiseUserIsSpeaking(member, false); | |||
| RaiseUserIsSpeaking(member, _channels[member.VoiceChannelId], false); | |||
| } | |||
| member.Update(data); | |||
| RaiseUserVoiceStateUpdated(member); | |||
| @@ -4,7 +4,7 @@ using System.Collections.Concurrent; | |||
| using System.Collections.Generic; | |||
| using System.Linq; | |||
| namespace Discord.Collections | |||
| namespace Discord | |||
| { | |||
| internal abstract class AsyncCollection<TValue> : IEnumerable<TValue> | |||
| where TValue : class | |||
| @@ -52,13 +52,16 @@ namespace Discord.Collections | |||
| protected readonly DiscordClient _client; | |||
| protected readonly ConcurrentDictionary<string, TValue> _dictionary; | |||
| private readonly Action<TValue> _onCache, _onUncache; | |||
| protected AsyncCollection(DiscordClient client, object writerLock) | |||
| protected AsyncCollection(DiscordClient client, object writerLock, Action<TValue> onCache, Action<TValue> onUncache) | |||
| { | |||
| _client = client; | |||
| _writerLock = writerLock; | |||
| _dictionary = new ConcurrentDictionary<string, TValue>(); | |||
| } | |||
| _onCache = onCache; | |||
| _onUncache = onUncache; | |||
| } | |||
| public TValue this[string key] | |||
| { | |||
| @@ -85,7 +88,7 @@ namespace Discord.Collections | |||
| result = _dictionary.GetOrAdd(key, newItem); | |||
| if (result == newItem) | |||
| { | |||
| OnCreated(newItem); | |||
| _onCache(result); | |||
| RaiseItemCreated(result); | |||
| } | |||
| } | |||
| @@ -100,7 +103,7 @@ namespace Discord.Collections | |||
| TValue result; | |||
| if (_dictionary.TryRemove(key, out result)) | |||
| { | |||
| OnRemoved(result); //TODO: If this object is accessed before OnRemoved finished firing, properties such as Server.Channels will have null elements | |||
| _onUncache(result); //TODO: If this object is accessed before OnRemoved finished firing, properties such as Server.Channels will have null elements | |||
| return result; | |||
| } | |||
| } | |||
| @@ -130,16 +133,7 @@ namespace Discord.Collections | |||
| } | |||
| } | |||
| protected virtual void OnCreated(TValue item) { } | |||
| protected virtual void OnRemoved(TValue item) { } | |||
| public IEnumerator<TValue> GetEnumerator() | |||
| { | |||
| return _dictionary.Select(x => x.Value).GetEnumerator(); | |||
| } | |||
| IEnumerator IEnumerable.GetEnumerator() | |||
| { | |||
| return GetEnumerator(); | |||
| } | |||
| public IEnumerator<TValue> GetEnumerator() => _dictionary.Select(x => x.Value).GetEnumerator(); | |||
| IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); | |||
| } | |||
| } | |||
| @@ -1,5 +1,6 @@ | |||
| using Discord.API; | |||
| using Newtonsoft.Json; | |||
| using System; | |||
| using System.Collections.Concurrent; | |||
| using System.Collections.Generic; | |||
| using System.Linq; | |||
| @@ -29,6 +30,7 @@ namespace Discord | |||
| private readonly DiscordClient _client; | |||
| private readonly ConcurrentDictionary<string, bool> _messages; | |||
| private bool _areMembersStale; | |||
| private bool _hasRef; | |||
| /// <summary> Returns the unique identifier for this channel. </summary> | |||
| public string Id { get; } | |||
| @@ -101,6 +103,39 @@ namespace Discord | |||
| _permissionOverwrites = _initialPermissionsOverwrites; | |||
| _areMembersStale = true; | |||
| } | |||
| internal void OnCached() | |||
| { | |||
| var server = Server; | |||
| if (server != null) | |||
| server.AddChannel(Id); | |||
| if (RecipientId != null) | |||
| { | |||
| var user = Recipient; | |||
| if (user != null) | |||
| { | |||
| user.PrivateChannelId = Id; | |||
| user.AddRef(); | |||
| _hasRef = true; | |||
| } | |||
| } | |||
| } | |||
| internal void OnUncached() | |||
| { | |||
| var server = Server; | |||
| if (server != null) | |||
| server.RemoveChannel(Id); | |||
| if (RecipientId != null) | |||
| { | |||
| var user = Recipient; | |||
| if (user != null) | |||
| { | |||
| user.PrivateChannelId = null; | |||
| if (_hasRef) | |||
| user.RemoveRef(); | |||
| } | |||
| } | |||
| _hasRef = false; | |||
| } | |||
| internal void Update(ChannelReference model) | |||
| { | |||
| @@ -11,6 +11,7 @@ namespace Discord | |||
| { | |||
| private readonly DiscordClient _client; | |||
| private ConcurrentDictionary<string, PackedChannelPermissions> _permissions; | |||
| private bool _hasRef; | |||
| /// <summary> Returns the name of this user on this server. </summary> | |||
| public string Name { get; private set; } | |||
| @@ -77,6 +78,41 @@ namespace Discord | |||
| RoleIds = _initialRoleIds; | |||
| _permissions = new ConcurrentDictionary<string, PackedChannelPermissions>(); | |||
| } | |||
| internal void OnCached() | |||
| { | |||
| var server = Server; | |||
| if (server != null) | |||
| { | |||
| server.AddMember(this); | |||
| if (UserId == _client.CurrentUserId) | |||
| server.CurrentMember = this; | |||
| } | |||
| var user = User; | |||
| if (user != null) | |||
| { | |||
| user.AddServer(ServerId); | |||
| user.AddRef(); | |||
| _hasRef = true; | |||
| } | |||
| } | |||
| internal void OnUncached() | |||
| { | |||
| var server = Server; | |||
| if (server != null) | |||
| { | |||
| server.RemoveMember(this); | |||
| if (UserId == _client.CurrentUserId) | |||
| server.CurrentMember = null; | |||
| } | |||
| var user = User; | |||
| if (user != null) | |||
| { | |||
| user.RemoveServer(ServerId); | |||
| if (_hasRef) | |||
| user.RemoveRef(); | |||
| } | |||
| _hasRef = false; | |||
| } | |||
| public override string ToString() => UserId; | |||
| @@ -93,6 +93,7 @@ namespace Discord | |||
| private readonly DiscordClient _client; | |||
| private string _cleanText; | |||
| private bool _gotRef; | |||
| /// <summary> Returns the global unique identifier for this message. </summary> | |||
| public string Id { get; internal set; } | |||
| @@ -154,16 +155,7 @@ namespace Discord | |||
| public User User => _client.Users[UserId]; | |||
| /// <summary> Returns the author of this message. </summary> | |||
| [JsonIgnore] | |||
| public Member Member | |||
| { | |||
| get | |||
| { | |||
| if (!Channel.IsPrivate) | |||
| return _client.Members[UserId, ServerId]; | |||
| else | |||
| throw new InvalidOperationException("Unable to access Member in a private channel. Use User instead or check for Channel.IsPrivate."); | |||
| } | |||
| } | |||
| public Member Member => _client.Members[UserId, ServerId]; | |||
| internal Message(DiscordClient client, string id, string channelId, string userId) | |||
| { | |||
| @@ -174,6 +166,28 @@ namespace Discord | |||
| Attachments = _initialAttachments; | |||
| Embeds = _initialEmbeds; | |||
| MentionIds = _initialMentions; | |||
| } | |||
| internal void OnCached() | |||
| { | |||
| var channel = Channel; | |||
| if (channel != null) | |||
| channel.AddMessage(Id); | |||
| var user = User; | |||
| if (user != null) | |||
| { | |||
| user.AddRef(); | |||
| _gotRef = true; | |||
| } | |||
| } | |||
| internal void OnUncached() | |||
| { | |||
| var channel = Channel; | |||
| if (channel != null) | |||
| channel.RemoveMessage(Id); | |||
| var user = User; | |||
| if (user != null && _gotRef) | |||
| user.RemoveRef(); | |||
| _gotRef = false; | |||
| } | |||
| internal void Update(MessageInfo model) | |||
| @@ -52,7 +52,19 @@ namespace Discord | |||
| if (IsEveryone) | |||
| Position = int.MinValue; | |||
| } | |||
| } | |||
| internal void OnCached() | |||
| { | |||
| var server = Server; | |||
| if (server != null) | |||
| server.AddRole(Id); | |||
| } | |||
| internal void OnUncached() | |||
| { | |||
| var server = Server; | |||
| if (server != null) | |||
| server.RemoveRole(Id); | |||
| } | |||
| internal void Update(RoleInfo model) | |||
| { | |||
| @@ -104,6 +104,23 @@ namespace Discord | |||
| _members = new ConcurrentDictionary<string, bool>(); | |||
| _roles = new ConcurrentDictionary<string, bool>(); | |||
| } | |||
| internal void OnCached() | |||
| { | |||
| } | |||
| internal void OnUncached() | |||
| { | |||
| var channels = _client.Channels; | |||
| foreach (var channelId in ChannelIds) | |||
| channels.TryRemove(channelId); | |||
| var members = _client.Members; | |||
| foreach (var userId in UserIds) | |||
| members.TryRemove(userId, Id); | |||
| var roles = _client.Roles; | |||
| foreach (var roleId in RoleIds) | |||
| roles.TryRemove(roleId); | |||
| } | |||
| internal void Update(GuildInfo model) | |||
| { | |||
| @@ -80,7 +80,13 @@ namespace Discord | |||
| _client = client; | |||
| Id = id; | |||
| _servers = new ConcurrentDictionary<string, bool>(); | |||
| } | |||
| } | |||
| internal void OnCached() | |||
| { | |||
| } | |||
| internal void OnUncached() | |||
| { | |||
| } | |||
| internal void Update(UserReference model) | |||
| { | |||