Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add relationship features #369

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/Discord.Net.Core/Discord.Net.Core.csproj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project ToolsVersion="15.0" Sdk="Microsoft.NET.Sdk" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Project ToolsVersion="15.0" Sdk="Microsoft.NET.Sdk" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VersionPrefix>1.0.0</VersionPrefix>
<VersionSuffix Condition="'$(BuildNumber)' == ''">rc-dev</VersionSuffix>
Expand Down
9 changes: 9 additions & 0 deletions src/Discord.Net.Core/Entities/Users/IRelationship.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace Discord
{
public interface IRelationship
{
RelationshipType Type { get; }

IUser User { get; }
}
}
7 changes: 7 additions & 0 deletions src/Discord.Net.Core/Entities/Users/IUser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,12 @@ public interface IUser : ISnowflakeEntity, IMentionable, IPresence
Task<IDMChannel> GetDMChannelAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null);
/// <summary> Returns a private message channel to this user, creating one if it does not already exist. </summary>
Task<IDMChannel> CreateDMChannelAsync(RequestOptions options = null);

/// <summary> Adds this user as a friend, this will remove a block </summary>
Task AddFriendAsync(RequestOptions options = null);
/// <summary> Blocks this user, and removes the user as a friend </summary>
Task BlockUserAsync(RequestOptions options = null);
/// <summary> Removes the relationship of this user </summary>
Task RemoveRelationshipAsync(RequestOptions options = null);
}
}
11 changes: 11 additions & 0 deletions src/Discord.Net.Core/Entities/Users/RelationshipType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace Discord
{
public enum RelationshipType
{
None,
Friend,
Blocked,
IncomingPending,
OutgoingPending
}
}
4 changes: 3 additions & 1 deletion src/Discord.Net.Core/IDiscordClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,14 @@ public interface IDiscordClient : IDisposable
Task<IGuild> GetGuildAsync(ulong id, CacheMode mode = CacheMode.AllowDownload);
Task<IReadOnlyCollection<IGuild>> GetGuildsAsync(CacheMode mode = CacheMode.AllowDownload);
Task<IGuild> CreateGuildAsync(string name, IVoiceRegion region, Stream jpegIcon = null);

Task<IInvite> GetInviteAsync(string inviteId);

Task<IUser> GetUserAsync(ulong id, CacheMode mode = CacheMode.AllowDownload);
Task<IUser> GetUserAsync(string username, string discriminator);

Task<IReadOnlyCollection<IRelationship>> GetRelationshipsAsync();

Task<IReadOnlyCollection<IVoiceRegion>> GetVoiceRegionsAsync();
Task<IVoiceRegion> GetVoiceRegionAsync(string id);
}
Expand Down
11 changes: 0 additions & 11 deletions src/Discord.Net.Rest/API/Common/RelationshipType.cs

This file was deleted.

7 changes: 5 additions & 2 deletions src/Discord.Net.Rest/BaseDiscordClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -121,11 +121,14 @@ internal virtual void Dispose(bool disposing)
}
/// <inheritdoc />
public void Dispose() => Dispose(true);

//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)
Expand Down
6 changes: 6 additions & 0 deletions src/Discord.Net.Rest/ClientHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -128,5 +128,11 @@ public static async Task<RestVoiceRegion> GetVoiceRegionAsync(BaseDiscordClient
var models = await client.ApiClient.GetVoiceRegionsAsync().ConfigureAwait(false);
return models.Select(x => RestVoiceRegion.Create(client, x)).Where(x => x.Id == id).FirstOrDefault();
}

