diff --git a/src/Discord.Net.Core/Entities/Users/RelationshipType.cs b/src/Discord.Net.Core/Entities/Users/RelationshipType.cs index 71dd3eb3c..dc5129aa1 100644 --- a/src/Discord.Net.Core/Entities/Users/RelationshipType.cs +++ b/src/Discord.Net.Core/Entities/Users/RelationshipType.cs @@ -1,14 +1,11 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Discord +namespace Discord { public enum RelationshipType { - Friend = 1, - Blocked = 2, - IncomingPending = 3, - OutgoingPending = 4 + None, + Friend, + Blocked, + IncomingPending, + OutgoingPending } } diff --git a/src/Discord.Net.Rest/API/Common/RelationshipType.cs b/src/Discord.Net.Rest/API/Common/RelationshipType.cs deleted file mode 100644 index 0ed99f396..000000000 --- a/src/Discord.Net.Rest/API/Common/RelationshipType.cs +++ /dev/null @@ -1,11 +0,0 @@ -#pragma warning disable CS1591 -namespace Discord.API -{ - internal enum RelationshipType - { - Friend = 1, - Blocked = 2, - IncomingPending = 3, - OutgoingPending = 4 - } -} diff --git a/src/Discord.Net.Rest/BaseDiscordClient.cs b/src/Discord.Net.Rest/BaseDiscordClient.cs index f151bfab1..f38b9506d 100644 --- a/src/Discord.Net.Rest/BaseDiscordClient.cs +++ b/src/Discord.Net.Rest/BaseDiscordClient.cs @@ -121,13 +121,14 @@ namespace Discord.Rest } /// public void Dispose() => Dispose(true); - - public Task> GetRelationshipsAsync() => ApiClient.GetRelationshipsAsync(); - + //IDiscordClient ConnectionState IDiscordClient.ConnectionState => ConnectionState.Disconnected; ISelfUser IDiscordClient.CurrentUser => CurrentUser; - + + Task> IDiscordClient.GetRelationshipsAsync() + => Task.FromResult>(null); + Task IDiscordClient.GetApplicationInfoAsync() { throw new NotSupportedException(); } Task IDiscordClient.GetChannelAsync(ulong id, CacheMode mode) diff --git a/src/Discord.Net.Rest/DiscordRestApiClient.cs b/src/Discord.Net.Rest/DiscordRestApiClient.cs index 05dc8a6fc..b730745cb 100644 --- a/src/Discord.Net.Rest/DiscordRestApiClient.cs +++ b/src/Discord.Net.Rest/DiscordRestApiClient.cs @@ -1030,10 +1030,10 @@ namespace Discord.API } //Relationships - public async Task> GetRelationshipsAsync(RequestOptions options = null) + public async Task> GetRelationshipsAsync(RequestOptions options = null) { options = RequestOptions.CreateOrClone(options); - return await SendAsync>("GET", () => "users/@me/relationships", new BucketIds(), options: options).ConfigureAwait(false); + return await SendAsync>("GET", () => "users/@me/relationships", new BucketIds(), options: options).ConfigureAwait(false); } public async Task AddFriendAsync(ulong userId, RequestOptions options = null) { diff --git a/src/Discord.Net.WebSocket/ClientState.cs b/src/Discord.Net.WebSocket/ClientState.cs index a452113e2..392791f62 100644 --- a/src/Discord.Net.WebSocket/ClientState.cs +++ b/src/Discord.Net.WebSocket/ClientState.cs @@ -10,11 +10,13 @@ namespace Discord.WebSocket private const double AverageChannelsPerGuild = 10.22; //Source: Googie2149 private const double AverageUsersPerGuild = 47.78; //Source: Googie2149 private const double CollectionMultiplier = 1.05; //Add 5% buffer to handle growth + private const double AverageRelationshipsPerUser = 30; private readonly ConcurrentDictionary _channels; private readonly ConcurrentDictionary _dmChannels; private readonly ConcurrentDictionary _guilds; private readonly ConcurrentDictionary _users; + private readonly ConcurrentDictionary _relations; private readonly ConcurrentHashSet _groupChannels; internal IReadOnlyCollection Channels => _channels.ToReadOnlyCollection(); @@ -22,6 +24,7 @@ namespace Discord.WebSocket internal IReadOnlyCollection GroupChannels => _groupChannels.Select(x => GetChannel(x) as SocketGroupChannel).ToReadOnlyCollection(_groupChannels); internal IReadOnlyCollection Guilds => _guilds.ToReadOnlyCollection(); internal IReadOnlyCollection Users => _users.ToReadOnlyCollection(); + internal IReadOnlyCollection Relationships => _relations.ToReadOnlyCollection(); internal IReadOnlyCollection PrivateChannels => _dmChannels.Select(x => x.Value as ISocketPrivateChannel).Concat( @@ -36,6 +39,7 @@ namespace Discord.WebSocket _dmChannels = new ConcurrentDictionary(ConcurrentHashSet.DefaultConcurrencyLevel, (int)(dmChannelCount * CollectionMultiplier)); _guilds = new ConcurrentDictionary(ConcurrentHashSet.DefaultConcurrencyLevel, (int)(guildCount * CollectionMultiplier)); _users = new ConcurrentDictionary(ConcurrentHashSet.DefaultConcurrencyLevel, (int)(estimatedUsersCount * CollectionMultiplier)); + _relations = new ConcurrentDictionary(ConcurrentHashSet.DefaultConcurrencyLevel, (int)AverageRelationshipsPerUser); _groupChannels = new ConcurrentHashSet(ConcurrentHashSet.DefaultConcurrencyLevel, (int)(10 * CollectionMultiplier)); } @@ -126,5 +130,22 @@ namespace Discord.WebSocket return user; return null; } + + internal SocketRelationship GetRelationship(ulong id) + { + if (_relations.TryGetValue(id, out SocketRelationship value)) + return value; + return null; + } + internal void AddRelationship(SocketRelationship relation) + { + _relations[relation.User.Id] = relation; + } + internal SocketRelationship RemoveRelationship(ulong id) + { + if (_relations.TryRemove(id, out SocketRelationship value)) + return value; + return null; + } } } diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.Events.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.Events.cs index 313e661f3..d731e390a 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.Events.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.Events.cs @@ -221,5 +221,19 @@ namespace Discord.WebSocket remove { _recipientRemovedEvent.Remove(value); } } private readonly AsyncEvent> _recipientRemovedEvent = new AsyncEvent>(); + + // relationships + public event Func RelationshipAdd + { + add { _relationshipAddedEvent.Add(value); } + remove { _relationshipAddedEvent.Remove(value); } + } + private readonly AsyncEvent> _relationshipAddedEvent = new AsyncEvent>(); + public event Func RelationshipRemoved + { + add { _relationshipRemovedEvent.Add(value); } + remove { _relationshipRemovedEvent.Remove(value); } + } + private readonly AsyncEvent> _relationshipRemovedEvent = new AsyncEvent>(); } } diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs index 66c25e5f6..c3db1b379 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs @@ -70,6 +70,7 @@ namespace Discord.WebSocket public IReadOnlyCollection GroupChannels => State.PrivateChannels.Select(x => x as SocketGroupChannel).Where(x => x != null).ToImmutableArray(); public IReadOnlyCollection VoiceRegions => _voiceRegions.ToReadOnlyCollection(); + public IReadOnlyCollection Relationships => State.Relationships; /// Creates a new REST/WebSocket discord client. public DiscordSocketClient() : this(new DiscordSocketConfig()) { } @@ -270,7 +271,7 @@ namespace Discord.WebSocket /// public Task GetInviteAsync(string inviteId) => ClientHelper.GetInviteAsync(this, inviteId); - + /// public SocketUser GetUser(ulong id) { @@ -484,6 +485,8 @@ namespace Discord.WebSocket } for (int i = 0; i < data.PrivateChannels.Length; i++) AddPrivateChannel(data.PrivateChannels[i], state); + for (int i = 0; i < data.Relationships.Length; i++) + AddRelationship(data.Relationships[i], state); _sessionId = data.SessionId; _unavailableGuilds = unavailableGuilds; @@ -1499,6 +1502,29 @@ namespace Discord.WebSocket } } return; + + //Relationships + case "RELATIONSHIP_ADD": + { + await _gatewayLogger.DebugAsync("Received Dispatch (RELATIONSHIP_ADD)").ConfigureAwait(false); + + var addedModel = (payload as JToken).ToObject(_serializer); + var before = State.GetRelationship(addedModel.Id); + var after = AddRelationship(addedModel, State); + + await _relationshipAddedEvent.InvokeAsync(before, after); + return; + } + case "RELATIONSHIP_REMOVE": + { + await _gatewayLogger.DebugAsync("Received Dispatch (RELATIONSHIP_REMOVE)").ConfigureAwait(false); + + var removedModel = (payload as JToken).ToObject(_serializer); + var removed = RemoveRelationship(removedModel.Id); + + await _relationshipRemovedEvent.InvokeAsync(removed); + return; + } //Ignored (User only) case "CHANNEL_PINS_ACK": @@ -1650,6 +1676,18 @@ namespace Discord.WebSocket return channel; } + internal SocketRelationship AddRelationship(Relationship model, ClientState state) + { + var relation = SocketRelationship.Create(this, state, model); + State.AddRelationship(relation); + return relation; + } + internal SocketRelationship RemoveRelationship(ulong id) + { + var relation = State.RemoveRelationship(id); + return relation; + } + //IDiscordClient ConnectionState IDiscordClient.ConnectionState => _connection.State; diff --git a/src/Discord.Net.WebSocket/Entities/Users/SocketRelationship.cs b/src/Discord.Net.WebSocket/Entities/Users/SocketRelationship.cs index 1fed00801..a7bbcb805 100644 --- a/src/Discord.Net.WebSocket/Entities/Users/SocketRelationship.cs +++ b/src/Discord.Net.WebSocket/Entities/Users/SocketRelationship.cs @@ -1,11 +1,23 @@ -using System; +using Model = Discord.API.Relationship; namespace Discord.WebSocket { public class SocketRelationship : IRelationship { - public RelationshipType Type { get; private set; } + public RelationshipType Type { get; internal set; } - public IUser User { get; private set; } + public IUser User { get; internal set; } + + public SocketRelationship(RelationshipType type, IUser user) + { + Type = type; + User = user; + } + + internal static SocketRelationship Create(DiscordSocketClient discord, ClientState state, Model model) + { + SocketSimpleUser user = SocketSimpleUser.Create(discord, state, model.User); + return new SocketRelationship(model.Type, user); + } } } diff --git a/src/Discord.Net.WebSocket/Entities/Users/SocketUser.cs b/src/Discord.Net.WebSocket/Entities/Users/SocketUser.cs index 5b47a1ad4..452f7f50e 100644 --- a/src/Discord.Net.WebSocket/Entities/Users/SocketUser.cs +++ b/src/Discord.Net.WebSocket/Entities/Users/SocketUser.cs @@ -56,19 +56,13 @@ namespace Discord.WebSocket async Task IUser.CreateDMChannelAsync(RequestOptions options) => await CreateDMChannelAsync(options).ConfigureAwait(false); - public Task AddFriendAsync(RequestOptions options = null) - { - throw new NotImplementedException(); - } + public async Task AddFriendAsync(RequestOptions options = null) + => await Discord.ApiClient.AddFriendAsync(Id, options); - public Task BlockUserAsync(RequestOptions options = null) - { - throw new NotImplementedException(); - } + public async Task BlockUserAsync(RequestOptions options = null) + => await Discord.ApiClient.BlockUserAsync(Id, options); - public Task RemoveRelationshipAsync(RequestOptions options = null) - { - throw new NotImplementedException(); - } + public async Task RemoveRelationshipAsync(RequestOptions options = null) + => await Discord.ApiClient.RemoveRelationshipAsync(Id, options); } }