| @@ -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 | |||
| } | |||
| } | |||
| @@ -1,11 +0,0 @@ | |||
| #pragma warning disable CS1591 | |||
| namespace Discord.API | |||
| { | |||
| internal enum RelationshipType | |||
| { | |||
| Friend = 1, | |||
| Blocked = 2, | |||
| IncomingPending = 3, | |||
| OutgoingPending = 4 | |||
| } | |||
| } | |||
| @@ -121,13 +121,14 @@ namespace Discord.Rest | |||
| } | |||
| /// <inheritdoc /> | |||
| public void Dispose() => Dispose(true); | |||
| public Task<IReadOnlyCollection<IRelationship>> GetRelationshipsAsync() => ApiClient.GetRelationshipsAsync(); | |||
| //IDiscordClient | |||
| ConnectionState IDiscordClient.ConnectionState => ConnectionState.Disconnected; | |||
| ISelfUser IDiscordClient.CurrentUser => CurrentUser; | |||
| Task<IReadOnlyCollection<IRelationship>> IDiscordClient.GetRelationshipsAsync() | |||
| => Task.FromResult<IReadOnlyCollection<IRelationship>>(null); | |||
| Task<IApplication> IDiscordClient.GetApplicationInfoAsync() { throw new NotSupportedException(); } | |||
| Task<IChannel> IDiscordClient.GetChannelAsync(ulong id, CacheMode mode) | |||
| @@ -1030,10 +1030,10 @@ namespace Discord.API | |||
| } | |||
| //Relationships | |||
| public async Task<IReadOnlyCollection<IRelationship>> GetRelationshipsAsync(RequestOptions options = null) | |||
| public async Task<IReadOnlyCollection<Relationship>> GetRelationshipsAsync(RequestOptions options = null) | |||
| { | |||
| options = RequestOptions.CreateOrClone(options); | |||
| return await SendAsync<IReadOnlyCollection<IRelationship>>("GET", () => "users/@me/relationships", new BucketIds(), options: options).ConfigureAwait(false); | |||
| return await SendAsync<IReadOnlyCollection<Relationship>>("GET", () => "users/@me/relationships", new BucketIds(), options: options).ConfigureAwait(false); | |||
| } | |||
| public async Task AddFriendAsync(ulong userId, RequestOptions options = null) | |||
| { | |||
| @@ -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<ulong, SocketChannel> _channels; | |||
| private readonly ConcurrentDictionary<ulong, SocketDMChannel> _dmChannels; | |||
| private readonly ConcurrentDictionary<ulong, SocketGuild> _guilds; | |||
| private readonly ConcurrentDictionary<ulong, SocketGlobalUser> _users; | |||
| private readonly ConcurrentDictionary<ulong, SocketRelationship> _relations; | |||
| private readonly ConcurrentHashSet<ulong> _groupChannels; | |||
| internal IReadOnlyCollection<SocketChannel> Channels => _channels.ToReadOnlyCollection(); | |||
| @@ -22,6 +24,7 @@ namespace Discord.WebSocket | |||
| internal IReadOnlyCollection<SocketGroupChannel> GroupChannels => _groupChannels.Select(x => GetChannel(x) as SocketGroupChannel).ToReadOnlyCollection(_groupChannels); | |||
| internal IReadOnlyCollection<SocketGuild> Guilds => _guilds.ToReadOnlyCollection(); | |||
| internal IReadOnlyCollection<SocketGlobalUser> Users => _users.ToReadOnlyCollection(); | |||
| internal IReadOnlyCollection<SocketRelationship> Relationships => _relations.ToReadOnlyCollection(); | |||
| internal IReadOnlyCollection<ISocketPrivateChannel> PrivateChannels => | |||
| _dmChannels.Select(x => x.Value as ISocketPrivateChannel).Concat( | |||
| @@ -36,6 +39,7 @@ namespace Discord.WebSocket | |||
| _dmChannels = new ConcurrentDictionary<ulong, SocketDMChannel>(ConcurrentHashSet.DefaultConcurrencyLevel, (int)(dmChannelCount * CollectionMultiplier)); | |||
| _guilds = new ConcurrentDictionary<ulong, SocketGuild>(ConcurrentHashSet.DefaultConcurrencyLevel, (int)(guildCount * CollectionMultiplier)); | |||
| _users = new ConcurrentDictionary<ulong, SocketGlobalUser>(ConcurrentHashSet.DefaultConcurrencyLevel, (int)(estimatedUsersCount * CollectionMultiplier)); | |||
| _relations = new ConcurrentDictionary<ulong, SocketRelationship>(ConcurrentHashSet.DefaultConcurrencyLevel, (int)AverageRelationshipsPerUser); | |||
| _groupChannels = new ConcurrentHashSet<ulong>(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; | |||
| } | |||
| } | |||
| } | |||
| @@ -221,5 +221,19 @@ namespace Discord.WebSocket | |||
| remove { _recipientRemovedEvent.Remove(value); } | |||
| } | |||
| private readonly AsyncEvent<Func<SocketGroupUser, Task>> _recipientRemovedEvent = new AsyncEvent<Func<SocketGroupUser, Task>>(); | |||
| // relationships | |||
| public event Func<SocketRelationship, SocketRelationship, Task> RelationshipAdd | |||
| { | |||
| add { _relationshipAddedEvent.Add(value); } | |||
| remove { _relationshipAddedEvent.Remove(value); } | |||
| } | |||
| private readonly AsyncEvent<Func<SocketRelationship, SocketRelationship, Task>> _relationshipAddedEvent = new AsyncEvent<Func<SocketRelationship, SocketRelationship, Task>>(); | |||
| public event Func<SocketRelationship, Task> RelationshipRemoved | |||
| { | |||
| add { _relationshipRemovedEvent.Add(value); } | |||
| remove { _relationshipRemovedEvent.Remove(value); } | |||
| } | |||
| private readonly AsyncEvent<Func<SocketRelationship, Task>> _relationshipRemovedEvent = new AsyncEvent<Func<SocketRelationship, Task>>(); | |||
| } | |||
| } | |||
| @@ -70,6 +70,7 @@ namespace Discord.WebSocket | |||
| public IReadOnlyCollection<SocketGroupChannel> GroupChannels | |||
| => State.PrivateChannels.Select(x => x as SocketGroupChannel).Where(x => x != null).ToImmutableArray(); | |||
| public IReadOnlyCollection<RestVoiceRegion> VoiceRegions => _voiceRegions.ToReadOnlyCollection(); | |||
| public IReadOnlyCollection<SocketRelationship> Relationships => State.Relationships; | |||
| /// <summary> Creates a new REST/WebSocket discord client. </summary> | |||
| public DiscordSocketClient() : this(new DiscordSocketConfig()) { } | |||
| @@ -270,7 +271,7 @@ namespace Discord.WebSocket | |||
| /// <inheritdoc /> | |||
| public Task<RestInvite> GetInviteAsync(string inviteId) | |||
| => ClientHelper.GetInviteAsync(this, inviteId); | |||
| /// <inheritdoc /> | |||
| 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<Relationship>(_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<Relationship>(_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; | |||
| @@ -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); | |||
| } | |||
| } | |||
| } | |||
| @@ -56,19 +56,13 @@ namespace Discord.WebSocket | |||
| async Task<IDMChannel> 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); | |||
| } | |||
| } | |||