public static async Task<IReadOnlyCollection<RestRelationship>> GetRelationshipsAsync(BaseDiscordClient client)
{
var models = await client.ApiClient.GetRelationshipsAsync().ConfigureAwait(false);
return models.Select(r => RestRelationship.Create(client, r)).ToImmutableArray();
}
}
}
40 changes: 34 additions & 6 deletions src/Discord.Net.Rest/DiscordRestApiClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ public async Task<TResponse> SendJsonAsync<TResponse>(string method, string endp
internal Task<TResponse> SendMultipartAsync<TResponse>(string method, Expression<Func<string>> endpointExpr, IReadOnlyDictionary<string, object> multipartArgs, BucketIds ids,
ClientBucketType clientBucket = ClientBucketType.Unbucketed, RequestOptions options = null, [CallerMemberName] string funcName = null)
=> SendMultipartAsync<TResponse>(method, GetEndpoint(endpointExpr), multipartArgs, GetBucketId(ids, endpointExpr, AuthTokenType, funcName), clientBucket, options);
public async Task<TResponse> SendMultipartAsync<TResponse>(string method, string endpoint, IReadOnlyDictionary<string, object> multipartArgs,
public async Task<TResponse> SendMultipartAsync<TResponse>(string method, string endpoint, IReadOnlyDictionary<string, object> multipartArgs,
string bucketId = null, ClientBucketType clientBucket = ClientBucketType.Unbucketed, RequestOptions options = null)
{
options = options ?? new RequestOptions();
Expand Down Expand Up @@ -387,7 +387,7 @@ public async Task ModifyGuildChannelsAsync(ulong guildId, IEnumerable<Rest.Modif
break;
}
}

//Channel Messages
public async Task<Message> GetChannelMessageAsync(ulong channelId, ulong messageId, RequestOptions options = null)
{
Expand Down Expand Up @@ -678,7 +678,7 @@ public async Task<Guild> CreateGuildAsync(CreateGuildParams args, RequestOptions
Preconditions.NotNullOrWhitespace(args.Name, nameof(args.Name));
Preconditions.NotNullOrWhitespace(args.RegionId, nameof(args.RegionId));
options = RequestOptions.CreateOrClone(options);

return await SendJsonAsync<Guild>("POST", () => "guilds", args, new BucketIds(), options: options).ConfigureAwait(false);
}
public async Task<Guild> DeleteGuildAsync(ulong guildId, RequestOptions options = null)
Expand Down Expand Up @@ -886,14 +886,14 @@ public async Task<Invite> DeleteInviteAsync(string inviteId, RequestOptions opti
{
Preconditions.NotNullOrEmpty(inviteId, nameof(inviteId));
options = RequestOptions.CreateOrClone(options);

return await SendAsync<Invite>("DELETE", () => $"invites/{inviteId}", new BucketIds(), options: options).ConfigureAwait(false);
}
public async Task AcceptInviteAsync(string inviteId, RequestOptions options = null)
{
Preconditions.NotNullOrEmpty(inviteId, nameof(inviteId));
options = RequestOptions.CreateOrClone(options);

await SendAsync("POST", () => $"invites/{inviteId}", new BucketIds(), options: options).ConfigureAwait(false);
}

Expand Down Expand Up @@ -1028,6 +1028,34 @@ public async Task<User> GetUserAsync(ulong userId, RequestOptions options = null
}
catch (HttpException ex) when (ex.HttpCode == HttpStatusCode.NotFound) { return null; }
}

//Relationships
public async Task<IReadOnlyCollection<Relationship>> GetRelationshipsAsync(RequestOptions options = null)
{
options = RequestOptions.CreateOrClone(options);
return await SendAsync<IReadOnlyCollection<Relationship>>("GET", () => "users/@me/relationships", new BucketIds(), options: options).ConfigureAwait(false);
}
public async Task AddFriendAsync(ulong userId, RequestOptions options = null)
{
Preconditions.NotEqual(userId, 0, nameof(userId));
options = RequestOptions.CreateOrClone(options);

await SendJsonAsync("PUT", () => $"users/@me/relationships/{userId}", new object(), new BucketIds(), options: options).ConfigureAwait(false);
}
public async Task BlockUserAsync(ulong userId, RequestOptions options = null)
{
Preconditions.NotEqual(userId, 0, nameof(userId));
options = RequestOptions.CreateOrClone(options);

await SendJsonAsync("PUT", () => $"users/@me/relationships/{userId}", new { type = 2 }, new BucketIds(), options: options).ConfigureAwait(false);
}
public async Task RemoveRelationshipAsync(ulong userId, RequestOptions options = null)
{
Preconditions.NotEqual(userId, 0, nameof(userId));
options = RequestOptions.CreateOrClone(options);

await SendAsync("DELETE", () => $"users/@me/relationships/{userId}", new BucketIds(), options: options).ConfigureAwait(false);
}

//Current User/DMs
public async Task<User> GetMyUserAsync(RequestOptions options = null)
Expand Down Expand Up @@ -1193,7 +1221,7 @@ private static Func<BucketIds, string> CreateBucketId(Expression<Func<string>> e
int argId = int.Parse(format.Substring(leftIndex + 1, rightIndex - leftIndex - 1));
string fieldName = GetFieldName(methodArgs[argId + 1]);
int? mappedId;

mappedId = BucketIds.GetIndex(fieldName);
if(!mappedId.HasValue && rightIndex != endIndex && format.Length > rightIndex + 1 && format[rightIndex + 1] == '/') //Ignore the next slash
rightIndex++;
Expand Down
3 changes: 3 additions & 0 deletions src/Discord.Net.Rest/DiscordRestClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@ public Task<IReadOnlyCollection<RestVoiceRegion>> GetVoiceRegionsAsync()
public Task<RestVoiceRegion> GetVoiceRegionAsync(string id)
=> ClientHelper.GetVoiceRegionAsync(this, id);

public Task<IReadOnlyCollection<RestRelationship>> GetRelationshipsAsync()
=> ClientHelper.GetRelationshipsAsync(this);

//IDiscordClient
async Task<IApplication> IDiscordClient.GetApplicationInfoAsync()
=> await GetApplicationInfoAsync().ConfigureAwait(false);
Expand Down
26 changes: 26 additions & 0 deletions src/Discord.Net.Rest/Entities/Users/RestRelationship.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Text;
using Model = Discord.API.Relationship;

namespace Discord.Rest
{
public class RestRelationship : IRelationship
{
public RelationshipType Type { get; internal set; }

public IUser User { get; internal set; }

internal RestRelationship(RelationshipType type, IUser user)
{
Type = type;
User = user;
}

internal static RestRelationship Create(BaseDiscordClient discord, Model model)
{
RestUser user = RestUser.Create(discord, model.User);
return new RestRelationship(model.Type, user);
}
}
}
9 changes: 9 additions & 0 deletions src/Discord.Net.Rest/Entities/Users/RestSelfUser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,5 +49,14 @@ public async Task ModifyAsync(Action<SelfUserProperties> func, RequestOptions op
var model = await UserHelper.ModifyAsync(this, Discord, func, options).ConfigureAwait(false);
Update(model);
}

Task IUser.AddFriendAsync(RequestOptions options) =>
throw new InvalidOperationException("You can't friend yourself!");

Task IUser.BlockUserAsync(RequestOptions options) =>
throw new InvalidOperationException("You can't block yourself!");

Task IUser.RemoveRelationshipAsync(RequestOptions options) =>
throw new InvalidOperationException("You don't have any relations with yourself!");
}
}
7 changes: 7 additions & 0 deletions src/Discord.Net.Rest/Entities/Users/RestUser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,13 @@ public Task<RestDMChannel> CreateDMChannelAsync(RequestOptions options = null)

public override string ToString() => $"{Username}#{Discriminator}";
private string DebuggerDisplay => $"{Username}#{Discriminator} ({Id}{(IsBot ? ", Bot" : "")})";

public Task AddFriendAsync(RequestOptions options = null)
=> Discord.ApiClient.AddFriendAsync(Id, options);
public Task BlockUserAsync(RequestOptions options = null)
=> Discord.ApiClient.BlockUserAsync(Id, options);
public Task RemoveRelationshipAsync(RequestOptions options = null)
=> Discord.ApiClient.RemoveRelationshipAsync(Id, options);

//IUser
Task<IDMChannel> IUser.GetDMChannelAsync(CacheMode mode, RequestOptions options)
Expand Down
7 changes: 7 additions & 0 deletions src/Discord.Net.Rpc/Entities/Users/RpcUser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,5 +54,12 @@ Task<IDMChannel> IUser.GetDMChannelAsync(CacheMode mode, RequestOptions options)
=> Task.FromResult<IDMChannel>(null);
async Task<IDMChannel> IUser.CreateDMChannelAsync(RequestOptions options)
=> await CreateDMChannelAsync(options).ConfigureAwait(false);

Task IUser.AddFriendAsync(RequestOptions options)
=> throw new NotSupportedException();
Task IUser.BlockUserAsync(RequestOptions options)
=> throw new NotSupportedException();
Task IUser.RemoveRelationshipAsync(RequestOptions options)
=> throw new NotSupportedException();
}
}
20 changes: 20 additions & 0 deletions src/Discord.Net.WebSocket/ClientState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@ internal class ClientState
private readonly ConcurrentDictionary<ulong, SocketDMChannel> _dmChannels;
private readonly ConcurrentDictionary<ulong, SocketGuild> _guilds;
private readonly ConcurrentDictionary<ulong, SocketGlobalUser> _users;
private readonly ConcurrentDictionary<ulong, SocketRelationship> _relationships;
private readonly ConcurrentHashSet<ulong> _groupChannels;

