| @@ -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> | |||
| @@ -0,0 +1,9 @@ | |||
| namespace Discord | |||
| { | |||
| public interface IRelationship | |||
| { | |||
| RelationshipType Type { get; } | |||
| IUser User { get; } | |||
| } | |||
| } | |||
| @@ -21,5 +21,12 @@ namespace Discord | |||
| 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); | |||
| } | |||
| } | |||
| @@ -0,0 +1,11 @@ | |||
| namespace Discord | |||
| { | |||
| public enum RelationshipType | |||
| { | |||
| None, | |||
| Friend, | |||
| Blocked, | |||
| IncomingPending, | |||
| OutgoingPending | |||
| } | |||
| } | |||
| @@ -25,12 +25,14 @@ namespace Discord | |||
| 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); | |||
| } | |||
| @@ -1,11 +0,0 @@ | |||
| #pragma warning disable CS1591 | |||
| namespace Discord.API | |||
| { | |||
| internal enum RelationshipType | |||
| { | |||
| Friend = 1, | |||
| Blocked = 2, | |||
| IncomingPending = 3, | |||
| OutgoingPending = 4 | |||
| } | |||
| } | |||
| @@ -121,11 +121,14 @@ namespace Discord.Rest | |||
| } | |||
| /// <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) | |||
| @@ -128,5 +128,11 @@ namespace Discord.Rest | |||
| 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(); | |||
| } | |||
| } | |||
| } | |||
| @@ -241,7 +241,7 @@ namespace Discord.API | |||
| 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(); | |||
| @@ -387,7 +387,7 @@ namespace Discord.API | |||
| break; | |||
| } | |||
| } | |||
| //Channel Messages | |||
| public async Task<Message> GetChannelMessageAsync(ulong channelId, ulong messageId, RequestOptions options = null) | |||
| { | |||
| @@ -678,7 +678,7 @@ namespace Discord.API | |||
| 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) | |||
| @@ -886,14 +886,14 @@ namespace Discord.API | |||
| { | |||
| 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); | |||
| } | |||
| @@ -1028,6 +1028,34 @@ namespace Discord.API | |||
| } | |||
| 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) | |||
| @@ -1193,7 +1221,7 @@ namespace Discord.API | |||
| 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++; | |||
| @@ -89,6 +89,9 @@ namespace Discord.Rest | |||
| 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); | |||
| @@ -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); | |||
| } | |||
| } | |||
| } | |||
| @@ -49,5 +49,14 @@ namespace Discord.Rest | |||
| 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!"); | |||
| } | |||
| } | |||
| @@ -53,6 +53,13 @@ namespace Discord.Rest | |||
| 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) | |||
| @@ -54,5 +54,12 @@ namespace Discord.Rpc | |||
| => 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(); | |||
| } | |||
| } | |||
| @@ -15,6 +15,7 @@ namespace Discord.WebSocket | |||
| 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(); | |||
| @@ -22,6 +23,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 => _relationships.ToReadOnlyCollection(); | |||
| internal IReadOnlyCollection<ISocketPrivateChannel> PrivateChannels => | |||
| _dmChannels.Select(x => x.Value as ISocketPrivateChannel).Concat( | |||
| @@ -36,6 +38,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)); | |||
| _relationships = new ConcurrentDictionary<ulong, SocketRelationship>(ConcurrentHashSet.DefaultConcurrencyLevel, 35); | |||
| _groupChannels = new ConcurrentHashSet<ulong>(ConcurrentHashSet.DefaultConcurrencyLevel, (int)(10 * CollectionMultiplier)); | |||
| } | |||
| @@ -126,5 +129,22 @@ namespace Discord.WebSocket | |||
| 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; | |||
| } | |||
| } | |||
| } | |||
| @@ -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,6 +271,9 @@ namespace Discord.WebSocket | |||
| /// <inheritdoc /> | |||
| public Task<RestInvite> GetInviteAsync(string inviteId) | |||
| => ClientHelper.GetInviteAsync(this, inviteId); | |||
| public Task<IReadOnlyCollection<SocketRelationship>> GetRelationshipsAsync() | |||
| => Task.FromResult(State.Relationships); | |||
| /// <inheritdoc /> | |||
| public SocketUser GetUser(ulong id) | |||
| @@ -484,6 +488,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 +1505,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 +1679,21 @@ namespace Discord.WebSocket | |||
| return channel; | |||
| } | |||
| internal SocketRelationship GetRelationship(ulong id) | |||
| { | |||
| return State.GetRelationship(id); | |||
| } | |||
| internal SocketRelationship AddRelationship(Relationship model, ClientState state) | |||
| { | |||
| var relationship = SocketRelationship.Create(this, state, model); | |||
| state.AddRelationship(SocketRelationship.Create(this, state, model)); | |||
| return relationship; | |||
| } | |||
| internal SocketRelationship RemoveRelationship(ulong id) | |||
| { | |||
| return State.RemoveRelationship(id); | |||
| } | |||
| //IDiscordClient | |||
| ConnectionState IDiscordClient.ConnectionState => _connection.State; | |||
| @@ -1692,5 +1736,8 @@ namespace Discord.WebSocket | |||
| => await StartAsync().ConfigureAwait(false); | |||
| async Task IDiscordClient.StopAsync() | |||
| => await StopAsync().ConfigureAwait(false); | |||
| async Task<IReadOnlyCollection<IRelationship>> IDiscordClient.GetRelationshipsAsync() | |||
| => await GetRelationshipsAsync(); | |||
| } | |||
| } | |||
| @@ -0,0 +1,23 @@ | |||
| using Model = Discord.API.Relationship; | |||
| namespace Discord.WebSocket | |||
| { | |||
| public class SocketRelationship : IRelationship | |||
| { | |||
| public RelationshipType Type { get; internal 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); | |||
| } | |||
| } | |||
| } | |||
| @@ -46,6 +46,15 @@ namespace Discord.WebSocket | |||
| public Task ModifyAsync(Action<SelfUserProperties> func, RequestOptions options = null) | |||
| => UserHelper.ModifyAsync(this, Discord, func, options); | |||
| 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!"); | |||
| internal new SocketSelfUser Clone() => MemberwiseClone() as SocketSelfUser; | |||
| } | |||
| } | |||
| @@ -21,6 +21,7 @@ namespace Discord.WebSocket | |||
| public string Mention => MentionUtils.MentionUser(Id); | |||
| public Game? Game => Presence.Game; | |||
| public UserStatus Status => Presence.Status; | |||
| public RelationshipType Relationship => Discord.GetRelationship(Id)?.Type ?? RelationshipType.None; | |||
| internal SocketUser(DiscordSocketClient discord, ulong id) | |||
| : base(discord, id) | |||
| @@ -55,5 +56,12 @@ namespace Discord.WebSocket | |||
| => Task.FromResult<IDMChannel>(GlobalUser.DMChannel); | |||
| async Task<IDMChannel> IUser.CreateDMChannelAsync(RequestOptions options) | |||
| => await CreateDMChannelAsync(options).ConfigureAwait(false); | |||
| public async Task AddFriendAsync(RequestOptions options = null) | |||
| => await Discord.ApiClient.AddFriendAsync(Id, options); | |||
| public async Task BlockUserAsync(RequestOptions options = null) | |||
| => await Discord.ApiClient.BlockUserAsync(Id, options); | |||
| public async Task RemoveRelationshipAsync(RequestOptions options = null) | |||
| => await Discord.ApiClient.RemoveRelationshipAsync(Id, options); | |||
| } | |||
| } | |||