From af7ee37e44ce87f1a77796a0c9561df9c404b8c9 Mon Sep 17 00:00:00 2001 From: RogueException Date: Mon, 18 Jul 2016 13:37:57 -0300 Subject: [PATCH] Added IGroupUser, GroupChannel.Add/Remove Recipient, Add/Remove Recipient events --- src/Discord.Net/API/DiscordAPIClient.cs | 17 +++ src/Discord.Net/API/Gateway/RecipientEvent.cs | 12 ++ src/Discord.Net/DiscordClient.cs | 8 +- src/Discord.Net/DiscordSocketClient.Events.cs | 12 ++ src/Discord.Net/DiscordSocketClient.cs | 117 ++++++++++++++---- .../Entities/Channels/GroupChannel.cs | 19 +-- .../Entities/Channels/IGroupChannel.cs | 3 + src/Discord.Net/Entities/Users/GroupUser.cs | 47 +++++++ src/Discord.Net/Entities/Users/GuildUser.cs | 1 - src/Discord.Net/Entities/Users/IGroupUser.cs | 13 ++ .../Entities/WebSocket/CachedDMChannel.cs | 6 +- .../{CachedPrivateUser.cs => CachedDMUser.cs} | 6 +- .../Entities/WebSocket/CachedGroupChannel.cs | 52 +++++--- .../Entities/WebSocket/CachedGroupUser.cs | 33 +++++ .../Entities/WebSocket/CachedGuild.cs | 1 - .../WebSocket/ICachedPrivateChannel.cs | 2 +- 16 files changed, 282 insertions(+), 67 deletions(-) create mode 100644 src/Discord.Net/API/Gateway/RecipientEvent.cs create mode 100644 src/Discord.Net/Entities/Users/GroupUser.cs create mode 100644 src/Discord.Net/Entities/Users/IGroupUser.cs rename src/Discord.Net/Entities/WebSocket/{CachedPrivateUser.cs => CachedDMUser.cs} (87%) create mode 100644 src/Discord.Net/Entities/WebSocket/CachedGroupUser.cs diff --git a/src/Discord.Net/API/DiscordAPIClient.cs b/src/Discord.Net/API/DiscordAPIClient.cs index c5dc1c83b..5f5486780 100644 --- a/src/Discord.Net/API/DiscordAPIClient.cs +++ b/src/Discord.Net/API/DiscordAPIClient.cs @@ -551,6 +551,23 @@ namespace Discord.API await SendAsync("DELETE", $"channels/{channelId}/pins/{messageId}", options: options).ConfigureAwait(false); } + //Channel Recipients + public async Task AddGroupRecipientAsync(ulong channelId, ulong userId, RequestOptions options = null) + { + Preconditions.GreaterThan(channelId, 0, nameof(channelId)); + Preconditions.GreaterThan(userId, 0, nameof(userId)); + + await SendAsync("PUT", $"channels/{channelId}/recipients/{userId}", options: options).ConfigureAwait(false); + + } + public async Task RemoveGroupRecipientAsync(ulong channelId, ulong userId, RequestOptions options = null) + { + Preconditions.NotEqual(channelId, 0, nameof(channelId)); + Preconditions.NotEqual(userId, 0, nameof(userId)); + + await SendAsync("DELETE", $"channels/{channelId}/recipients/{userId}", options: options).ConfigureAwait(false); + } + //Guilds public async Task GetGuildAsync(ulong guildId, RequestOptions options = null) { diff --git a/src/Discord.Net/API/Gateway/RecipientEvent.cs b/src/Discord.Net/API/Gateway/RecipientEvent.cs new file mode 100644 index 000000000..6cac8f27f --- /dev/null +++ b/src/Discord.Net/API/Gateway/RecipientEvent.cs @@ -0,0 +1,12 @@ +using Newtonsoft.Json; + +namespace Discord.API.Gateway +{ + public class RecipientEvent + { + [JsonProperty("user")] + public User User { get; set; } + [JsonProperty("channel_id")] + public ulong ChannelId { get; set; } + } +} diff --git a/src/Discord.Net/DiscordClient.cs b/src/Discord.Net/DiscordClient.cs index 8bb10063a..48bc52f15 100644 --- a/src/Discord.Net/DiscordClient.cs +++ b/src/Discord.Net/DiscordClient.cs @@ -170,11 +170,9 @@ namespace Discord return new DMChannel(this, new User(model.Recipients.Value[0]), model); else if (model.Type == ChannelType.Group) { - var recipients = model.Recipients.Value; - var users = new ConcurrentDictionary(1, recipients.Length); - for (int i = 0; i < recipients.Length; i++) - users[recipients[i].Id] = new User(recipients[i]); - return new GroupChannel(this, users, model); + var channel = new GroupChannel(this, model); + channel.UpdateUsers(model.Recipients.Value, UpdateSource.Creation); + return channel; } else throw new InvalidOperationException($"Unexpected channel type: {model.Type}"); diff --git a/src/Discord.Net/DiscordSocketClient.Events.cs b/src/Discord.Net/DiscordSocketClient.Events.cs index 698a940e4..7182a7070 100644 --- a/src/Discord.Net/DiscordSocketClient.Events.cs +++ b/src/Discord.Net/DiscordSocketClient.Events.cs @@ -185,6 +185,18 @@ namespace Discord remove { _userIsTypingEvent.Remove(value); } } private readonly AsyncEvent> _userIsTypingEvent = new AsyncEvent>(); + public event Func RecipientAdded + { + add { _recipientAddedEvent.Add(value); } + remove { _recipientAddedEvent.Remove(value); } + } + private readonly AsyncEvent> _recipientAddedEvent = new AsyncEvent>(); + public event Func RecipientRemoved + { + add { _recipientRemovedEvent.Add(value); } + remove { _recipientRemovedEvent.Remove(value); } + } + private readonly AsyncEvent> _recipientRemovedEvent = new AsyncEvent>(); //TODO: Add PresenceUpdated? VoiceStateUpdated?, VoiceConnected, VoiceDisconnected; } diff --git a/src/Discord.Net/DiscordSocketClient.cs b/src/Discord.Net/DiscordSocketClient.cs index 7375d1088..4b2d42882 100644 --- a/src/Discord.Net/DiscordSocketClient.cs +++ b/src/Discord.Net/DiscordSocketClient.cs @@ -147,6 +147,7 @@ namespace Discord ConnectionState = ConnectionState.Connecting; await _gatewayLogger.InfoAsync("Connecting").ConfigureAwait(false); + try { _connectTask = new TaskCompletionSource(); @@ -160,7 +161,6 @@ namespace Discord await ApiClient.SendIdentifyAsync().ConfigureAwait(false); await _connectTask.Task.ConfigureAwait(false); - ConnectionState = ConnectionState.Connected; await _gatewayLogger.InfoAsync("Connected").ConfigureAwait(false); } @@ -173,6 +173,7 @@ namespace Discord /// public async Task DisconnectAsync() { + if (_connectTask?.TrySetCanceled() ?? false) return; await _connectionLock.WaitAsync().ConfigureAwait(false); try { @@ -181,6 +182,17 @@ namespace Discord } finally { _connectionLock.Release(); } } + private async Task DisconnectAsync(Exception ex) + { + if (_connectTask?.TrySetException(ex) ?? false) return; + await _connectionLock.WaitAsync().ConfigureAwait(false); + try + { + _isReconnecting = false; + await DisconnectInternalAsync(ex).ConfigureAwait(false); + } + finally { _connectionLock.Release(); } + } private async Task DisconnectInternalAsync(Exception ex) { ulong guildId; @@ -340,17 +352,14 @@ namespace Discord { var recipients = model.Recipients.Value; var user = GetOrAddUser(recipients[0], dataStore); - var channel = new CachedDMChannel(this, new CachedPrivateUser(user), model); + var channel = new CachedDMChannel(this, new CachedDMUser(user), model); dataStore.AddChannel(channel); return channel; } case ChannelType.Group: { - var recipients = model.Recipients.Value; - var users = new ConcurrentDictionary(1, recipients.Length); - for (int i = 0; i < recipients.Length; i++) - users[recipients[i].Id] = new CachedPrivateUser(GetOrAddUser(recipients[i], dataStore)); - var channel = new CachedGroupChannel(this, users, model); + var channel = new CachedGroupChannel(this, model); + channel.UpdateUsers(model.Recipients.Value, UpdateSource.Creation); dataStore.AddChannel(channel); return channel; } @@ -521,34 +530,43 @@ namespace Discord //Connection case "READY": { - await _gatewayLogger.DebugAsync("Received Dispatch (READY)").ConfigureAwait(false); - - var data = (payload as JToken).ToObject(_serializer); - var dataStore = new DataStore( data.Guilds.Length, data.PrivateChannels.Length); + try + { + await _gatewayLogger.DebugAsync("Received Dispatch (READY)").ConfigureAwait(false); + + var data = (payload as JToken).ToObject(_serializer); + var dataStore = new DataStore(data.Guilds.Length, data.PrivateChannels.Length); - var currentUser = new CachedSelfUser(this, data.User); - int unavailableGuilds = 0; - for (int i = 0; i < data.Guilds.Length; i++) + var currentUser = new CachedSelfUser(this, data.User); + int unavailableGuilds = 0; + for (int i = 0; i < data.Guilds.Length; i++) + { + var model = data.Guilds[i]; + AddGuild(model, dataStore); + if (model.Unavailable == true) + unavailableGuilds++; + } + for (int i = 0; i < data.PrivateChannels.Length; i++) + AddPrivateChannel(data.PrivateChannels[i], dataStore); + + _sessionId = data.SessionId; + _currentUser = currentUser; + _unavailableGuilds = unavailableGuilds; + _lastGuildAvailableTime = Environment.TickCount; + DataStore = dataStore; + + } + catch (Exception ex) { - var model = data.Guilds[i]; - AddGuild(model, dataStore); - if (model.Unavailable == true) - unavailableGuilds++; + await DisconnectAsync(new Exception("Processing READY failed", ex)); + return; } - for (int i = 0; i < data.PrivateChannels.Length; i++) - AddPrivateChannel(data.PrivateChannels[i], dataStore); - - _sessionId = data.SessionId; - _currentUser = currentUser; - _unavailableGuilds = unavailableGuilds; - _lastGuildAvailableTime = Environment.TickCount; - DataStore = dataStore; _guildDownloadTask = WaitForGuildsAsync(_cancelToken.Token, _clientLogger); await _readyEvent.InvokeAsync().ConfigureAwait(false); await SyncGuildsAsync().ConfigureAwait(false); - + var _ = _connectTask.TrySetResultAsync(true); //Signal the .Connect() call to complete await _gatewayLogger.InfoAsync("Ready").ConfigureAwait(false); } @@ -913,6 +931,51 @@ namespace Discord } } break; + case "CHANNEL_RECIPIENT_ADD": + { + await _gatewayLogger.DebugAsync("Received Dispatch (CHANNEL_RECIPIENT_ADD)").ConfigureAwait(false); + + var data = (payload as JToken).ToObject(_serializer); + var channel = DataStore.GetChannel(data.ChannelId) as CachedGroupChannel; + if (channel != null) + { + var user = channel.AddUser(data.User, DataStore); + await _recipientAddedEvent.InvokeAsync(user).ConfigureAwait(false); + } + else + { + await _gatewayLogger.WarningAsync("CHANNEL_RECIPIENT_ADD referenced an unknown channel.").ConfigureAwait(false); + return; + } + } + break; + case "CHANNEL_RECIPIENT_REMOVE": + { + await _gatewayLogger.DebugAsync("Received Dispatch (CHANNEL_RECIPIENT_REMOVE)").ConfigureAwait(false); + + var data = (payload as JToken).ToObject(_serializer); + var channel = DataStore.GetChannel(data.ChannelId) as CachedGroupChannel; + if (channel != null) + { + var user = channel.RemoveUser(data.User.Id); + if (user != null) + { + user.User.RemoveRef(this); + await _recipientRemovedEvent.InvokeAsync(user).ConfigureAwait(false); + } + else + { + await _gatewayLogger.WarningAsync("CHANNEL_RECIPIENT_REMOVE referenced an unknown user.").ConfigureAwait(false); + return; + } + } + else + { + await _gatewayLogger.WarningAsync("CHANNEL_RECIPIENT_ADD referenced an unknown channel.").ConfigureAwait(false); + return; + } + } + break; //Roles case "GUILD_ROLE_CREATE": diff --git a/src/Discord.Net/Entities/Channels/GroupChannel.cs b/src/Discord.Net/Entities/Channels/GroupChannel.cs index 0fda28f9d..0e9347166 100644 --- a/src/Discord.Net/Entities/Channels/GroupChannel.cs +++ b/src/Discord.Net/Entities/Channels/GroupChannel.cs @@ -15,7 +15,7 @@ namespace Discord [DebuggerDisplay(@"{DebuggerDisplay,nq}")] internal class GroupChannel : SnowflakeEntity, IGroupChannel { - protected ConcurrentDictionary _users; + protected ConcurrentDictionary _users; private string _iconId; public override DiscordClient Discord { get; } @@ -25,11 +25,10 @@ namespace Discord public virtual IReadOnlyCollection CachedMessages => ImmutableArray.Create(); public string IconUrl => API.CDN.GetChannelIconUrl(Id, _iconId); - public GroupChannel(DiscordClient discord, ConcurrentDictionary recipients, Model model) + public GroupChannel(DiscordClient discord, Model model) : base(model.Id) { Discord = discord; - _users = recipients; Update(model, UpdateSource.Creation); } @@ -46,13 +45,13 @@ namespace Discord UpdateUsers(model.Recipients.Value, source); } - protected virtual void UpdateUsers(API.User[] models, UpdateSource source) + internal virtual void UpdateUsers(API.User[] models, UpdateSource source) { if (!IsAttached) { - var users = new ConcurrentDictionary(1, (int)(models.Length * 1.05)); + var users = new ConcurrentDictionary(1, (int)(models.Length * 1.05)); for (int i = 0; i < models.Length; i++) - users[models[i].Id] = new User(models[i]); + users[models[i].Id] = new GroupUser(this, new User(models[i])); _users = users; } } @@ -69,9 +68,13 @@ namespace Discord await Discord.ApiClient.DeleteChannelAsync(Id).ConfigureAwait(false); } + public async Task AddUserAsync(IUser user) + { + await Discord.ApiClient.AddGroupRecipientAsync(Id, user.Id).ConfigureAwait(false); + } public async Task GetUserAsync(ulong id) { - IUser user; + GroupUser user; if (_users.TryGetValue(id, out user)) return user; var currentUser = await Discord.GetCurrentUserAsync().ConfigureAwait(false); @@ -82,7 +85,7 @@ namespace Discord public async Task> GetUsersAsync() { var currentUser = await Discord.GetCurrentUserAsync().ConfigureAwait(false); - return _users.Select(x => x.Value).Concat(ImmutableArray.Create(currentUser)).ToReadOnlyCollection(_users); + return _users.Select(x => x.Value).Concat(ImmutableArray.Create(currentUser)).ToReadOnlyCollection(_users); } public async Task SendMessageAsync(string text, bool isTTS) diff --git a/src/Discord.Net/Entities/Channels/IGroupChannel.cs b/src/Discord.Net/Entities/Channels/IGroupChannel.cs index 209838568..6b71f76b6 100644 --- a/src/Discord.Net/Entities/Channels/IGroupChannel.cs +++ b/src/Discord.Net/Entities/Channels/IGroupChannel.cs @@ -4,6 +4,9 @@ namespace Discord { public interface IGroupChannel : IMessageChannel, IPrivateChannel { + /// Adds a user to this group. + Task AddUserAsync(IUser user); + /// Leaves this group. Task LeaveAsync(); } diff --git a/src/Discord.Net/Entities/Users/GroupUser.cs b/src/Discord.Net/Entities/Users/GroupUser.cs new file mode 100644 index 000000000..af32e4395 --- /dev/null +++ b/src/Discord.Net/Entities/Users/GroupUser.cs @@ -0,0 +1,47 @@ +using Discord.API.Rest; +using System; +using System.Threading.Tasks; + +namespace Discord +{ + internal class GroupUser : IGroupUser + { + public GroupChannel Channel { get; private set; } + public User User { get; private set; } + + public ulong Id => User.Id; + public string AvatarUrl => User.AvatarUrl; + public DateTimeOffset CreatedAt => User.CreatedAt; + public string Discriminator => User.Discriminator; + public ushort DiscriminatorValue => User.DiscriminatorValue; + public bool IsAttached => User.IsAttached; + public bool IsBot => User.IsBot; + public string Mention => User.Mention; + public string NicknameMention => User.NicknameMention; + public string Username => User.Username; + + public virtual UserStatus Status => UserStatus.Unknown; + public virtual Game Game => null; + + public DiscordClient Discord => Channel.Discord; + + public GroupUser(GroupChannel channel, User user) + { + Channel = channel; + User = user; + } + + public async Task KickAsync() + { + await Discord.ApiClient.RemoveGroupRecipientAsync(Channel.Id, Id).ConfigureAwait(false); + } + + public async Task CreateDMChannelAsync() + { + var args = new CreateDMChannelParams { Recipient = this }; + var model = await Discord.ApiClient.CreateDMChannelAsync(args).ConfigureAwait(false); + + return new DMChannel(Discord, new User(model.Recipients.Value[0]), model); + } + } +} diff --git a/src/Discord.Net/Entities/Users/GuildUser.cs b/src/Discord.Net/Entities/Users/GuildUser.cs index eb7989de1..60b1b6e38 100644 --- a/src/Discord.Net/Entities/Users/GuildUser.cs +++ b/src/Discord.Net/Entities/Users/GuildUser.cs @@ -7,7 +7,6 @@ using System.Linq; using System.Threading.Tasks; using Model = Discord.API.GuildMember; using PresenceModel = Discord.API.Presence; -using VoiceStateModel = Discord.API.VoiceState; namespace Discord { diff --git a/src/Discord.Net/Entities/Users/IGroupUser.cs b/src/Discord.Net/Entities/Users/IGroupUser.cs new file mode 100644 index 000000000..8ed53616c --- /dev/null +++ b/src/Discord.Net/Entities/Users/IGroupUser.cs @@ -0,0 +1,13 @@ +using System.Threading.Tasks; + +namespace Discord +{ + public interface IGroupUser : IUser + { + /// Kicks this user from this group. + Task KickAsync(); + + /// Returns a private message channel to this user, creating one if it does not already exist. + Task CreateDMChannelAsync(); + } +} diff --git a/src/Discord.Net/Entities/WebSocket/CachedDMChannel.cs b/src/Discord.Net/Entities/WebSocket/CachedDMChannel.cs index c3014d074..c22aa645b 100644 --- a/src/Discord.Net/Entities/WebSocket/CachedDMChannel.cs +++ b/src/Discord.Net/Entities/WebSocket/CachedDMChannel.cs @@ -11,11 +11,11 @@ namespace Discord private readonly MessageManager _messages; public new DiscordSocketClient Discord => base.Discord as DiscordSocketClient; - public new CachedPrivateUser Recipient => base.Recipient as CachedPrivateUser; + public new CachedDMUser Recipient => base.Recipient as CachedDMUser; public IReadOnlyCollection Members => ImmutableArray.Create(Discord.CurrentUser, Recipient); - IReadOnlyCollection ICachedPrivateChannel.Recipients => ImmutableArray.Create(Recipient); + IReadOnlyCollection ICachedPrivateChannel.Recipients => ImmutableArray.Create(Recipient); - public CachedDMChannel(DiscordSocketClient discord, CachedPrivateUser recipient, Model model) + public CachedDMChannel(DiscordSocketClient discord, CachedDMUser recipient, Model model) : base(discord, recipient, model) { if (Discord.MessageCacheSize > 0) diff --git a/src/Discord.Net/Entities/WebSocket/CachedPrivateUser.cs b/src/Discord.Net/Entities/WebSocket/CachedDMUser.cs similarity index 87% rename from src/Discord.Net/Entities/WebSocket/CachedPrivateUser.cs rename to src/Discord.Net/Entities/WebSocket/CachedDMUser.cs index b920c22ce..c25b8ac7a 100644 --- a/src/Discord.Net/Entities/WebSocket/CachedPrivateUser.cs +++ b/src/Discord.Net/Entities/WebSocket/CachedDMUser.cs @@ -5,7 +5,7 @@ using PresenceModel = Discord.API.Presence; namespace Discord { [DebuggerDisplay("{DebuggerDisplay,nq}")] - internal class CachedPrivateUser : ICachedUser + internal class CachedDMUser : ICachedUser { public CachedGlobalUser User { get; } @@ -26,7 +26,7 @@ namespace Discord public string NicknameMention => User.NicknameMention; public string Username => User.Username; - public CachedPrivateUser(CachedGlobalUser user) + public CachedDMUser(CachedGlobalUser user) { User = user; } @@ -36,7 +36,7 @@ namespace Discord User.Update(model, source); } - public CachedPrivateUser Clone() => MemberwiseClone() as CachedPrivateUser; + public CachedDMUser Clone() => MemberwiseClone() as CachedDMUser; ICachedUser ICachedUser.Clone() => Clone(); public override string ToString() => $"{Username}#{Discriminator}"; diff --git a/src/Discord.Net/Entities/WebSocket/CachedGroupChannel.cs b/src/Discord.Net/Entities/WebSocket/CachedGroupChannel.cs index 5320727fa..b8f7606a1 100644 --- a/src/Discord.Net/Entities/WebSocket/CachedGroupChannel.cs +++ b/src/Discord.Net/Entities/WebSocket/CachedGroupChannel.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Threading.Tasks; using MessageModel = Discord.API.Message; using Model = Discord.API.Channel; +using UserModel = Discord.API.User; using VoiceStateModel = Discord.API.VoiceState; namespace Discord @@ -17,11 +18,11 @@ namespace Discord public new DiscordSocketClient Discord => base.Discord as DiscordSocketClient; public IReadOnlyCollection Members - => _users.Select(x => x.Value).Concat(ImmutableArray.Create(Discord.CurrentUser)).Cast().ToReadOnlyCollection(() => _users.Count + 1); - public new IReadOnlyCollection Recipients => _users.Cast().ToReadOnlyCollection(_users); + => _users.Select(x => x.Value as ICachedUser).Concat(ImmutableArray.Create(Discord.CurrentUser)).ToReadOnlyCollection(() => _users.Count + 1); + public new IReadOnlyCollection Recipients => _users.Cast().ToReadOnlyCollection(_users); - public CachedGroupChannel(DiscordSocketClient discord, ConcurrentDictionary recipients, Model model) - : base(discord, recipients, model) + public CachedGroupChannel(DiscordSocketClient discord, Model model) + : base(discord, model) { if (Discord.MessageCacheSize > 0) _messages = new MessageCache(Discord, this); @@ -36,23 +37,46 @@ namespace Discord base.Update(model, source); } - protected override void UpdateUsers(API.User[] models, UpdateSource source) + internal override void UpdateUsers(UserModel[] models, UpdateSource source) { - var users = new ConcurrentDictionary(1, models.Length); + var users = new ConcurrentDictionary(1, models.Length); for (int i = 0; i < models.Length; i++) - users[models[i].Id] = new CachedPrivateUser(Discord.GetOrAddUser(models[i], Discord.DataStore)); + { + var globalUser = Discord.GetOrAddUser(models[i], Discord.DataStore); + users[models[i].Id] = new CachedGroupUser(this, globalUser); + } _users = users; } + public CachedGroupUser AddUser(UserModel model, DataStore dataStore) + { + GroupUser user; + if (_users.TryGetValue(model.Id, out user)) + return user as CachedGroupUser; + else + { + var globalUser = Discord.GetOrAddUser(model, dataStore); + var privateUser = new CachedGroupUser(this, globalUser); + _users[privateUser.Id] = privateUser; + return privateUser; + } + } public ICachedUser GetUser(ulong id) { - IUser user; + GroupUser user; if (_users.TryGetValue(id, out user)) - return user as ICachedUser; + return user as CachedGroupUser; if (id == Discord.CurrentUser.Id) return Discord.CurrentUser; return null; } + public CachedGroupUser RemoveUser(ulong id) + { + GroupUser user; + if (_users.TryRemove(id, out user)) + return user as CachedGroupUser; + return null; + } public VoiceState AddOrUpdateVoiceState(VoiceStateModel model, DataStore dataStore, ConcurrentDictionary voiceStates = null) { @@ -106,15 +130,7 @@ namespace Discord public CachedDMChannel Clone() => MemberwiseClone() as CachedDMChannel; IMessage IMessageChannel.GetCachedMessage(ulong id) => GetMessage(id); - ICachedUser ICachedMessageChannel.GetUser(ulong id, bool skipCheck) - { - IUser user; - if (_users.TryGetValue(id, out user)) - return user as ICachedUser; - if (id == Discord.CurrentUser.Id) - return Discord.CurrentUser; - return null; - } + ICachedUser ICachedMessageChannel.GetUser(ulong id, bool skipCheck) => GetUser(id); ICachedChannel ICachedChannel.Clone() => Clone(); } } diff --git a/src/Discord.Net/Entities/WebSocket/CachedGroupUser.cs b/src/Discord.Net/Entities/WebSocket/CachedGroupUser.cs new file mode 100644 index 000000000..8735bbd11 --- /dev/null +++ b/src/Discord.Net/Entities/WebSocket/CachedGroupUser.cs @@ -0,0 +1,33 @@ +using System.Diagnostics; + +namespace Discord +{ + [DebuggerDisplay("{DebuggerDisplay,nq}")] + internal class CachedGroupUser : GroupUser, ICachedUser + { + public new DiscordSocketClient Discord => base.Discord as DiscordSocketClient; + public new CachedGroupChannel Channel => base.Channel as CachedGroupChannel; + public new CachedGlobalUser User => base.User as CachedGlobalUser; + public Presence Presence => User.Presence; //{ get; private set; } + + public override Game Game => Presence.Game; + public override UserStatus Status => Presence.Status; + + public VoiceState? VoiceState => Channel.GetVoiceState(Id); + public bool IsSelfDeafened => VoiceState?.IsSelfDeafened ?? false; + public bool IsSelfMuted => VoiceState?.IsSelfMuted ?? false; + public bool IsSuppressed => VoiceState?.IsSuppressed ?? false; + public CachedVoiceChannel VoiceChannel => VoiceState?.VoiceChannel; + + public CachedGroupUser(CachedGroupChannel channel, CachedGlobalUser user) + : base(channel, user) + { + } + + public CachedGroupUser Clone() => MemberwiseClone() as CachedGroupUser; + ICachedUser ICachedUser.Clone() => Clone(); + + public override string ToString() => $"{Username}#{Discriminator}"; + private string DebuggerDisplay => $"{Username}#{Discriminator} ({Id})"; + } +} diff --git a/src/Discord.Net/Entities/WebSocket/CachedGuild.cs b/src/Discord.Net/Entities/WebSocket/CachedGuild.cs index 26bca90e3..da90f9a6f 100644 --- a/src/Discord.Net/Entities/WebSocket/CachedGuild.cs +++ b/src/Discord.Net/Entities/WebSocket/CachedGuild.cs @@ -208,7 +208,6 @@ namespace Discord var user = Discord.GetOrAddUser(model.User, dataStore); member = new CachedGuildUser(this, user, model); members[user.Id] = member; - user.AddRef(); DownloadedMemberCount++; } return member; diff --git a/src/Discord.Net/Entities/WebSocket/ICachedPrivateChannel.cs b/src/Discord.Net/Entities/WebSocket/ICachedPrivateChannel.cs index 79fba737a..3b9081521 100644 --- a/src/Discord.Net/Entities/WebSocket/ICachedPrivateChannel.cs +++ b/src/Discord.Net/Entities/WebSocket/ICachedPrivateChannel.cs @@ -4,6 +4,6 @@ namespace Discord { internal interface ICachedPrivateChannel : ICachedChannel, IPrivateChannel { - new IReadOnlyCollection Recipients { get; } + new IReadOnlyCollection Recipients { get; } } }