internal IReadOnlyCollection<SocketChannel> Channels => _channels.ToReadOnlyCollection();
internal IReadOnlyCollection<SocketDMChannel> DMChannels => _dmChannels.ToReadOnlyCollection();
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 => _relationships.ToReadOnlyCollection();

internal IReadOnlyCollection<ISocketPrivateChannel> PrivateChannels =>
_dmChannels.Select(x => x.Value as ISocketPrivateChannel).Concat(
Expand All @@ -36,6 +38,7 @@ public ClientState(int guildCount, int dmChannelCount)
_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));
_relationships = new ConcurrentDictionary<ulong, SocketRelationship>(ConcurrentHashSet.DefaultConcurrencyLevel, 35);
_groupChannels = new ConcurrentHashSet<ulong>(ConcurrentHashSet.DefaultConcurrencyLevel, (int)(10 * CollectionMultiplier));
}

Expand Down Expand Up @@ -126,5 +129,22 @@ internal SocketGlobalUser RemoveUser(ulong id)
return user;
return null;
}

internal SocketRelationship GetRelationship(ulong id)
{
if (_relationships.TryGetValue(id, out SocketRelationship value))
return value;
return null;
}
internal void AddRelationship(SocketRelationship relationship)
{
_relationships[relationship.User.Id] = relationship;
}
internal SocketRelationship RemoveRelationship(ulong id)
{
if (_relationships.TryRemove(id, out SocketRelationship value))
return value;
return null;
}
}
}
14 changes: 14 additions & 0 deletions src/Discord.Net.WebSocket/DiscordSocketClient.Events.cs
Original file line number Diff line number Diff line change
Expand Up @@ -221,5 +221,19 @@ public event Func<SocketGroupUser, Task> RecipientRemoved
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>>();
}
}
Loading