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