diff --git a/Discord.Net.sln b/Discord.Net.sln index 11960606b..4ce5a23fc 100644 --- a/Discord.Net.sln +++ b/Discord.Net.sln @@ -1,28 +1,50 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 14 -VisualStudioVersion = 14.0.25123.0 +VisualStudioVersion = 14.0.25420.1 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Discord.Net", "src\Discord.Net\Discord.Net.xproj", "{91E9E7BD-75C9-4E98-84AA-2C271922E5C2}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{F7F3E124-93C7-4846-AE87-9CE12BD82859}" + ProjectSection(SolutionItems) = preProject + global.json = global.json + README.md = README.md + EndProjectSection EndProject -Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Discord.Net.Commands", "src\Discord.Net.Commands\Discord.Net.Commands.xproj", "{078DD7E6-943D-4D09-AFC2-D2BA58B76C9C}" +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Discord.Net", "src\Discord.Net\Discord.Net.xproj", "{496DB20A-A455-4D01-B6BC-90FE6D7C6B81}" +EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Discord.Net.Core", "src\Discord.Net.Core\Discord.Net.Core.xproj", "{91E9E7BD-75C9-4E98-84AA-2C271922E5C2}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Impls", "Impls", "{288C363D-A636-4EAE-9AC1-4698B641B26E}" +EndProject +Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Discord.Net.Utils", "src\Discord.Net.Utils\Discord.Net.Utils.shproj", "{2B75119C-9893-4AAA-8D38-6176EEB09060}" +EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Discord.Net.Rest", "src\Discord.Net.Rest\Discord.Net.Rest.xproj", "{BFC6DC28-0351-4573-926A-D4124244C04F}" EndProject Global + GlobalSection(SharedMSBuildProjectFiles) = preSolution + src\Discord.Net.Utils\Discord.Net.Utils.projitems*{2b75119c-9893-4aaa-8d38-6176eeb09060}*SharedItemsImports = 13 + EndGlobalSection GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution + {496DB20A-A455-4D01-B6BC-90FE6D7C6B81}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {496DB20A-A455-4D01-B6BC-90FE6D7C6B81}.Debug|Any CPU.Build.0 = Debug|Any CPU + {496DB20A-A455-4D01-B6BC-90FE6D7C6B81}.Release|Any CPU.ActiveCfg = Release|Any CPU + {496DB20A-A455-4D01-B6BC-90FE6D7C6B81}.Release|Any CPU.Build.0 = Release|Any CPU {91E9E7BD-75C9-4E98-84AA-2C271922E5C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {91E9E7BD-75C9-4E98-84AA-2C271922E5C2}.Debug|Any CPU.Build.0 = Debug|Any CPU {91E9E7BD-75C9-4E98-84AA-2C271922E5C2}.Release|Any CPU.ActiveCfg = Release|Any CPU {91E9E7BD-75C9-4E98-84AA-2C271922E5C2}.Release|Any CPU.Build.0 = Release|Any CPU - {078DD7E6-943D-4D09-AFC2-D2BA58B76C9C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {078DD7E6-943D-4D09-AFC2-D2BA58B76C9C}.Debug|Any CPU.Build.0 = Debug|Any CPU - {078DD7E6-943D-4D09-AFC2-D2BA58B76C9C}.Release|Any CPU.ActiveCfg = Release|Any CPU - {078DD7E6-943D-4D09-AFC2-D2BA58B76C9C}.Release|Any CPU.Build.0 = Release|Any CPU + {BFC6DC28-0351-4573-926A-D4124244C04F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BFC6DC28-0351-4573-926A-D4124244C04F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BFC6DC28-0351-4573-926A-D4124244C04F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BFC6DC28-0351-4573-926A-D4124244C04F}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {BFC6DC28-0351-4573-926A-D4124244C04F} = {288C363D-A636-4EAE-9AC1-4698B641B26E} + EndGlobalSection EndGlobal diff --git a/src/Discord.Net/API/CDN.cs b/src/Discord.Net.Core/API/CDN.cs similarity index 96% rename from src/Discord.Net/API/CDN.cs rename to src/Discord.Net.Core/API/CDN.cs index 3973344db..d7a3f1d39 100644 --- a/src/Discord.Net/API/CDN.cs +++ b/src/Discord.Net.Core/API/CDN.cs @@ -1,6 +1,6 @@ namespace Discord.API { - internal static class CDN + public static class CDN { public static string GetApplicationIconUrl(ulong appId, string iconId) => iconId != null ? $"{DiscordConfig.CDNUrl}app-icons/{appId}/{iconId}.jpg" : null; diff --git a/src/Discord.Net/API/Common/Application.cs b/src/Discord.Net.Core/API/Common/Application.cs similarity index 100% rename from src/Discord.Net/API/Common/Application.cs rename to src/Discord.Net.Core/API/Common/Application.cs diff --git a/src/Discord.Net/API/Common/Attachment.cs b/src/Discord.Net.Core/API/Common/Attachment.cs similarity index 100% rename from src/Discord.Net/API/Common/Attachment.cs rename to src/Discord.Net.Core/API/Common/Attachment.cs diff --git a/src/Discord.Net/API/Common/Ban.cs b/src/Discord.Net.Core/API/Common/Ban.cs similarity index 100% rename from src/Discord.Net/API/Common/Ban.cs rename to src/Discord.Net.Core/API/Common/Ban.cs diff --git a/src/Discord.Net/API/Common/Channel.cs b/src/Discord.Net.Core/API/Common/Channel.cs similarity index 100% rename from src/Discord.Net/API/Common/Channel.cs rename to src/Discord.Net.Core/API/Common/Channel.cs diff --git a/src/Discord.Net/API/Common/Connection.cs b/src/Discord.Net.Core/API/Common/Connection.cs similarity index 100% rename from src/Discord.Net/API/Common/Connection.cs rename to src/Discord.Net.Core/API/Common/Connection.cs diff --git a/src/Discord.Net/API/Common/Embed.cs b/src/Discord.Net.Core/API/Common/Embed.cs similarity index 100% rename from src/Discord.Net/API/Common/Embed.cs rename to src/Discord.Net.Core/API/Common/Embed.cs diff --git a/src/Discord.Net/API/Common/EmbedProvider.cs b/src/Discord.Net.Core/API/Common/EmbedProvider.cs similarity index 100% rename from src/Discord.Net/API/Common/EmbedProvider.cs rename to src/Discord.Net.Core/API/Common/EmbedProvider.cs diff --git a/src/Discord.Net/API/Common/EmbedThumbnail.cs b/src/Discord.Net.Core/API/Common/EmbedThumbnail.cs similarity index 100% rename from src/Discord.Net/API/Common/EmbedThumbnail.cs rename to src/Discord.Net.Core/API/Common/EmbedThumbnail.cs diff --git a/src/Discord.Net/API/Common/Emoji.cs b/src/Discord.Net.Core/API/Common/Emoji.cs similarity index 100% rename from src/Discord.Net/API/Common/Emoji.cs rename to src/Discord.Net.Core/API/Common/Emoji.cs diff --git a/src/Discord.Net/API/Common/Game.cs b/src/Discord.Net.Core/API/Common/Game.cs similarity index 100% rename from src/Discord.Net/API/Common/Game.cs rename to src/Discord.Net.Core/API/Common/Game.cs diff --git a/src/Discord.Net/API/Common/Guild.cs b/src/Discord.Net.Core/API/Common/Guild.cs similarity index 100% rename from src/Discord.Net/API/Common/Guild.cs rename to src/Discord.Net.Core/API/Common/Guild.cs diff --git a/src/Discord.Net/API/Common/GuildEmbed.cs b/src/Discord.Net.Core/API/Common/GuildEmbed.cs similarity index 100% rename from src/Discord.Net/API/Common/GuildEmbed.cs rename to src/Discord.Net.Core/API/Common/GuildEmbed.cs diff --git a/src/Discord.Net/API/Common/GuildMember.cs b/src/Discord.Net.Core/API/Common/GuildMember.cs similarity index 100% rename from src/Discord.Net/API/Common/GuildMember.cs rename to src/Discord.Net.Core/API/Common/GuildMember.cs diff --git a/src/Discord.Net/API/Common/Integration.cs b/src/Discord.Net.Core/API/Common/Integration.cs similarity index 100% rename from src/Discord.Net/API/Common/Integration.cs rename to src/Discord.Net.Core/API/Common/Integration.cs diff --git a/src/Discord.Net/API/Common/IntegrationAccount.cs b/src/Discord.Net.Core/API/Common/IntegrationAccount.cs similarity index 100% rename from src/Discord.Net/API/Common/IntegrationAccount.cs rename to src/Discord.Net.Core/API/Common/IntegrationAccount.cs diff --git a/src/Discord.Net/API/Common/Invite.cs b/src/Discord.Net.Core/API/Common/Invite.cs similarity index 100% rename from src/Discord.Net/API/Common/Invite.cs rename to src/Discord.Net.Core/API/Common/Invite.cs diff --git a/src/Discord.Net/API/Common/InviteChannel.cs b/src/Discord.Net.Core/API/Common/InviteChannel.cs similarity index 100% rename from src/Discord.Net/API/Common/InviteChannel.cs rename to src/Discord.Net.Core/API/Common/InviteChannel.cs diff --git a/src/Discord.Net/API/Common/InviteGuild.cs b/src/Discord.Net.Core/API/Common/InviteGuild.cs similarity index 100% rename from src/Discord.Net/API/Common/InviteGuild.cs rename to src/Discord.Net.Core/API/Common/InviteGuild.cs diff --git a/src/Discord.Net/API/Common/InviteMetadata.cs b/src/Discord.Net.Core/API/Common/InviteMetadata.cs similarity index 100% rename from src/Discord.Net/API/Common/InviteMetadata.cs rename to src/Discord.Net.Core/API/Common/InviteMetadata.cs diff --git a/src/Discord.Net/API/Common/Message.cs b/src/Discord.Net.Core/API/Common/Message.cs similarity index 100% rename from src/Discord.Net/API/Common/Message.cs rename to src/Discord.Net.Core/API/Common/Message.cs diff --git a/src/Discord.Net/API/Common/Overwrite.cs b/src/Discord.Net.Core/API/Common/Overwrite.cs similarity index 100% rename from src/Discord.Net/API/Common/Overwrite.cs rename to src/Discord.Net.Core/API/Common/Overwrite.cs diff --git a/src/Discord.Net/API/Common/Presence.cs b/src/Discord.Net.Core/API/Common/Presence.cs similarity index 100% rename from src/Discord.Net/API/Common/Presence.cs rename to src/Discord.Net.Core/API/Common/Presence.cs diff --git a/src/Discord.Net/API/Common/ReadState.cs b/src/Discord.Net.Core/API/Common/ReadState.cs similarity index 100% rename from src/Discord.Net/API/Common/ReadState.cs rename to src/Discord.Net.Core/API/Common/ReadState.cs diff --git a/src/Discord.Net/API/Common/Relationship.cs b/src/Discord.Net.Core/API/Common/Relationship.cs similarity index 100% rename from src/Discord.Net/API/Common/Relationship.cs rename to src/Discord.Net.Core/API/Common/Relationship.cs diff --git a/src/Discord.Net/API/Common/RelationshipType.cs b/src/Discord.Net.Core/API/Common/RelationshipType.cs similarity index 100% rename from src/Discord.Net/API/Common/RelationshipType.cs rename to src/Discord.Net.Core/API/Common/RelationshipType.cs diff --git a/src/Discord.Net/API/Common/Role.cs b/src/Discord.Net.Core/API/Common/Role.cs similarity index 100% rename from src/Discord.Net/API/Common/Role.cs rename to src/Discord.Net.Core/API/Common/Role.cs diff --git a/src/Discord.Net/API/Common/User.cs b/src/Discord.Net.Core/API/Common/User.cs similarity index 100% rename from src/Discord.Net/API/Common/User.cs rename to src/Discord.Net.Core/API/Common/User.cs diff --git a/src/Discord.Net/API/Common/UserGuild.cs b/src/Discord.Net.Core/API/Common/UserGuild.cs similarity index 100% rename from src/Discord.Net/API/Common/UserGuild.cs rename to src/Discord.Net.Core/API/Common/UserGuild.cs diff --git a/src/Discord.Net/API/Common/VoiceRegion.cs b/src/Discord.Net.Core/API/Common/VoiceRegion.cs similarity index 100% rename from src/Discord.Net/API/Common/VoiceRegion.cs rename to src/Discord.Net.Core/API/Common/VoiceRegion.cs diff --git a/src/Discord.Net/API/Common/VoiceState.cs b/src/Discord.Net.Core/API/Common/VoiceState.cs similarity index 100% rename from src/Discord.Net/API/Common/VoiceState.cs rename to src/Discord.Net.Core/API/Common/VoiceState.cs diff --git a/src/Discord.Net/API/DiscordRestApiClient.cs b/src/Discord.Net.Core/API/DiscordRestApiClient.cs similarity index 75% rename from src/Discord.Net/API/DiscordRestApiClient.cs rename to src/Discord.Net.Core/API/DiscordRestApiClient.cs index 0d0b22c25..8946de96b 100644 --- a/src/Discord.Net/API/DiscordRestApiClient.cs +++ b/src/Discord.Net.Core/API/DiscordRestApiClient.cs @@ -4,7 +4,6 @@ using Discord.Net; using Discord.Net.Converters; using Discord.Net.Queue; using Discord.Net.Rest; -using Discord.Rest; using Newtonsoft.Json; using System; using System.Collections.Generic; @@ -28,6 +27,7 @@ namespace Discord.API protected readonly JsonSerializer _serializer; protected readonly SemaphoreSlim _stateLock; private readonly RestClientProvider _restClientProvider; + private readonly string _userAgent; protected string _authToken; protected bool _isDisposed; @@ -36,11 +36,13 @@ namespace Discord.API public LoginState LoginState { get; private set; } public TokenType AuthTokenType { get; private set; } - internal RequestQueue RequestQueue { get; private set; } + public User CurrentUser { get; private set; } + public RequestQueue RequestQueue { get; private set; } - public DiscordRestApiClient(RestClientProvider restClientProvider, JsonSerializer serializer = null, RequestQueue requestQueue = null) + public DiscordRestApiClient(RestClientProvider restClientProvider, string userAgent, JsonSerializer serializer = null, RequestQueue requestQueue = null) { _restClientProvider = restClientProvider; + _userAgent = userAgent; _serializer = serializer ?? new JsonSerializer { ContractResolver = new DiscordContractResolver() }; RequestQueue = requestQueue; @@ -52,7 +54,7 @@ namespace Discord.API { _restClient = _restClientProvider(baseUrl); _restClient.SetHeader("accept", "*/*"); - _restClient.SetHeader("user-agent", DiscordRestConfig.UserAgent); + _restClient.SetHeader("user-agent", _userAgent); _restClient.SetHeader("authorization", GetPrefixedToken(AuthTokenType, _authToken)); } internal static string GetPrefixedToken(TokenType tokenType, string token) @@ -111,6 +113,8 @@ namespace Discord.API _authToken = token; _restClient.SetHeader("authorization", GetPrefixedToken(AuthTokenType, _authToken)); + CurrentUser = await GetMyUserAsync(); + LoginState = LoginState.LoggedIn; } catch (Exception) @@ -144,6 +148,7 @@ namespace Discord.API await RequestQueue.SetCancelTokenAsync(CancellationToken.None).ConfigureAwait(false); _restClient.SetCancelToken(CancellationToken.None); + CurrentUser = null; LoginState = LoginState.LoggedOut; } @@ -268,8 +273,8 @@ namespace Discord.API { Preconditions.NotEqual(guildId, 0, nameof(guildId)); Preconditions.NotNull(args, nameof(args)); - Preconditions.GreaterThan(args._bitrate, 0, nameof(args.Bitrate)); - Preconditions.NotNullOrWhitespace(args._name, nameof(args.Name)); + Preconditions.GreaterThan(args.Bitrate, 0, nameof(args.Bitrate)); + Preconditions.NotNullOrWhitespace(args.Name, nameof(args.Name)); return await SendAsync("POST", $"guilds/{guildId}/channels", args, options: options).ConfigureAwait(false); } @@ -283,8 +288,8 @@ namespace Discord.API { Preconditions.NotEqual(channelId, 0, nameof(channelId)); Preconditions.NotNull(args, nameof(args)); - Preconditions.AtLeast(args._position, 0, nameof(args.Position)); - Preconditions.NotNullOrEmpty(args._name, nameof(args.Name)); + Preconditions.AtLeast(args.Position, 0, nameof(args.Position)); + Preconditions.NotNullOrEmpty(args.Name, nameof(args.Name)); return await SendAsync("PATCH", $"channels/{channelId}", args, options: options).ConfigureAwait(false); } @@ -292,8 +297,8 @@ namespace Discord.API { Preconditions.NotEqual(channelId, 0, nameof(channelId)); Preconditions.NotNull(args, nameof(args)); - Preconditions.AtLeast(args._position, 0, nameof(args.Position)); - Preconditions.NotNullOrEmpty(args._name, nameof(args.Name)); + Preconditions.AtLeast(args.Position, 0, nameof(args.Position)); + Preconditions.NotNullOrEmpty(args.Name, nameof(args.Name)); return await SendAsync("PATCH", $"channels/{channelId}", args, options: options).ConfigureAwait(false); } @@ -301,10 +306,10 @@ namespace Discord.API { Preconditions.NotEqual(channelId, 0, nameof(channelId)); Preconditions.NotNull(args, nameof(args)); - Preconditions.GreaterThan(args._bitrate, 0, nameof(args.Bitrate)); - Preconditions.AtLeast(args._userLimit, 0, nameof(args.Bitrate)); - Preconditions.AtLeast(args._position, 0, nameof(args.Position)); - Preconditions.NotNullOrEmpty(args._name, nameof(args.Name)); + Preconditions.GreaterThan(args.Bitrate, 0, nameof(args.Bitrate)); + Preconditions.AtLeast(args.UserLimit, 0, nameof(args.Bitrate)); + Preconditions.AtLeast(args.Position, 0, nameof(args.Position)); + Preconditions.NotNullOrEmpty(args.Name, nameof(args.Name)); return await SendAsync("PATCH", $"channels/{channelId}", args, options: options).ConfigureAwait(false); } @@ -326,6 +331,132 @@ namespace Discord.API break; } } + + //Channel Messages + public async Task GetChannelMessageAsync(ulong channelId, ulong messageId, RequestOptions options = null) + { + Preconditions.NotEqual(channelId, 0, nameof(channelId)); + Preconditions.NotEqual(messageId, 0, nameof(messageId)); + + try + { + return await SendAsync("GET", $"channels/{channelId}/messages/{messageId}", options: options).ConfigureAwait(false); + } + catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { return null; } + } + public async Task> GetChannelMessagesAsync(ulong channelId, GetChannelMessagesParams args, RequestOptions options = null) + { + Preconditions.NotEqual(channelId, 0, nameof(channelId)); + Preconditions.NotNull(args, nameof(args)); + Preconditions.AtLeast(args.Limit, 0, nameof(args.Limit)); + Preconditions.AtMost(args.Limit, DiscordConfig.MaxMessagesPerBatch, nameof(args.Limit)); + + int limit = args.Limit.GetValueOrDefault(DiscordConfig.MaxMessagesPerBatch); + ulong? relativeId = args.RelativeMessageId.IsSpecified ? args.RelativeMessageId.Value : (ulong?)null; + string relativeDir; + + switch (args.RelativeDirection.GetValueOrDefault(Direction.Before)) + { + case Direction.Before: + default: + relativeDir = "before"; + break; + case Direction.After: + relativeDir = "after"; + break; + case Direction.Around: + relativeDir = "around"; + break; + } + + string endpoint; + if (relativeId != null) + endpoint = $"channels/{channelId}/messages?limit={limit}&{relativeDir}={relativeId}"; + else + endpoint = $"channels/{channelId}/messages?limit={limit}"; + return await SendAsync>("GET", endpoint, options: options).ConfigureAwait(false); + } + public async Task CreateMessageAsync(ulong channelId, CreateMessageParams args, RequestOptions options = null) + { + Preconditions.NotEqual(channelId, 0, nameof(channelId)); + Preconditions.NotNull(args, nameof(args)); + Preconditions.NotNullOrEmpty(args.Content, nameof(args.Content)); + if (args.Content.Length > DiscordConfig.MaxMessageSize) + throw new ArgumentException($"Message content is too long, length must be less or equal to {DiscordConfig.MaxMessageSize}.", nameof(args.Content)); + + return await SendAsync("POST", $"channels/{channelId}/messages", args, GlobalBucket.DirectMessage, options: options).ConfigureAwait(false); + } + public async Task UploadFileAsync(ulong channelId, UploadFileParams args, RequestOptions options = null) + { + Preconditions.NotNull(args, nameof(args)); + Preconditions.NotEqual(channelId, 0, nameof(channelId)); + + if (args.Content.GetValueOrDefault(null) == null) + args.Content = ""; + else if (args.Content.IsSpecified) + { + if (args.Content.Value == null) + args.Content = ""; + if (args.Content.Value?.Length > DiscordConfig.MaxMessageSize) + throw new ArgumentOutOfRangeException($"Message content is too long, length must be less or equal to {DiscordConfig.MaxMessageSize}.", nameof(args.Content)); + } + + return await SendMultipartAsync("POST", $"channels/{channelId}/messages", args.ToDictionary(), GlobalBucket.DirectMessage, options: options).ConfigureAwait(false); + } + public async Task DeleteMessageAsync(ulong channelId, ulong messageId, RequestOptions options = null) + { + Preconditions.NotEqual(channelId, 0, nameof(channelId)); + Preconditions.NotEqual(messageId, 0, nameof(messageId)); + + await SendAsync("DELETE", $"channels/{channelId}/messages/{messageId}", options: options).ConfigureAwait(false); + } + public async Task DeleteMessagesAsync(ulong channelId, DeleteMessagesParams args, RequestOptions options = null) + { + Preconditions.NotEqual(channelId, 0, nameof(channelId)); + Preconditions.NotNull(args, nameof(args)); + + Preconditions.NotNull(args.MessageIds, nameof(args.MessageIds)); + Preconditions.AtMost(args.MessageIds.Length, 100, nameof(args.MessageIds.Length)); + + switch (args.MessageIds.Length) + { + case 0: + return; + case 1: + await DeleteMessageAsync(channelId, args.MessageIds[0]).ConfigureAwait(false); + break; + default: + await SendAsync("POST", $"channels/{channelId}/messages/bulk_delete", args, options: options).ConfigureAwait(false); + break; + } + } + public async Task ModifyMessageAsync(ulong channelId, ulong messageId, ModifyMessageParams args, RequestOptions options = null) + { + Preconditions.NotEqual(channelId, 0, nameof(channelId)); + Preconditions.NotEqual(messageId, 0, nameof(messageId)); + Preconditions.NotNull(args, nameof(args)); + if (args.Content.IsSpecified) + { + Preconditions.NotNullOrEmpty(args.Content, nameof(args.Content)); + if (args.Content.Value.Length > DiscordConfig.MaxMessageSize) + throw new ArgumentOutOfRangeException($"Message content is too long, length must be less or equal to {DiscordConfig.MaxMessageSize}.", nameof(args.Content)); + } + + return await SendAsync("PATCH", $"channels/{channelId}/messages/{messageId}", args, options: options).ConfigureAwait(false); + } + public async Task AckMessageAsync(ulong channelId, ulong messageId, RequestOptions options = null) + { + Preconditions.NotEqual(channelId, 0, nameof(channelId)); + Preconditions.NotEqual(messageId, 0, nameof(messageId)); + + await SendAsync("POST", $"channels/{channelId}/messages/{messageId}/ack", options: options).ConfigureAwait(false); + } + public async Task TriggerTypingIndicatorAsync(ulong channelId, RequestOptions options = null) + { + Preconditions.NotEqual(channelId, 0, nameof(channelId)); + + await SendAsync("POST", $"channels/{channelId}/typing", options: options).ConfigureAwait(false); + } //Channel Permissions public async Task ModifyChannelPermissionsAsync(ulong channelId, ulong targetId, ModifyChannelPermissionsParams args, RequestOptions options = null) @@ -399,7 +530,7 @@ namespace Discord.API { Preconditions.NotNull(args, nameof(args)); Preconditions.NotNullOrWhitespace(args.Name, nameof(args.Name)); - Preconditions.NotNullOrWhitespace(args.Region, nameof(args.Region)); + Preconditions.NotNullOrWhitespace(args.RegionId, nameof(args.RegionId)); return await SendAsync("POST", "guilds", args, options: options).ConfigureAwait(false); } @@ -419,11 +550,11 @@ namespace Discord.API { Preconditions.NotEqual(guildId, 0, nameof(guildId)); Preconditions.NotNull(args, nameof(args)); - Preconditions.NotEqual(args._afkChannelId, 0, nameof(args.AFKChannelId)); - Preconditions.AtLeast(args._afkTimeout, 0, nameof(args.AFKTimeout)); - Preconditions.NotNullOrEmpty(args._name, nameof(args.Name)); - Preconditions.GreaterThan(args._ownerId, 0, nameof(args.OwnerId)); - Preconditions.NotNull(args._region, nameof(args.Region)); + Preconditions.NotEqual(args.AfkChannelId, 0, nameof(args.AfkChannelId)); + Preconditions.AtLeast(args.AfkTimeout, 0, nameof(args.AfkTimeout)); + Preconditions.NotNullOrEmpty(args.Name, nameof(args.Name)); + Preconditions.GreaterThan(args.OwnerId, 0, nameof(args.OwnerId)); + Preconditions.NotNull(args.RegionId, nameof(args.RegionId)); return await SendAsync("PATCH", $"guilds/{guildId}", args, options: options).ConfigureAwait(false); } @@ -456,7 +587,7 @@ namespace Discord.API Preconditions.NotEqual(guildId, 0, nameof(guildId)); Preconditions.NotEqual(userId, 0, nameof(userId)); Preconditions.NotNull(args, nameof(args)); - Preconditions.AtLeast(args._deleteMessageDays, 0, nameof(args.DeleteMessageDays)); + Preconditions.AtLeast(args.DeleteMessageDays, 0, nameof(args.DeleteMessageDays)); await SendAsync("PUT", $"guilds/{guildId}/bans/{userId}", args, options: options).ConfigureAwait(false); } @@ -514,8 +645,8 @@ namespace Discord.API Preconditions.NotEqual(guildId, 0, nameof(guildId)); Preconditions.NotEqual(integrationId, 0, nameof(integrationId)); Preconditions.NotNull(args, nameof(args)); - Preconditions.AtLeast(args._expireBehavior, 0, nameof(args.ExpireBehavior)); - Preconditions.AtLeast(args._expireGracePeriod, 0, nameof(args.ExpireGracePeriod)); + Preconditions.AtLeast(args.ExpireBehavior, 0, nameof(args.ExpireBehavior)); + Preconditions.AtLeast(args.ExpireGracePeriod, 0, nameof(args.ExpireGracePeriod)); return await SendAsync("PATCH", $"guilds/{guildId}/integrations/{integrationId}", args, options: options).ConfigureAwait(false); } @@ -552,18 +683,18 @@ namespace Discord.API return await SendAsync>("GET", $"guilds/{guildId}/invites", options: options).ConfigureAwait(false); } - public async Task GetChannelInvitesAsync(ulong channelId, RequestOptions options = null) + public async Task> GetChannelInvitesAsync(ulong channelId, RequestOptions options = null) { Preconditions.NotEqual(channelId, 0, nameof(channelId)); - return await SendAsync("GET", $"channels/{channelId}/invites", options: options).ConfigureAwait(false); + return await SendAsync>("GET", $"channels/{channelId}/invites", options: options).ConfigureAwait(false); } public async Task CreateChannelInviteAsync(ulong channelId, CreateChannelInviteParams args, RequestOptions options = null) { Preconditions.NotEqual(channelId, 0, nameof(channelId)); Preconditions.NotNull(args, nameof(args)); - Preconditions.AtLeast(args._maxAge, 0, nameof(args.MaxAge)); - Preconditions.AtLeast(args._maxUses, 0, nameof(args.MaxUses)); + Preconditions.AtLeast(args.MaxAge, 0, nameof(args.MaxAge)); + Preconditions.AtLeast(args.MaxUses, 0, nameof(args.MaxUses)); return await SendAsync("POST", $"channels/{channelId}/invites", args, options: options).ConfigureAwait(false); } @@ -596,42 +727,15 @@ namespace Discord.API { Preconditions.NotEqual(guildId, 0, nameof(guildId)); Preconditions.NotNull(args, nameof(args)); - Preconditions.GreaterThan(args._limit, 0, nameof(args.Limit)); - Preconditions.GreaterThan(args._afterUserId, 0, nameof(args.AfterUserId)); - - int limit = args._limit.GetValueOrDefault(int.MaxValue); - ulong afterUserId = args._afterUserId.GetValueOrDefault(0); + Preconditions.GreaterThan(args.Limit, 0, nameof(args.Limit)); + Preconditions.AtMost(args.Limit, DiscordConfig.MaxUsersPerBatch, nameof(args.Limit)); + Preconditions.GreaterThan(args.AfterUserId, 0, nameof(args.AfterUserId)); - List result; - if (args._limit.IsSpecified) - result = new List((limit + DiscordConfig.MaxUsersPerBatch - 1) / DiscordConfig.MaxUsersPerBatch); - else - result = new List(); - - while (true) - { - int runLimit = (limit >= DiscordConfig.MaxUsersPerBatch) ? DiscordConfig.MaxUsersPerBatch : limit; - string endpoint = $"guilds/{guildId}/members?limit={runLimit}&after={afterUserId}"; - var models = await SendAsync("GET", endpoint, options: options).ConfigureAwait(false); - - //Was this an empty batch? - if (models.Length == 0) break; - - result.Add(models); - - limit -= DiscordConfig.MaxUsersPerBatch; - afterUserId = models[models.Length - 1].User.Id; - - //Was this an incomplete (the last) batch? - if (models.Length != DiscordConfig.MaxUsersPerBatch) break; - } - - if (result.Count > 1) - return result.SelectMany(x => x).ToImmutableArray(); - else if (result.Count == 1) - return result[0]; - else - return ImmutableArray.Create(); + int limit = args.Limit.GetValueOrDefault(int.MaxValue); + ulong afterUserId = args.AfterUserId.GetValueOrDefault(0); + + string endpoint = $"guilds/{guildId}/members?limit={limit}&after={afterUserId}"; + return await SendAsync>("GET", endpoint, options: options).ConfigureAwait(false); } public async Task RemoveGuildMemberAsync(ulong guildId, ulong userId, RequestOptions options = null) { @@ -646,7 +750,18 @@ namespace Discord.API Preconditions.NotEqual(userId, 0, nameof(userId)); Preconditions.NotNull(args, nameof(args)); - await SendAsync("PATCH", $"guilds/{guildId}/members/{userId}", args, GuildBucket.ModifyMember, guildId, options: options).ConfigureAwait(false); + bool isCurrentUser = userId == CurrentUser.Id; + + if (isCurrentUser && args.Nickname.IsSpecified) + { + var nickArgs = new ModifyCurrentUserNickParams(args.Nickname.Value ?? ""); + await ModifyMyNickAsync(guildId, nickArgs).ConfigureAwait(false); + args.Nickname = Optional.Create(); //Remove + } + if (!isCurrentUser || args.Deaf.IsSpecified || args.Mute.IsSpecified || args.RoleIds.IsSpecified) + { + await SendAsync("PATCH", $"guilds/{guildId}/members/{userId}", args, GuildBucket.ModifyMember, guildId, options: options).ConfigureAwait(false); + } } //Guild Roles @@ -674,9 +789,9 @@ namespace Discord.API Preconditions.NotEqual(guildId, 0, nameof(guildId)); Preconditions.NotEqual(roleId, 0, nameof(roleId)); Preconditions.NotNull(args, nameof(args)); - Preconditions.AtLeast(args._color, 0, nameof(args.Color)); - Preconditions.NotNullOrEmpty(args._name, nameof(args.Name)); - Preconditions.AtLeast(args._position, 0, nameof(args.Position)); + Preconditions.AtLeast(args.Color, 0, nameof(args.Color)); + Preconditions.NotNullOrEmpty(args.Name, nameof(args.Name)); + Preconditions.AtLeast(args.Position, 0, nameof(args.Position)); return await SendAsync("PATCH", $"guilds/{guildId}/roles/{roleId}", args, options: options).ConfigureAwait(false); } @@ -697,266 +812,6 @@ namespace Discord.API } } - //Messages - public async Task GetChannelMessageAsync(ulong channelId, ulong messageId, RequestOptions options = null) - { - Preconditions.NotEqual(channelId, 0, nameof(channelId)); - Preconditions.NotEqual(messageId, 0, nameof(messageId)); - - try - { - return await SendAsync("GET", $"channels/{channelId}/messages/{messageId}", options: options).ConfigureAwait(false); - } - catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { return null; } - } - public async Task> GetChannelMessagesAsync(ulong channelId, GetChannelMessagesParams args, RequestOptions options = null) - { - Preconditions.NotEqual(channelId, 0, nameof(channelId)); - Preconditions.NotNull(args, nameof(args)); - Preconditions.AtLeast(args.Limit, 0, nameof(args.Limit)); - - int limit = args.Limit; - ulong? relativeId = args._relativeMessageId.IsSpecified ? args._relativeMessageId.Value : (ulong?)null; - string relativeDir; - - switch (args.RelativeDirection) - { - case Direction.Before: - default: - relativeDir = "before"; - break; - case Direction.After: - relativeDir = "after"; - break; - case Direction.Around: - relativeDir = "around"; - break; - } - - int runs = (limit + DiscordConfig.MaxMessagesPerBatch - 1) / DiscordConfig.MaxMessagesPerBatch; - int lastRunCount = limit - (runs - 1) * DiscordConfig.MaxMessagesPerBatch; - var result = new API.Message[runs][]; - - int i = 0; - for (; i < runs; i++) - { - int runCount = i == (runs - 1) ? lastRunCount : DiscordConfig.MaxMessagesPerBatch; - string endpoint; - if (relativeId != null) - endpoint = $"channels/{channelId}/messages?limit={runCount}&{relativeDir}={relativeId}"; - else - endpoint = $"channels/{channelId}/messages?limit={runCount}"; - var models = await SendAsync("GET", endpoint, options: options).ConfigureAwait(false); - - //Was this an empty batch? - if (models.Length == 0) break; - - //We can't assume these messages to be sorted by id (fails in rare cases), lets search for the highest/lowest id ourselves - switch (args.RelativeDirection) - { - case Direction.Before: - case Direction.Around: - default: - result[i] = models; - relativeId = ulong.MaxValue; - //Lowest id *should* be the last one - for (int j = models.Length - 1; j >= 0; j--) - { - if (models[j].Id < relativeId.Value) - relativeId = models[j].Id; - } - break; - case Direction.After: - result[runs - i - 1] = models; - relativeId = ulong.MinValue; - //Highest id *should* be the first one - for (int j = 0; j < models.Length; j++) - { - if (models[j].Id > relativeId.Value) - relativeId = models[j].Id; - } - break; - } - - //Was this an incomplete (the last) batch? - if (models.Length != DiscordConfig.MaxMessagesPerBatch) { i++; break; } - } - - if (i > 1) - { - switch (args.RelativeDirection) - { - case Direction.Before: - case Direction.Around: - default: - return result.Take(i).SelectMany(x => x).ToImmutableArray(); - case Direction.After: - return result.Skip(runs - i).Take(i).SelectMany(x => x).ToImmutableArray(); - } - } - else if (i == 1) - { - switch (args.RelativeDirection) - { - case Direction.Before: - case Direction.Around: - default: - return result[0]; - case Direction.After: - return result[runs - 1]; - } - } - else - return ImmutableArray.Create(); - } - public Task CreateMessageAsync(ulong guildId, ulong channelId, CreateMessageParams args, RequestOptions options = null) - { - Preconditions.NotEqual(guildId, 0, nameof(guildId)); - - return CreateMessageInternalAsync(guildId, channelId, args); - } - public Task CreateDMMessageAsync(ulong channelId, CreateMessageParams args, RequestOptions options = null) - { - return CreateMessageInternalAsync(0, channelId, args); - } - private async Task CreateMessageInternalAsync(ulong guildId, ulong channelId, CreateMessageParams args, RequestOptions options = null) - { - Preconditions.NotEqual(channelId, 0, nameof(channelId)); - Preconditions.NotNull(args, nameof(args)); - Preconditions.NotNullOrEmpty(args._content, nameof(args.Content)); - if (args._content.Length > DiscordConfig.MaxMessageSize) - throw new ArgumentException($"Message content is too long, length must be less or equal to {DiscordConfig.MaxMessageSize}.", nameof(args.Content)); - - if (guildId != 0) - return await SendAsync("POST", $"channels/{channelId}/messages", args, GuildBucket.SendEditMessage, guildId, options: options).ConfigureAwait(false); - else - return await SendAsync("POST", $"channels/{channelId}/messages", args, GlobalBucket.DirectMessage, options: options).ConfigureAwait(false); - } - public Task UploadFileAsync(ulong guildId, ulong channelId, UploadFileParams args, RequestOptions options = null) - { - Preconditions.NotEqual(guildId, 0, nameof(guildId)); - - return UploadFileInternalAsync(guildId, channelId, args); - } - public Task UploadDMFileAsync(ulong channelId, UploadFileParams args, RequestOptions options = null) - { - return UploadFileInternalAsync(0, channelId, args); - } - private async Task UploadFileInternalAsync(ulong guildId, ulong channelId, UploadFileParams args, RequestOptions options = null) - { - Preconditions.NotNull(args, nameof(args)); - Preconditions.NotEqual(channelId, 0, nameof(channelId)); - - if (args._content.GetValueOrDefault(null) == null) - args._content = ""; - else if (args._content.IsSpecified) - { - if (args._content.Value == null) - args._content = ""; - if (args._content.Value?.Length > DiscordConfig.MaxMessageSize) - throw new ArgumentOutOfRangeException($"Message content is too long, length must be less or equal to {DiscordConfig.MaxMessageSize}.", nameof(args.Content)); - } - - if (guildId != 0) - return await SendMultipartAsync("POST", $"channels/{channelId}/messages", args.ToDictionary(), GuildBucket.SendEditMessage, guildId, options: options).ConfigureAwait(false); - else - return await SendMultipartAsync("POST", $"channels/{channelId}/messages", args.ToDictionary(), GlobalBucket.DirectMessage, options: options).ConfigureAwait(false); - } - public Task DeleteMessageAsync(ulong guildId, ulong channelId, ulong messageId, RequestOptions options = null) - { - Preconditions.NotEqual(guildId, 0, nameof(guildId)); - - return DeleteMessageInternalAsync(guildId, channelId, messageId); - } - public Task DeleteDMMessageAsync(ulong channelId, ulong messageId, RequestOptions options = null) - { - return DeleteMessageInternalAsync(0, channelId, messageId); - } - private async Task DeleteMessageInternalAsync(ulong guildId, ulong channelId, ulong messageId, RequestOptions options = null) - { - Preconditions.NotEqual(channelId, 0, nameof(channelId)); - Preconditions.NotEqual(messageId, 0, nameof(messageId)); - - if (guildId != 0) - await SendAsync("DELETE", $"channels/{channelId}/messages/{messageId}", GuildBucket.DeleteMessage, guildId, options: options).ConfigureAwait(false); - else - await SendAsync("DELETE", $"channels/{channelId}/messages/{messageId}", options: options).ConfigureAwait(false); - } - public Task DeleteMessagesAsync(ulong guildId, ulong channelId, DeleteMessagesParams args, RequestOptions options = null) - { - Preconditions.NotEqual(guildId, 0, nameof(guildId)); - - return DeleteMessagesInternalAsync(guildId, channelId, args); - } - public Task DeleteDMMessagesAsync(ulong channelId, DeleteMessagesParams args, RequestOptions options = null) - { - return DeleteMessagesInternalAsync(0, channelId, args); - } - private async Task DeleteMessagesInternalAsync(ulong guildId, ulong channelId, DeleteMessagesParams args, RequestOptions options = null) - { - Preconditions.NotEqual(channelId, 0, nameof(channelId)); - Preconditions.NotNull(args, nameof(args)); - - var messageIds = args._messages; - Preconditions.NotNull(args._messages, nameof(args.MessageIds)); - Preconditions.AtMost(messageIds.Length, 100, nameof(messageIds.Length)); - - switch (messageIds.Length) - { - case 0: - return; - case 1: - await DeleteMessageInternalAsync(guildId, channelId, messageIds[0]).ConfigureAwait(false); - break; - default: - if (guildId != 0) - await SendAsync("POST", $"channels/{channelId}/messages/bulk_delete", args, GuildBucket.DeleteMessages, guildId, options: options).ConfigureAwait(false); - else - await SendAsync("POST", $"channels/{channelId}/messages/bulk_delete", args, options: options).ConfigureAwait(false); - break; - } - } - public Task ModifyMessageAsync(ulong guildId, ulong channelId, ulong messageId, ModifyMessageParams args, RequestOptions options = null) - { - Preconditions.NotEqual(guildId, 0, nameof(guildId)); - - return ModifyMessageInternalAsync(guildId, channelId, messageId, args); - } - public Task ModifyDMMessageAsync(ulong channelId, ulong messageId, ModifyMessageParams args, RequestOptions options = null) - { - return ModifyMessageInternalAsync(0, channelId, messageId, args); - } - private async Task ModifyMessageInternalAsync(ulong guildId, ulong channelId, ulong messageId, ModifyMessageParams args, RequestOptions options = null) - { - Preconditions.NotEqual(channelId, 0, nameof(channelId)); - Preconditions.NotEqual(messageId, 0, nameof(messageId)); - Preconditions.NotNull(args, nameof(args)); - if (args._content.IsSpecified) - { - Preconditions.NotNullOrEmpty(args._content, nameof(args.Content)); - if (args._content.Value.Length > DiscordConfig.MaxMessageSize) - throw new ArgumentOutOfRangeException($"Message content is too long, length must be less or equal to {DiscordConfig.MaxMessageSize}.", nameof(args.Content)); - } - - if (guildId != 0) - return await SendAsync("PATCH", $"channels/{channelId}/messages/{messageId}", args, GuildBucket.SendEditMessage, guildId, options: options).ConfigureAwait(false); - else - return await SendAsync("PATCH", $"channels/{channelId}/messages/{messageId}", args, options: options).ConfigureAwait(false); - } - public async Task AckMessageAsync(ulong channelId, ulong messageId, RequestOptions options = null) - { - Preconditions.NotEqual(channelId, 0, nameof(channelId)); - Preconditions.NotEqual(messageId, 0, nameof(messageId)); - - await SendAsync("POST", $"channels/{channelId}/messages/{messageId}/ack", options: options).ConfigureAwait(false); - } - public async Task TriggerTypingIndicatorAsync(ulong channelId, RequestOptions options = null) - { - Preconditions.NotEqual(channelId, 0, nameof(channelId)); - - await SendAsync("POST", $"channels/{channelId}/typing", options: options).ConfigureAwait(false); - } - //Users public async Task GetUserAsync(ulong userId, RequestOptions options = null) { @@ -1012,7 +867,7 @@ namespace Discord.API public async Task ModifySelfAsync(ModifyCurrentUserParams args, RequestOptions options = null) { Preconditions.NotNull(args, nameof(args)); - Preconditions.NotNullOrEmpty(args._username, nameof(args.Username)); + Preconditions.NotNullOrEmpty(args.Username, nameof(args.Username)); return await SendAsync("PATCH", "users/@me", args, options: options).ConfigureAwait(false); } @@ -1026,7 +881,7 @@ namespace Discord.API public async Task CreateDMChannelAsync(CreateDMChannelParams args, RequestOptions options = null) { Preconditions.NotNull(args, nameof(args)); - Preconditions.GreaterThan(args._recipientId, 0, nameof(args.Recipient)); + Preconditions.GreaterThan(args.RecipientId, 0, nameof(args.RecipientId)); return await SendAsync("POST", $"users/@me/channels", args, options: options).ConfigureAwait(false); } diff --git a/src/Discord.Net/API/Image.cs b/src/Discord.Net.Core/API/Image.cs similarity index 93% rename from src/Discord.Net/API/Image.cs rename to src/Discord.Net.Core/API/Image.cs index b2357a0a6..5442bd30f 100644 --- a/src/Discord.Net/API/Image.cs +++ b/src/Discord.Net.Core/API/Image.cs @@ -2,7 +2,7 @@ namespace Discord.API { - internal struct Image + public struct Image { public Stream Stream { get; } public string Hash { get; } diff --git a/src/Discord.Net/API/Int53Attribute.cs b/src/Discord.Net.Core/API/Int53Attribute.cs similarity index 100% rename from src/Discord.Net/API/Int53Attribute.cs rename to src/Discord.Net.Core/API/Int53Attribute.cs diff --git a/src/Discord.Net.Core/API/Rest/CreateChannelInviteParams.cs b/src/Discord.Net.Core/API/Rest/CreateChannelInviteParams.cs new file mode 100644 index 000000000..8a619a8b7 --- /dev/null +++ b/src/Discord.Net.Core/API/Rest/CreateChannelInviteParams.cs @@ -0,0 +1,16 @@ +#pragma warning disable CS1591 +using Newtonsoft.Json; + +namespace Discord.API.Rest +{ + [JsonObject(MemberSerialization = MemberSerialization.OptIn)] + public class CreateChannelInviteParams + { + [JsonProperty("max_age")] + public Optional MaxAge { get; set; } + [JsonProperty("max_uses")] + public Optional MaxUses { get; set; } + [JsonProperty("temporary")] + public Optional IsTemporary { get; set; } + } +} diff --git a/src/Discord.Net/API/Rest/CreateDMChannelParams.cs b/src/Discord.Net.Core/API/Rest/CreateDMChannelParams.cs similarity index 56% rename from src/Discord.Net/API/Rest/CreateDMChannelParams.cs rename to src/Discord.Net.Core/API/Rest/CreateDMChannelParams.cs index f52b20ca1..83fe76e98 100644 --- a/src/Discord.Net/API/Rest/CreateDMChannelParams.cs +++ b/src/Discord.Net.Core/API/Rest/CreateDMChannelParams.cs @@ -7,8 +7,11 @@ namespace Discord.API.Rest public class CreateDMChannelParams { [JsonProperty("recipient_id")] - internal ulong _recipientId { get; set; } - public ulong RecipientId { set { _recipientId = value; } } - public IUser Recipient { set { _recipientId = value.Id; } } + public ulong RecipientId { get; } + + public CreateDMChannelParams(ulong recipientId) + { + RecipientId = recipientId; + } } } diff --git a/src/Discord.Net/API/Rest/CreateGuildBanParams.cs b/src/Discord.Net.Core/API/Rest/CreateGuildBanParams.cs similarity index 64% rename from src/Discord.Net/API/Rest/CreateGuildBanParams.cs rename to src/Discord.Net.Core/API/Rest/CreateGuildBanParams.cs index 3b8fce473..9c049e8a8 100644 --- a/src/Discord.Net/API/Rest/CreateGuildBanParams.cs +++ b/src/Discord.Net.Core/API/Rest/CreateGuildBanParams.cs @@ -7,7 +7,6 @@ namespace Discord.API.Rest public class CreateGuildBanParams { [JsonProperty("delete-message-days")] - internal Optional _deleteMessageDays { get; set; } - public int DeleteMessageDays { set { _deleteMessageDays = value; } } + public Optional DeleteMessageDays { get; set; } } } diff --git a/src/Discord.Net.Core/API/Rest/CreateGuildChannelParams.cs b/src/Discord.Net.Core/API/Rest/CreateGuildChannelParams.cs new file mode 100644 index 000000000..f0e06e3d2 --- /dev/null +++ b/src/Discord.Net.Core/API/Rest/CreateGuildChannelParams.cs @@ -0,0 +1,23 @@ +#pragma warning disable CS1591 +using Newtonsoft.Json; + +namespace Discord.API.Rest +{ + [JsonObject(MemberSerialization = MemberSerialization.OptIn)] + public class CreateGuildChannelParams + { + [JsonProperty("name")] + public string Name { get; } + [JsonProperty("type")] + public ChannelType Type { get; } + + [JsonProperty("bitrate")] + public Optional Bitrate { get; set; } + + public CreateGuildChannelParams(string name, ChannelType type) + { + Name = name; + Type = type; + } + } +} diff --git a/src/Discord.Net/API/Rest/CreateGuildIntegrationParams.cs b/src/Discord.Net.Core/API/Rest/CreateGuildIntegrationParams.cs similarity index 57% rename from src/Discord.Net/API/Rest/CreateGuildIntegrationParams.cs rename to src/Discord.Net.Core/API/Rest/CreateGuildIntegrationParams.cs index 8e8dfb76d..0d6e3a654 100644 --- a/src/Discord.Net/API/Rest/CreateGuildIntegrationParams.cs +++ b/src/Discord.Net.Core/API/Rest/CreateGuildIntegrationParams.cs @@ -7,9 +7,14 @@ namespace Discord.API.Rest public class CreateGuildIntegrationParams { [JsonProperty("id")] - public ulong Id { internal get; set; } - + public ulong Id { get; } [JsonProperty("type")] - public string Type { internal get; set; } + public string Type { get; } + + public CreateGuildIntegrationParams(ulong id, string type) + { + Id = id; + Type = type; + } } } diff --git a/src/Discord.Net/API/Rest/CreateGuildParams.cs b/src/Discord.Net.Core/API/Rest/CreateGuildParams.cs similarity index 52% rename from src/Discord.Net/API/Rest/CreateGuildParams.cs rename to src/Discord.Net.Core/API/Rest/CreateGuildParams.cs index 8b9bd6178..4bc18c28b 100644 --- a/src/Discord.Net/API/Rest/CreateGuildParams.cs +++ b/src/Discord.Net.Core/API/Rest/CreateGuildParams.cs @@ -1,6 +1,5 @@ #pragma warning disable CS1591 using Newtonsoft.Json; -using System.IO; namespace Discord.API.Rest { @@ -8,13 +7,17 @@ namespace Discord.API.Rest public class CreateGuildParams { [JsonProperty("name")] - public string Name { internal get; set; } - + public string Name { get; } [JsonProperty("region")] - public string Region { internal get; set; } + public string RegionId { get; } [JsonProperty("icon")] - internal Optional _icon { get; set; } - public Stream Icon { set { _icon = value != null ? new Image(value) : (Image?)null; } } + public Optional Icon { get; set; } + + public CreateGuildParams(string name, string regionId) + { + Name = name; + RegionId = regionId; + } } } diff --git a/src/Discord.Net.Core/API/Rest/CreateMessageParams.cs b/src/Discord.Net.Core/API/Rest/CreateMessageParams.cs new file mode 100644 index 000000000..9577ab579 --- /dev/null +++ b/src/Discord.Net.Core/API/Rest/CreateMessageParams.cs @@ -0,0 +1,22 @@ +#pragma warning disable CS1591 +using Newtonsoft.Json; + +namespace Discord.API.Rest +{ + [JsonObject(MemberSerialization = MemberSerialization.OptIn)] + public class CreateMessageParams + { + [JsonProperty("content")] + public string Content { get; } + + [JsonProperty("nonce")] + public Optional Nonce { get; set; } + [JsonProperty("tts")] + public Optional IsTTS { get; set; } + + public CreateMessageParams(string content) + { + Content = content; + } + } +} diff --git a/src/Discord.Net.Core/API/Rest/DeleteMessagesParams.cs b/src/Discord.Net.Core/API/Rest/DeleteMessagesParams.cs new file mode 100644 index 000000000..09b9a2bf1 --- /dev/null +++ b/src/Discord.Net.Core/API/Rest/DeleteMessagesParams.cs @@ -0,0 +1,17 @@ +#pragma warning disable CS1591 +using Newtonsoft.Json; + +namespace Discord.API.Rest +{ + [JsonObject(MemberSerialization = MemberSerialization.OptIn)] + public class DeleteMessagesParams + { + [JsonProperty("messages")] + public ulong[] MessageIds { get; } + + public DeleteMessagesParams(ulong[] messageIds) + { + MessageIds = messageIds; + } + } +} diff --git a/src/Discord.Net.Core/API/Rest/GetChannelMessagesParams.cs b/src/Discord.Net.Core/API/Rest/GetChannelMessagesParams.cs new file mode 100644 index 000000000..2d00833ca --- /dev/null +++ b/src/Discord.Net.Core/API/Rest/GetChannelMessagesParams.cs @@ -0,0 +1,10 @@ +#pragma warning disable CS1591 +namespace Discord.API.Rest +{ + public class GetChannelMessagesParams + { + public Optional Limit { get; set; } + public Optional RelativeDirection { get; set; } + public Optional RelativeMessageId { get; set; } + } +} diff --git a/src/Discord.Net/API/Rest/GetGatewayResponse.cs b/src/Discord.Net.Core/API/Rest/GetGatewayResponse.cs similarity index 100% rename from src/Discord.Net/API/Rest/GetGatewayResponse.cs rename to src/Discord.Net.Core/API/Rest/GetGatewayResponse.cs diff --git a/src/Discord.Net.Core/API/Rest/GetGuildMembersParams.cs b/src/Discord.Net.Core/API/Rest/GetGuildMembersParams.cs new file mode 100644 index 000000000..2bd34ddcb --- /dev/null +++ b/src/Discord.Net.Core/API/Rest/GetGuildMembersParams.cs @@ -0,0 +1,9 @@ +#pragma warning disable CS1591 +namespace Discord.API.Rest +{ + public class GetGuildMembersParams + { + public Optional Limit { get; set; } + public Optional AfterUserId { get; set; } + } +} diff --git a/src/Discord.Net/API/Rest/GetGuildPruneCountResponse.cs b/src/Discord.Net.Core/API/Rest/GetGuildPruneCountResponse.cs similarity index 100% rename from src/Discord.Net/API/Rest/GetGuildPruneCountResponse.cs rename to src/Discord.Net.Core/API/Rest/GetGuildPruneCountResponse.cs diff --git a/src/Discord.Net/API/Rest/GuildPruneParams.cs b/src/Discord.Net.Core/API/Rest/GuildPruneParams.cs similarity index 65% rename from src/Discord.Net/API/Rest/GuildPruneParams.cs rename to src/Discord.Net.Core/API/Rest/GuildPruneParams.cs index 9b8b3d6e1..9cff46992 100644 --- a/src/Discord.Net/API/Rest/GuildPruneParams.cs +++ b/src/Discord.Net.Core/API/Rest/GuildPruneParams.cs @@ -7,6 +7,11 @@ namespace Discord.API.Rest public class GuildPruneParams { [JsonProperty("days")] - public int Days { internal get; set; } + public int Days { get; } + + public GuildPruneParams(int days) + { + Days = days; + } } } diff --git a/src/Discord.Net/API/Rest/ModifyChannelPermissionsParams.cs b/src/Discord.Net.Core/API/Rest/ModifyChannelPermissionsParams.cs similarity index 51% rename from src/Discord.Net/API/Rest/ModifyChannelPermissionsParams.cs rename to src/Discord.Net.Core/API/Rest/ModifyChannelPermissionsParams.cs index a650eeefa..8676b22e7 100644 --- a/src/Discord.Net/API/Rest/ModifyChannelPermissionsParams.cs +++ b/src/Discord.Net.Core/API/Rest/ModifyChannelPermissionsParams.cs @@ -7,10 +7,17 @@ namespace Discord.API.Rest public class ModifyChannelPermissionsParams { [JsonProperty("type")] - public string Type { internal get; set; } + public string Type { get; } [JsonProperty("allow")] - public ulong Allow { internal get; set; } + public ulong Allow { get; } [JsonProperty("deny")] - public ulong Deny { internal get; set; } + public ulong Deny { get; } + + public ModifyChannelPermissionsParams(string type, ulong allow, ulong deny) + { + Type = type; + Allow = allow; + Deny = deny; + } } } diff --git a/src/Discord.Net/API/Rest/ModifyCurrentUserNickParams.cs b/src/Discord.Net.Core/API/Rest/ModifyCurrentUserNickParams.cs similarity index 61% rename from src/Discord.Net/API/Rest/ModifyCurrentUserNickParams.cs rename to src/Discord.Net.Core/API/Rest/ModifyCurrentUserNickParams.cs index dd6fba46e..ca7ad2bd3 100644 --- a/src/Discord.Net/API/Rest/ModifyCurrentUserNickParams.cs +++ b/src/Discord.Net.Core/API/Rest/ModifyCurrentUserNickParams.cs @@ -7,6 +7,11 @@ namespace Discord.API.Rest public class ModifyCurrentUserNickParams { [JsonProperty("nick")] - public string Nickname { internal get; set; } + public string Nickname { get; } + + public ModifyCurrentUserNickParams(string nickname) + { + Nickname = nickname; + } } } diff --git a/src/Discord.Net/API/Rest/ModifyCurrentUserParams.cs b/src/Discord.Net.Core/API/Rest/ModifyCurrentUserParams.cs similarity index 51% rename from src/Discord.Net/API/Rest/ModifyCurrentUserParams.cs rename to src/Discord.Net.Core/API/Rest/ModifyCurrentUserParams.cs index 732e46377..d11ef2b77 100644 --- a/src/Discord.Net/API/Rest/ModifyCurrentUserParams.cs +++ b/src/Discord.Net.Core/API/Rest/ModifyCurrentUserParams.cs @@ -1,6 +1,5 @@ #pragma warning disable CS1591 using Newtonsoft.Json; -using System.IO; namespace Discord.API.Rest { @@ -8,11 +7,8 @@ namespace Discord.API.Rest public class ModifyCurrentUserParams { [JsonProperty("username")] - internal Optional _username { get; set; } - public string Username { set { _username = value; } } - + public Optional Username { get; set; } [JsonProperty("avatar")] - internal Optional _avatar { get; set; } - public Stream Avatar { set { _avatar = new Image(value); } } + public Optional Avatar { get; set; } } } diff --git a/src/Discord.Net/API/Rest/ModifyGuildChannelParams.cs b/src/Discord.Net.Core/API/Rest/ModifyGuildChannelParams.cs similarity index 55% rename from src/Discord.Net/API/Rest/ModifyGuildChannelParams.cs rename to src/Discord.Net.Core/API/Rest/ModifyGuildChannelParams.cs index 868150f87..6d6ee4c24 100644 --- a/src/Discord.Net/API/Rest/ModifyGuildChannelParams.cs +++ b/src/Discord.Net.Core/API/Rest/ModifyGuildChannelParams.cs @@ -7,11 +7,8 @@ namespace Discord.API.Rest public class ModifyGuildChannelParams { [JsonProperty("name")] - internal Optional _name { get; set; } - public string Name { set { _name = value; } } - + public Optional Name { get; set; } [JsonProperty("position")] - internal Optional _position { get; set; } - public int Position { set { _position = value; } } + public Optional Position { get; set; } } } diff --git a/src/Discord.Net/API/Rest/ModifyGuildChannelsParams.cs b/src/Discord.Net.Core/API/Rest/ModifyGuildChannelsParams.cs similarity index 55% rename from src/Discord.Net/API/Rest/ModifyGuildChannelsParams.cs rename to src/Discord.Net.Core/API/Rest/ModifyGuildChannelsParams.cs index 99679b924..8ac3299fa 100644 --- a/src/Discord.Net/API/Rest/ModifyGuildChannelsParams.cs +++ b/src/Discord.Net.Core/API/Rest/ModifyGuildChannelsParams.cs @@ -7,9 +7,14 @@ namespace Discord.API.Rest public class ModifyGuildChannelsParams { [JsonProperty("id")] - public ulong Id { internal get; set; } - + public ulong Id { get; set; } [JsonProperty("position")] - public int Position { internal get; set; } + public int Position { get; set; } + + public ModifyGuildChannelsParams(ulong id, int position) + { + Id = id; + Position = position; + } } } diff --git a/src/Discord.Net.Core/API/Rest/ModifyGuildEmbedParams.cs b/src/Discord.Net.Core/API/Rest/ModifyGuildEmbedParams.cs new file mode 100644 index 000000000..f362f8cd7 --- /dev/null +++ b/src/Discord.Net.Core/API/Rest/ModifyGuildEmbedParams.cs @@ -0,0 +1,14 @@ +#pragma warning disable CS1591 +using Newtonsoft.Json; + +namespace Discord.API.Rest +{ + [JsonObject(MemberSerialization = MemberSerialization.OptIn)] + public class ModifyGuildEmbedParams + { + [JsonProperty("enabled")] + public Optional Enabled { get; set; } + [JsonProperty("channel")] + public Optional ChannelId { get; set; } + } +} diff --git a/src/Discord.Net.Core/API/Rest/ModifyGuildIntegrationParams.cs b/src/Discord.Net.Core/API/Rest/ModifyGuildIntegrationParams.cs new file mode 100644 index 000000000..3a5526c96 --- /dev/null +++ b/src/Discord.Net.Core/API/Rest/ModifyGuildIntegrationParams.cs @@ -0,0 +1,16 @@ +#pragma warning disable CS1591 +using Newtonsoft.Json; + +namespace Discord.API.Rest +{ + [JsonObject(MemberSerialization = MemberSerialization.OptIn)] + public class ModifyGuildIntegrationParams + { + [JsonProperty("expire_behavior")] + public Optional ExpireBehavior { get; set; } + [JsonProperty("expire_grace_period")] + public Optional ExpireGracePeriod { get; set; } + [JsonProperty("enable_emoticons")] + public Optional EnableEmoticons { get; set; } + } +} diff --git a/src/Discord.Net.Core/API/Rest/ModifyGuildMemberParams.cs b/src/Discord.Net.Core/API/Rest/ModifyGuildMemberParams.cs new file mode 100644 index 000000000..17a8e2da1 --- /dev/null +++ b/src/Discord.Net.Core/API/Rest/ModifyGuildMemberParams.cs @@ -0,0 +1,20 @@ +#pragma warning disable CS1591 +using Newtonsoft.Json; + +namespace Discord.API.Rest +{ + [JsonObject(MemberSerialization = MemberSerialization.OptIn)] + public class ModifyGuildMemberParams + { + [JsonProperty("mute")] + public Optional Mute { get; set; } + [JsonProperty("deaf")] + public Optional Deaf { get; set; } + [JsonProperty("nick")] + public Optional Nickname { get; set; } + [JsonProperty("roles")] + public Optional RoleIds { get; set; } + [JsonProperty("channel_id")] + public Optional ChannelId { get; set; } + } +} diff --git a/src/Discord.Net.Core/API/Rest/ModifyGuildParams.cs b/src/Discord.Net.Core/API/Rest/ModifyGuildParams.cs new file mode 100644 index 000000000..f72ff2c96 --- /dev/null +++ b/src/Discord.Net.Core/API/Rest/ModifyGuildParams.cs @@ -0,0 +1,30 @@ +#pragma warning disable CS1591 +using Newtonsoft.Json; + +namespace Discord.API.Rest +{ + [JsonObject(MemberSerialization = MemberSerialization.OptIn)] + public class ModifyGuildParams + { + [JsonProperty("username")] + public Optional Username { get; set; } + [JsonProperty("name")] + public Optional Name { get; set; } + [JsonProperty("region")] + public Optional RegionId { get; set; } + [JsonProperty("verification_level")] + public Optional VerificationLevel { get; set; } + [JsonProperty("default_message_notifications")] + public Optional DefaultMessageNotifications { get; set; } + [JsonProperty("afk_timeout")] + public Optional AfkTimeout { get; set; } + [JsonProperty("icon")] + public Optional Icon { get; set; } + [JsonProperty("splash")] + public Optional Splash { get; set; } + [JsonProperty("afk_channel_id")] + public Optional AfkChannelId { get; set; } + [JsonProperty("owner_id")] + public Optional OwnerId { get; set; } + } +} diff --git a/src/Discord.Net.Core/API/Rest/ModifyGuildRoleParams.cs b/src/Discord.Net.Core/API/Rest/ModifyGuildRoleParams.cs new file mode 100644 index 000000000..d1226b534 --- /dev/null +++ b/src/Discord.Net.Core/API/Rest/ModifyGuildRoleParams.cs @@ -0,0 +1,20 @@ +#pragma warning disable CS1591 +using Newtonsoft.Json; + +namespace Discord.API.Rest +{ + [JsonObject(MemberSerialization = MemberSerialization.OptIn)] + public class ModifyGuildRoleParams + { + [JsonProperty("name")] + public Optional Name { get; set; } + [JsonProperty("permissions")] + public Optional Permissions { get; set; } + [JsonProperty("position")] + public Optional Position { get; set; } + [JsonProperty("color")] + public Optional Color { get; set; } + [JsonProperty("hoist")] + public Optional Hoist { get; set; } + } +} diff --git a/src/Discord.Net/API/Rest/ModifyGuildRolesParams.cs b/src/Discord.Net.Core/API/Rest/ModifyGuildRolesParams.cs similarity index 67% rename from src/Discord.Net/API/Rest/ModifyGuildRolesParams.cs rename to src/Discord.Net.Core/API/Rest/ModifyGuildRolesParams.cs index 69cd413e4..2350a8c47 100644 --- a/src/Discord.Net/API/Rest/ModifyGuildRolesParams.cs +++ b/src/Discord.Net.Core/API/Rest/ModifyGuildRolesParams.cs @@ -7,6 +7,11 @@ namespace Discord.API.Rest public class ModifyGuildRolesParams : ModifyGuildRoleParams { [JsonProperty("id")] - public ulong Id { internal get; set; } + public ulong Id { get; } + + public ModifyGuildRolesParams(ulong id) + { + Id = id; + } } } diff --git a/src/Discord.Net/API/Rest/ModifyMessageParams.cs b/src/Discord.Net.Core/API/Rest/ModifyMessageParams.cs similarity index 67% rename from src/Discord.Net/API/Rest/ModifyMessageParams.cs rename to src/Discord.Net.Core/API/Rest/ModifyMessageParams.cs index aca058ff4..4901ddc9d 100644 --- a/src/Discord.Net/API/Rest/ModifyMessageParams.cs +++ b/src/Discord.Net.Core/API/Rest/ModifyMessageParams.cs @@ -7,7 +7,6 @@ namespace Discord.API.Rest public class ModifyMessageParams { [JsonProperty("content")] - internal Optional _content { get; set; } - public string Content { set { _content = value; } } + public Optional Content { get; set; } } } diff --git a/src/Discord.Net.Core/API/Rest/ModifyPresenceParams.cs b/src/Discord.Net.Core/API/Rest/ModifyPresenceParams.cs new file mode 100644 index 000000000..52145643a --- /dev/null +++ b/src/Discord.Net.Core/API/Rest/ModifyPresenceParams.cs @@ -0,0 +1,9 @@ +#pragma warning disable CS1591 +namespace Discord.API.Rest +{ + public class ModifyPresenceParams + { + public Optional Status { get; set; } + public Optional Game { get; set; } + } +} diff --git a/src/Discord.Net/API/Rest/ModifyTextChannelParams.cs b/src/Discord.Net.Core/API/Rest/ModifyTextChannelParams.cs similarity index 70% rename from src/Discord.Net/API/Rest/ModifyTextChannelParams.cs rename to src/Discord.Net.Core/API/Rest/ModifyTextChannelParams.cs index 783771212..3546cee95 100644 --- a/src/Discord.Net/API/Rest/ModifyTextChannelParams.cs +++ b/src/Discord.Net.Core/API/Rest/ModifyTextChannelParams.cs @@ -7,7 +7,6 @@ namespace Discord.API.Rest public class ModifyTextChannelParams : ModifyGuildChannelParams { [JsonProperty("topic")] - internal Optional _topic { get; set; } - public string Topic { set { _topic = value; } } + public Optional Topic { get; set; } } } diff --git a/src/Discord.Net/API/Rest/ModifyVoiceChannelParams.cs b/src/Discord.Net.Core/API/Rest/ModifyVoiceChannelParams.cs similarity index 57% rename from src/Discord.Net/API/Rest/ModifyVoiceChannelParams.cs rename to src/Discord.Net.Core/API/Rest/ModifyVoiceChannelParams.cs index 5732feceb..8b5af9d8e 100644 --- a/src/Discord.Net/API/Rest/ModifyVoiceChannelParams.cs +++ b/src/Discord.Net.Core/API/Rest/ModifyVoiceChannelParams.cs @@ -7,11 +7,8 @@ namespace Discord.API.Rest public class ModifyVoiceChannelParams : ModifyGuildChannelParams { [JsonProperty("bitrate")] - internal Optional _bitrate { get; set; } - public int Bitrate { set { _bitrate = value; } } - + public Optional Bitrate { get; set; } [JsonProperty("user_limit")] - internal Optional _userLimit { get; set; } - public int UserLimit { set { _userLimit = value; } } + public Optional UserLimit { get; set; } } } diff --git a/src/Discord.Net.Core/API/Rest/UploadFileParams.cs b/src/Discord.Net.Core/API/Rest/UploadFileParams.cs new file mode 100644 index 000000000..bbd798900 --- /dev/null +++ b/src/Discord.Net.Core/API/Rest/UploadFileParams.cs @@ -0,0 +1,35 @@ +#pragma warning disable CS1591 +using Discord.Net.Rest; +using System.Collections.Generic; +using System.IO; + +namespace Discord.API.Rest +{ + public class UploadFileParams + { + public Stream File { get; } + + public Optional Filename { get; set; } + public Optional Content { get; set; } + public Optional Nonce { get; set; } + public Optional IsTTS { get; set; } + + public UploadFileParams(Stream file) + { + File = file; + } + + public IReadOnlyDictionary ToDictionary() + { + var d = new Dictionary(); + d["file"] = new MultipartFile(File, Filename.GetValueOrDefault("unknown.dat")); + if (Content.IsSpecified) + d["content"] = Content.Value; + if (IsTTS.IsSpecified) + d["tts"] = IsTTS.Value.ToString(); + if (Nonce.IsSpecified) + d["nonce"] = Nonce.Value; + return d; + } + } +} diff --git a/src/Discord.Net/API/WebSocketMessage.cs b/src/Discord.Net.Core/API/WebSocketMessage.cs similarity index 100% rename from src/Discord.Net/API/WebSocketMessage.cs rename to src/Discord.Net.Core/API/WebSocketMessage.cs diff --git a/src/Discord.Net/Audio/IAudioClient.cs b/src/Discord.Net.Core/Audio/IAudioClient.cs similarity index 65% rename from src/Discord.Net/Audio/IAudioClient.cs rename to src/Discord.Net.Core/Audio/IAudioClient.cs index 312152142..3cfdfa856 100644 --- a/src/Discord.Net/Audio/IAudioClient.cs +++ b/src/Discord.Net.Core/Audio/IAudioClient.cs @@ -1,4 +1,5 @@ using System; +using System.IO; using System.Threading.Tasks; namespace Discord.Audio @@ -8,8 +9,7 @@ namespace Discord.Audio event Func Connected; event Func Disconnected; event Func LatencyUpdated; - - DiscordVoiceAPIClient ApiClient { get; } + /// Gets the current connection state of this client. ConnectionState ConnectionState { get; } /// Gets the estimated round-trip latency, in milliseconds, to the gateway server. @@ -17,7 +17,7 @@ namespace Discord.Audio Task DisconnectAsync(); - RTPWriteStream CreateOpusStream(int samplesPerFrame, int bufferSize = 4000); - OpusEncodeStream CreatePCMStream(int samplesPerFrame, int? bitrate = null, OpusApplication application = OpusApplication.MusicOrMixed, int bufferSize = 4000); + Stream CreateOpusStream(int samplesPerFrame, int bufferSize = 4000); + Stream CreatePCMStream(int samplesPerFrame, int? bitrate = null, OpusApplication application = OpusApplication.MusicOrMixed, int bufferSize = 4000); } } diff --git a/src/Discord.Net/Audio/Opus/OpusApplication.cs b/src/Discord.Net.Core/Audio/Opus/OpusApplication.cs similarity index 100% rename from src/Discord.Net/Audio/Opus/OpusApplication.cs rename to src/Discord.Net.Core/Audio/Opus/OpusApplication.cs diff --git a/src/Discord.Net/ConnectionState.cs b/src/Discord.Net.Core/ConnectionState.cs similarity index 100% rename from src/Discord.Net/ConnectionState.cs rename to src/Discord.Net.Core/ConnectionState.cs diff --git a/src/Discord.Net.Core/Discord.Net.Core.xproj b/src/Discord.Net.Core/Discord.Net.Core.xproj new file mode 100644 index 000000000..6759e09b4 --- /dev/null +++ b/src/Discord.Net.Core/Discord.Net.Core.xproj @@ -0,0 +1,19 @@ + + + + 14.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + 91e9e7bd-75c9-4e98-84aa-2c271922e5c2 + Discord + .\obj + .\bin\ + v4.5.2 + + + 2.0 + + + \ No newline at end of file diff --git a/src/Discord.Net/DiscordConfig.cs b/src/Discord.Net.Core/DiscordConfig.cs similarity index 100% rename from src/Discord.Net/DiscordConfig.cs rename to src/Discord.Net.Core/DiscordConfig.cs diff --git a/src/Discord.Net/Entities/Channels/ChannelType.cs b/src/Discord.Net.Core/Entities/Channels/ChannelType.cs similarity index 100% rename from src/Discord.Net/Entities/Channels/ChannelType.cs rename to src/Discord.Net.Core/Entities/Channels/ChannelType.cs diff --git a/src/Discord.Net/Entities/Channels/IChannel.cs b/src/Discord.Net.Core/Entities/Channels/IChannel.cs similarity index 57% rename from src/Discord.Net/Entities/Channels/IChannel.cs rename to src/Discord.Net.Core/Entities/Channels/IChannel.cs index 53c681b56..e3990204c 100644 --- a/src/Discord.Net/Entities/Channels/IChannel.cs +++ b/src/Discord.Net.Core/Entities/Channels/IChannel.cs @@ -3,11 +3,15 @@ using System.Threading.Tasks; namespace Discord { - public interface IChannel : ISnowflakeEntity, IUpdateable + public interface IChannel : ISnowflakeEntity { + IReadOnlyCollection CachedUsers { get; } + /// Gets a collection of all users in this channel. - Task> GetUsersAsync(); + IAsyncEnumerable> GetUsersAsync(); + /// Gets a user in this channel with the provided id. Task GetUserAsync(ulong id); + IUser GetCachedUser(ulong id); } } diff --git a/src/Discord.Net/Entities/Channels/IDMChannel.cs b/src/Discord.Net.Core/Entities/Channels/IDMChannel.cs similarity index 100% rename from src/Discord.Net/Entities/Channels/IDMChannel.cs rename to src/Discord.Net.Core/Entities/Channels/IDMChannel.cs diff --git a/src/Discord.Net.Core/Entities/Channels/IGroupChannel.cs b/src/Discord.Net.Core/Entities/Channels/IGroupChannel.cs new file mode 100644 index 000000000..dafc997a7 --- /dev/null +++ b/src/Discord.Net.Core/Entities/Channels/IGroupChannel.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Discord +{ + public interface IGroupChannel : IMessageChannel, IPrivateChannel + { + ///// Adds a user to this group. + //Task AddUserAsync(IUser user); + + //new IReadOnlyCollection CachedUsers { get; } + + /// Leaves this group. + Task LeaveAsync(); + } +} \ No newline at end of file diff --git a/src/Discord.Net/Entities/Channels/IGuildChannel.cs b/src/Discord.Net.Core/Entities/Channels/IGuildChannel.cs similarity index 89% rename from src/Discord.Net/Entities/Channels/IGuildChannel.cs rename to src/Discord.Net.Core/Entities/Channels/IGuildChannel.cs index e42ace8d6..0927c2b79 100644 --- a/src/Discord.Net/Entities/Channels/IGuildChannel.cs +++ b/src/Discord.Net.Core/Entities/Channels/IGuildChannel.cs @@ -12,8 +12,9 @@ namespace Discord /// Gets the position of this channel in the guild's channel list, relative to others of the same type. int Position { get; } - /// Gets the guild this channel is a member of. - IGuild Guild { get; } + /// Gets the id of the guild this channel is a member of. + ulong GuildId { get; } + new IReadOnlyCollection CachedUsers { get; } /// Creates a new invite to this channel. /// The time (in seconds) until the invite expires. Set to null to never expire. @@ -43,8 +44,9 @@ namespace Discord Task AddPermissionOverwriteAsync(IUser user, OverwritePermissions permissions); /// Gets a collection of all users in this channel. - new Task> GetUsersAsync(); + new IAsyncEnumerable> GetUsersAsync(); /// Gets a user in this channel with the provided id. new Task GetUserAsync(ulong id); + new IGuildUser GetCachedUser(ulong id); } } \ No newline at end of file diff --git a/src/Discord.Net/Entities/Channels/IMessageChannel.cs b/src/Discord.Net.Core/Entities/Channels/IMessageChannel.cs similarity index 82% rename from src/Discord.Net/Entities/Channels/IMessageChannel.cs rename to src/Discord.Net.Core/Entities/Channels/IMessageChannel.cs index a2288b0ee..e8089d2af 100644 --- a/src/Discord.Net/Entities/Channels/IMessageChannel.cs +++ b/src/Discord.Net.Core/Entities/Channels/IMessageChannel.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.IO; using System.Threading.Tasks; @@ -15,20 +16,21 @@ namespace Discord Task SendFileAsync(string filePath, string text = null, bool isTTS = false); /// Sends a file to this text channel, with an optional caption. Task SendFileAsync(Stream stream, string filename, string text = null, bool isTTS = false); + /// Gets a message from this message channel with the given id, or null if not found. Task GetMessageAsync(ulong id); /// Gets the message from this channel's cache with the given id, or null if not found. IMessage GetCachedMessage(ulong id); /// Gets the last N messages from this message channel. - Task> GetMessagesAsync(int limit = DiscordConfig.MaxMessagesPerBatch); + IAsyncEnumerable> GetMessagesAsync(int limit = DiscordConfig.MaxMessagesPerBatch); /// Gets a collection of messages in this channel. - Task> GetMessagesAsync(ulong fromMessageId, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch); + IAsyncEnumerable> GetMessagesAsync(ulong fromMessageId, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch); /// Gets a collection of pinned messages in this channel. Task> GetPinnedMessagesAsync(); /// Bulk deletes multiple messages. Task DeleteMessagesAsync(IEnumerable messages); /// Broadcasts the "user is typing" message to all users in this channel, lasting 10 seconds. - Task TriggerTypingAsync(); + IDisposable EnterTypingState(); } } diff --git a/src/Discord.Net/Entities/Channels/IPrivateChannel.cs b/src/Discord.Net.Core/Entities/Channels/IPrivateChannel.cs similarity index 100% rename from src/Discord.Net/Entities/Channels/IPrivateChannel.cs rename to src/Discord.Net.Core/Entities/Channels/IPrivateChannel.cs diff --git a/src/Discord.Net/Entities/Channels/ITextChannel.cs b/src/Discord.Net.Core/Entities/Channels/ITextChannel.cs similarity index 100% rename from src/Discord.Net/Entities/Channels/ITextChannel.cs rename to src/Discord.Net.Core/Entities/Channels/ITextChannel.cs diff --git a/src/Discord.Net/Entities/Channels/IVoiceChannel.cs b/src/Discord.Net.Core/Entities/Channels/IVoiceChannel.cs similarity index 100% rename from src/Discord.Net/Entities/Channels/IVoiceChannel.cs rename to src/Discord.Net.Core/Entities/Channels/IVoiceChannel.cs diff --git a/src/Discord.Net/Entities/Guilds/DefaultMessageNotifications.cs b/src/Discord.Net.Core/Entities/Guilds/DefaultMessageNotifications.cs similarity index 100% rename from src/Discord.Net/Entities/Guilds/DefaultMessageNotifications.cs rename to src/Discord.Net.Core/Entities/Guilds/DefaultMessageNotifications.cs diff --git a/src/Discord.Net.Core/Entities/Guilds/Emoji.cs b/src/Discord.Net.Core/Entities/Guilds/Emoji.cs new file mode 100644 index 000000000..fc8b64fa4 --- /dev/null +++ b/src/Discord.Net.Core/Entities/Guilds/Emoji.cs @@ -0,0 +1,33 @@ +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics; +using Model = Discord.API.Emoji; + +namespace Discord +{ + [DebuggerDisplay(@"{DebuggerDisplay,nq}")] + public struct Emoji + { + public ulong Id { get; } + public string Name { get; } + public bool IsManaged { get; } + public bool RequireColons { get; } + public IReadOnlyList RoleIds { get; } + + public Emoji(ulong id, string name, bool isManaged, bool requireColons, IReadOnlyList roleIds) + { + Id = id; + Name = name; + IsManaged = isManaged; + RequireColons = requireColons; + RoleIds = roleIds; + } + public static Emoji Create(Model model) + { + return new Emoji(model.Id, model.Name, model.Managed, model.RequireColons, ImmutableArray.Create(model.Roles)); + } + + public override string ToString() => Name; + private string DebuggerDisplay => $"{Name} ({Id})"; + } +} diff --git a/src/Discord.Net.Core/Entities/Guilds/IBan.cs b/src/Discord.Net.Core/Entities/Guilds/IBan.cs new file mode 100644 index 000000000..aee2cc198 --- /dev/null +++ b/src/Discord.Net.Core/Entities/Guilds/IBan.cs @@ -0,0 +1,13 @@ +//using Discord.Rest; +using System.Diagnostics; +using Model = Discord.API.Ban; + +namespace Discord +{ + + public interface IBan + { + IUser User { get; } + string Reason { get; } + } +} diff --git a/src/Discord.Net/Entities/Guilds/IGuild.cs b/src/Discord.Net.Core/Entities/Guilds/IGuild.cs similarity index 89% rename from src/Discord.Net/Entities/Guilds/IGuild.cs rename to src/Discord.Net.Core/Entities/Guilds/IGuild.cs index b1c010439..489f9e8f5 100644 --- a/src/Discord.Net/Entities/Guilds/IGuild.cs +++ b/src/Discord.Net.Core/Entities/Guilds/IGuild.cs @@ -6,7 +6,7 @@ using Discord.Audio; namespace Discord { - public interface IGuild : IDeletable, ISnowflakeEntity, IUpdateable + public interface IGuild : IDeletable, ISnowflakeEntity { /// Gets the name of this guild. string Name { get; } @@ -20,8 +20,12 @@ namespace Discord MfaLevel MfaLevel { get; } /// Gets the level of requirements a user must fulfill before being allowed to post messages in this guild. VerificationLevel VerificationLevel { get; } + /// Returns the id of this guild's icon, or null if one is not set. + string IconId { get; } /// Returns the url to this guild's icon, or null if one is not set. string IconUrl { get; } + /// Returns the id of this guild's splash image, or null if one is not set. + string SplashId { get; } /// Returns the url to this guild's splash image, or null if one is not set. string SplashUrl { get; } /// Returns true if this guild is currently connected and ready to be used. Only applies to the WebSocket client. @@ -48,6 +52,7 @@ namespace Discord IReadOnlyCollection Features { get; } /// Gets a collection of all roles in this guild. IReadOnlyCollection Roles { get; } + IReadOnlyCollection CachedUsers { get; } /// Modifies this guild. Task ModifyAsync(Action func); @@ -61,7 +66,7 @@ namespace Discord Task LeaveAsync(); /// Gets a collection of all users banned on this guild. - Task> GetBansAsync(); + Task> GetBansAsync(); /// Bans the provided user from this guild and optionally prunes their recent messages. Task AddBanAsync(IUser user, int pruneDays = 0); /// Bans the provided user id from this guild and optionally prunes their recent messages. @@ -75,11 +80,15 @@ namespace Discord Task> GetChannelsAsync(); /// Gets the channel in this guild with the provided id, or null if not found. Task GetChannelAsync(ulong id); + IGuildChannel GetCachedChannel(ulong id); /// Creates a new text channel. Task CreateTextChannelAsync(string name); /// Creates a new voice channel. Task CreateVoiceChannelAsync(string name); + Task> GetIntegrationsAsync(); + Task CreateIntegrationAsync(ulong id, string type); + /// Gets a collection of all invites to this guild. Task> GetInvitesAsync(); @@ -92,9 +101,10 @@ namespace Discord Task> GetUsersAsync(); /// Gets the user in this guild with the provided id, or null if not found. Task GetUserAsync(ulong id); + IGuildUser GetCachedUser(ulong id); /// Gets the current user for this guild. Task GetCurrentUserAsync(); - /// Downloads all users for this guild if the current list is incomplete. Only applies to the WebSocket client. + /// Downloads all users for this guild if the current list is incomplete. Task DownloadUsersAsync(); /// Removes all users from this guild if they have not logged on in a provided number of days or, if simulate is true, returns the number of users that would be removed. Task PruneUsersAsync(int days = 30, bool simulate = false); diff --git a/src/Discord.Net/Entities/Guilds/IGuildIntegration.cs b/src/Discord.Net.Core/Entities/Guilds/IGuildIntegration.cs similarity index 83% rename from src/Discord.Net/Entities/Guilds/IGuildIntegration.cs rename to src/Discord.Net.Core/Entities/Guilds/IGuildIntegration.cs index 7f6ed6408..1a0c6d2d0 100644 --- a/src/Discord.Net/Entities/Guilds/IGuildIntegration.cs +++ b/src/Discord.Net.Core/Entities/Guilds/IGuildIntegration.cs @@ -2,7 +2,6 @@ namespace Discord { - //TODO: Add docstrings public interface IGuildIntegration { ulong Id { get; } @@ -15,8 +14,8 @@ namespace Discord DateTimeOffset SyncedAt { get; } IntegrationAccount Account { get; } - IGuild Guild { get; } + ulong GuildId { get; } + ulong RoleId { get; } IUser User { get; } - IRole Role { get; } } } diff --git a/src/Discord.Net/Entities/Guilds/IUserGuild.cs b/src/Discord.Net.Core/Entities/Guilds/IUserGuild.cs similarity index 100% rename from src/Discord.Net/Entities/Guilds/IUserGuild.cs rename to src/Discord.Net.Core/Entities/Guilds/IUserGuild.cs diff --git a/src/Discord.Net/Entities/Guilds/IVoiceRegion.cs b/src/Discord.Net.Core/Entities/Guilds/IVoiceRegion.cs similarity index 100% rename from src/Discord.Net/Entities/Guilds/IVoiceRegion.cs rename to src/Discord.Net.Core/Entities/Guilds/IVoiceRegion.cs diff --git a/src/Discord.Net/Entities/Guilds/IntegrationAccount.cs b/src/Discord.Net.Core/Entities/Guilds/IntegrationAccount.cs similarity index 100% rename from src/Discord.Net/Entities/Guilds/IntegrationAccount.cs rename to src/Discord.Net.Core/Entities/Guilds/IntegrationAccount.cs diff --git a/src/Discord.Net/Entities/Guilds/MfaLevel.cs b/src/Discord.Net.Core/Entities/Guilds/MfaLevel.cs similarity index 100% rename from src/Discord.Net/Entities/Guilds/MfaLevel.cs rename to src/Discord.Net.Core/Entities/Guilds/MfaLevel.cs diff --git a/src/Discord.Net/Entities/Guilds/VerificationLevel.cs b/src/Discord.Net.Core/Entities/Guilds/VerificationLevel.cs similarity index 100% rename from src/Discord.Net/Entities/Guilds/VerificationLevel.cs rename to src/Discord.Net.Core/Entities/Guilds/VerificationLevel.cs diff --git a/src/Discord.Net/Entities/IApplication.cs b/src/Discord.Net.Core/Entities/IApplication.cs similarity index 77% rename from src/Discord.Net/Entities/IApplication.cs rename to src/Discord.Net.Core/Entities/IApplication.cs index 157205226..4fb1e4b91 100644 --- a/src/Discord.Net/Entities/IApplication.cs +++ b/src/Discord.Net.Core/Entities/IApplication.cs @@ -1,6 +1,6 @@ namespace Discord { - public interface IApplication : ISnowflakeEntity, IUpdateable + public interface IApplication : ISnowflakeEntity { string Name { get; } string Description { get; } diff --git a/src/Discord.Net/Entities/IDeletable.cs b/src/Discord.Net.Core/Entities/IDeletable.cs similarity index 100% rename from src/Discord.Net/Entities/IDeletable.cs rename to src/Discord.Net.Core/Entities/IDeletable.cs diff --git a/src/Discord.Net.Core/Entities/IEntity.cs b/src/Discord.Net.Core/Entities/IEntity.cs new file mode 100644 index 000000000..b1bb922c9 --- /dev/null +++ b/src/Discord.Net.Core/Entities/IEntity.cs @@ -0,0 +1,15 @@ +using System; + +namespace Discord +{ + public interface IEntity + where TId : IEquatable + { + /// Gets the IDiscordClient that created this object. + IDiscordClient Discord { get; } + + /// Gets the unique identifier for this object. + TId Id { get; } + + } +} diff --git a/src/Discord.Net/Entities/IMentionable.cs b/src/Discord.Net.Core/Entities/IMentionable.cs similarity index 100% rename from src/Discord.Net/Entities/IMentionable.cs rename to src/Discord.Net.Core/Entities/IMentionable.cs diff --git a/src/Discord.Net.Core/Entities/ISnowflakeEntity.cs b/src/Discord.Net.Core/Entities/ISnowflakeEntity.cs new file mode 100644 index 000000000..6eb947bf8 --- /dev/null +++ b/src/Discord.Net.Core/Entities/ISnowflakeEntity.cs @@ -0,0 +1,6 @@ +namespace Discord +{ + public interface ISnowflakeEntity : IEntity + { + } +} diff --git a/src/Discord.Net/Entities/IUpdateable.cs b/src/Discord.Net.Core/Entities/IUpdateable.cs similarity index 100% rename from src/Discord.Net/Entities/IUpdateable.cs rename to src/Discord.Net.Core/Entities/IUpdateable.cs diff --git a/src/Discord.Net/Entities/Invites/IInvite.cs b/src/Discord.Net.Core/Entities/Invites/IInvite.cs similarity index 100% rename from src/Discord.Net/Entities/Invites/IInvite.cs rename to src/Discord.Net.Core/Entities/Invites/IInvite.cs diff --git a/src/Discord.Net/Entities/Invites/IInviteMetadata.cs b/src/Discord.Net.Core/Entities/Invites/IInviteMetadata.cs similarity index 100% rename from src/Discord.Net/Entities/Invites/IInviteMetadata.cs rename to src/Discord.Net.Core/Entities/Invites/IInviteMetadata.cs diff --git a/src/Discord.Net/Entities/Messages/Direction.cs b/src/Discord.Net.Core/Entities/Messages/Direction.cs similarity index 100% rename from src/Discord.Net/Entities/Messages/Direction.cs rename to src/Discord.Net.Core/Entities/Messages/Direction.cs diff --git a/src/Discord.Net/Entities/Messages/EmbedProvider.cs b/src/Discord.Net.Core/Entities/Messages/EmbedProvider.cs similarity index 69% rename from src/Discord.Net/Entities/Messages/EmbedProvider.cs rename to src/Discord.Net.Core/Entities/Messages/EmbedProvider.cs index 1f1ef6d2d..4d4aa1ea8 100644 --- a/src/Discord.Net/Entities/Messages/EmbedProvider.cs +++ b/src/Discord.Net.Core/Entities/Messages/EmbedProvider.cs @@ -12,7 +12,9 @@ namespace Discord Name = name; Url = url; } - internal EmbedProvider(Model model) - : this(model.Name, model.Url) { } + public static EmbedProvider Create(Model model) + { + return new EmbedProvider(model.Name, model.Url); + } } } diff --git a/src/Discord.Net/Entities/Messages/EmbedThumbnail.cs b/src/Discord.Net.Core/Entities/Messages/EmbedThumbnail.cs similarity index 81% rename from src/Discord.Net/Entities/Messages/EmbedThumbnail.cs rename to src/Discord.Net.Core/Entities/Messages/EmbedThumbnail.cs index 736d7d743..8e1a2a527 100644 --- a/src/Discord.Net/Entities/Messages/EmbedThumbnail.cs +++ b/src/Discord.Net.Core/Entities/Messages/EmbedThumbnail.cs @@ -16,14 +16,11 @@ namespace Discord Height = height; Width = width; } - - internal EmbedThumbnail(Model model) - : this( - model.Url, - model.ProxyUrl, - model.Height.IsSpecified ? model.Height.Value : (int?)null, - model.Width.IsSpecified ? model.Width.Value : (int?)null) + public static EmbedThumbnail Create(Model model) { + return new EmbedThumbnail(model.Url, model.ProxyUrl, + model.Height.IsSpecified ? model.Height.Value : (int?)null, + model.Width.IsSpecified ? model.Width.Value : (int?)null); } } } diff --git a/src/Discord.Net/Entities/Messages/IAttachment.cs b/src/Discord.Net.Core/Entities/Messages/IAttachment.cs similarity index 100% rename from src/Discord.Net/Entities/Messages/IAttachment.cs rename to src/Discord.Net.Core/Entities/Messages/IAttachment.cs diff --git a/src/Discord.Net/Entities/Messages/IEmbed.cs b/src/Discord.Net.Core/Entities/Messages/IEmbed.cs similarity index 69% rename from src/Discord.Net/Entities/Messages/IEmbed.cs rename to src/Discord.Net.Core/Entities/Messages/IEmbed.cs index e0080f320..3bca85fd0 100644 --- a/src/Discord.Net/Entities/Messages/IEmbed.cs +++ b/src/Discord.Net.Core/Entities/Messages/IEmbed.cs @@ -6,7 +6,7 @@ string Type { get; } string Title { get; } string Description { get; } - EmbedProvider Provider { get; } - EmbedThumbnail Thumbnail { get; } + EmbedProvider? Provider { get; } + EmbedThumbnail? Thumbnail { get; } } } diff --git a/src/Discord.Net/Entities/Messages/IMessage.cs b/src/Discord.Net.Core/Entities/Messages/IMessage.cs similarity index 85% rename from src/Discord.Net/Entities/Messages/IMessage.cs rename to src/Discord.Net.Core/Entities/Messages/IMessage.cs index 0c83b1024..be83e7482 100644 --- a/src/Discord.Net/Entities/Messages/IMessage.cs +++ b/src/Discord.Net.Core/Entities/Messages/IMessage.cs @@ -3,8 +3,10 @@ using System.Collections.Generic; namespace Discord { - public interface IMessage : ISnowflakeEntity, IUpdateable + public interface IMessage : ISnowflakeEntity { + /// Gets the type of this system message. + MessageType Type { get; } /// Returns true if this message was sent as a text-to-speech message. bool IsTTS { get; } /// Returns true if this message was added to its channel's pinned messages. @@ -16,8 +18,8 @@ namespace Discord /// Gets the time of this message's last edit, if any. DateTimeOffset? EditedTimestamp { get; } - /// Gets the channel this message was sent to. - IMessageChannel Channel { get; } + /// Gets the id of the channel this message was sent to. + ulong ChannelId { get; } /// Gets the author of this message. IUser Author { get; } diff --git a/src/Discord.Net.Core/Entities/Messages/ISystemMessage.cs b/src/Discord.Net.Core/Entities/Messages/ISystemMessage.cs new file mode 100644 index 000000000..2dfaf8f2d --- /dev/null +++ b/src/Discord.Net.Core/Entities/Messages/ISystemMessage.cs @@ -0,0 +1,6 @@ +namespace Discord +{ + public interface ISystemMessage : IMessage + { + } +} diff --git a/src/Discord.Net/Entities/Messages/IUserMessage.cs b/src/Discord.Net.Core/Entities/Messages/IUserMessage.cs similarity index 100% rename from src/Discord.Net/Entities/Messages/IUserMessage.cs rename to src/Discord.Net.Core/Entities/Messages/IUserMessage.cs diff --git a/src/Discord.Net/Entities/Messages/ChannelMentionHandling.cs b/src/Discord.Net.Core/Entities/Messages/Mentions/ChannelMentionHandling.cs similarity index 100% rename from src/Discord.Net/Entities/Messages/ChannelMentionHandling.cs rename to src/Discord.Net.Core/Entities/Messages/Mentions/ChannelMentionHandling.cs diff --git a/src/Discord.Net/Entities/Messages/EveryoneMentionHandling.cs b/src/Discord.Net.Core/Entities/Messages/Mentions/EveryoneMentionHandling.cs similarity index 100% rename from src/Discord.Net/Entities/Messages/EveryoneMentionHandling.cs rename to src/Discord.Net.Core/Entities/Messages/Mentions/EveryoneMentionHandling.cs diff --git a/src/Discord.Net/Entities/Messages/RoleMentionHandling.cs b/src/Discord.Net.Core/Entities/Messages/Mentions/RoleMentionHandling.cs similarity index 100% rename from src/Discord.Net/Entities/Messages/RoleMentionHandling.cs rename to src/Discord.Net.Core/Entities/Messages/Mentions/RoleMentionHandling.cs diff --git a/src/Discord.Net/Entities/Messages/UserMentionHandling.cs b/src/Discord.Net.Core/Entities/Messages/Mentions/UserMentionHandling.cs similarity index 100% rename from src/Discord.Net/Entities/Messages/UserMentionHandling.cs rename to src/Discord.Net.Core/Entities/Messages/Mentions/UserMentionHandling.cs diff --git a/src/Discord.Net/Entities/Messages/MessageType.cs b/src/Discord.Net.Core/Entities/Messages/MessageType.cs similarity index 100% rename from src/Discord.Net/Entities/Messages/MessageType.cs rename to src/Discord.Net.Core/Entities/Messages/MessageType.cs diff --git a/src/Discord.Net/Entities/Permissions/ChannelPermission.cs b/src/Discord.Net.Core/Entities/Permissions/ChannelPermission.cs similarity index 100% rename from src/Discord.Net/Entities/Permissions/ChannelPermission.cs rename to src/Discord.Net.Core/Entities/Permissions/ChannelPermission.cs diff --git a/src/Discord.Net/Entities/Permissions/ChannelPermissions.cs b/src/Discord.Net.Core/Entities/Permissions/ChannelPermissions.cs similarity index 100% rename from src/Discord.Net/Entities/Permissions/ChannelPermissions.cs rename to src/Discord.Net.Core/Entities/Permissions/ChannelPermissions.cs diff --git a/src/Discord.Net/Entities/Permissions/GuildPermission.cs b/src/Discord.Net.Core/Entities/Permissions/GuildPermission.cs similarity index 100% rename from src/Discord.Net/Entities/Permissions/GuildPermission.cs rename to src/Discord.Net.Core/Entities/Permissions/GuildPermission.cs diff --git a/src/Discord.Net/Entities/Permissions/GuildPermissions.cs b/src/Discord.Net.Core/Entities/Permissions/GuildPermissions.cs similarity index 100% rename from src/Discord.Net/Entities/Permissions/GuildPermissions.cs rename to src/Discord.Net.Core/Entities/Permissions/GuildPermissions.cs diff --git a/src/Discord.Net/Entities/Permissions/Overwrite.cs b/src/Discord.Net.Core/Entities/Permissions/Overwrite.cs similarity index 96% rename from src/Discord.Net/Entities/Permissions/Overwrite.cs rename to src/Discord.Net.Core/Entities/Permissions/Overwrite.cs index 7333d93e1..ff5b00623 100644 --- a/src/Discord.Net/Entities/Permissions/Overwrite.cs +++ b/src/Discord.Net.Core/Entities/Permissions/Overwrite.cs @@ -19,7 +19,7 @@ namespace Discord Permissions = permissions; } - internal Overwrite(Model model) + public Overwrite(Model model) : this(model.TargetId, model.TargetType, new OverwritePermissions(model.Allow, model.Deny)) { } } } diff --git a/src/Discord.Net/Entities/Permissions/OverwritePermissions.cs b/src/Discord.Net.Core/Entities/Permissions/OverwritePermissions.cs similarity index 100% rename from src/Discord.Net/Entities/Permissions/OverwritePermissions.cs rename to src/Discord.Net.Core/Entities/Permissions/OverwritePermissions.cs diff --git a/src/Discord.Net/Entities/Permissions/PermValue.cs b/src/Discord.Net.Core/Entities/Permissions/PermValue.cs similarity index 100% rename from src/Discord.Net/Entities/Permissions/PermValue.cs rename to src/Discord.Net.Core/Entities/Permissions/PermValue.cs diff --git a/src/Discord.Net/Entities/Permissions/PermissionTarget.cs b/src/Discord.Net.Core/Entities/Permissions/PermissionTarget.cs similarity index 100% rename from src/Discord.Net/Entities/Permissions/PermissionTarget.cs rename to src/Discord.Net.Core/Entities/Permissions/PermissionTarget.cs diff --git a/src/Discord.Net/Entities/Roles/Color.cs b/src/Discord.Net.Core/Entities/Roles/Color.cs similarity index 100% rename from src/Discord.Net/Entities/Roles/Color.cs rename to src/Discord.Net.Core/Entities/Roles/Color.cs diff --git a/src/Discord.Net/Entities/Roles/IRole.cs b/src/Discord.Net.Core/Entities/Roles/IRole.cs similarity index 64% rename from src/Discord.Net/Entities/Roles/IRole.cs rename to src/Discord.Net.Core/Entities/Roles/IRole.cs index 29975be46..bcea9b17e 100644 --- a/src/Discord.Net/Entities/Roles/IRole.cs +++ b/src/Discord.Net.Core/Entities/Roles/IRole.cs @@ -1,12 +1,10 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using Discord.API.Rest; - -namespace Discord +namespace Discord { - public interface IRole : IDeletable, ISnowflakeEntity + public interface IRole : ISnowflakeEntity, IDeletable, IMentionable { + /// Gets the guild owning this role. + IGuild Guild { get; } + /// Gets the color given to users of this role. Color Color { get; } /// Returns true if users of this role are separated in the user list. @@ -20,10 +18,7 @@ namespace Discord /// Gets this role's position relative to other roles in the same guild. int Position { get; } - /// Gets the id of the guild owning this role. - ulong GuildId { get; } - - /// Modifies this role. - Task ModifyAsync(Action func); + ///// Modifies this role. + //Task ModifyAsync(Action func); } } \ No newline at end of file diff --git a/src/Discord.Net/Entities/Users/Game.cs b/src/Discord.Net.Core/Entities/Users/Game.cs similarity index 71% rename from src/Discord.Net/Entities/Users/Game.cs rename to src/Discord.Net.Core/Entities/Users/Game.cs index 9b5d891ef..2a8645df3 100644 --- a/src/Discord.Net/Entities/Users/Game.cs +++ b/src/Discord.Net.Core/Entities/Users/Game.cs @@ -4,7 +4,7 @@ using Model = Discord.API.Game; namespace Discord { [DebuggerDisplay(@"{DebuggerDisplay,nq}")] - public class Game + public struct Game { public string Name { get; } public string StreamUrl { get; } @@ -18,8 +18,12 @@ namespace Discord } public Game(string name) : this(name, null, StreamType.NotStreaming) { } - internal Game(Model model) - : this(model.Name, model.StreamUrl.GetValueOrDefault(null), model.StreamType.GetValueOrDefault(null) ?? StreamType.NotStreaming) { } + public static Game Create(Model model) + { + return new Game(model.Name, + model.StreamUrl.GetValueOrDefault(null), + model.StreamType.GetValueOrDefault(null) ?? StreamType.NotStreaming); + } public override string ToString() => Name; private string DebuggerDisplay => StreamUrl != null ? $"{Name} ({StreamUrl})" : Name; diff --git a/src/Discord.Net/Entities/Users/IConnection.cs b/src/Discord.Net.Core/Entities/Users/IConnection.cs similarity index 100% rename from src/Discord.Net/Entities/Users/IConnection.cs rename to src/Discord.Net.Core/Entities/Users/IConnection.cs diff --git a/src/Discord.Net.Core/Entities/Users/IGroupUser.cs b/src/Discord.Net.Core/Entities/Users/IGroupUser.cs new file mode 100644 index 000000000..8b4c3bf70 --- /dev/null +++ b/src/Discord.Net.Core/Entities/Users/IGroupUser.cs @@ -0,0 +1,10 @@ +using System.Threading.Tasks; + +namespace Discord +{ + public interface IGroupUser : IUser, IVoiceState + { + ///// Kicks this user from this group. + //Task KickAsync(); + } +} diff --git a/src/Discord.Net/Entities/Users/IGuildUser.cs b/src/Discord.Net.Core/Entities/Users/IGuildUser.cs similarity index 53% rename from src/Discord.Net/Entities/Users/IGuildUser.cs rename to src/Discord.Net.Core/Entities/Users/IGuildUser.cs index 3115b246a..b109e2f31 100644 --- a/src/Discord.Net/Entities/Users/IGuildUser.cs +++ b/src/Discord.Net.Core/Entities/Users/IGuildUser.cs @@ -6,19 +6,17 @@ using Discord.API.Rest; namespace Discord { /// A Guild-User pairing. - public interface IGuildUser : IUpdateable, IUser, IVoiceState + public interface IGuildUser : IUser, IVoiceState { /// Gets when this user joined this guild. DateTimeOffset? JoinedAt { get; } /// Gets the nickname for this user. string Nickname { get; } - /// Gets the guild-level permissions granted to this user by their roles. - GuildPermissions GuildPermissions { get; } - /// Gets the guild for this guild-user pair. - IGuild Guild { get; } - /// Returns a collection of the roles this user is a member of in this guild, including the guild's @everyone role. - IReadOnlyCollection Roles { get; } + /// Gets the id of the guild for this user. + ulong GuildId { get; } + /// Returns a collection of the ids of the roles this user is a member of in this guild, including the guild's @everyone role. + IReadOnlyCollection RoleIds { get; } /// Gets the level permissions granted to this user to a given channel. ChannelPermissions GetPermissions(IGuildChannel channel); @@ -27,8 +25,5 @@ namespace Discord Task KickAsync(); /// Modifies this user's properties in this guild. Task ModifyAsync(Action func); - - /// Returns a private message channel to this user, creating one if it does not already exist. - Task CreateDMChannelAsync(); } } diff --git a/src/Discord.Net/Entities/Users/IPresence.cs b/src/Discord.Net.Core/Entities/Users/IPresence.cs similarity index 89% rename from src/Discord.Net/Entities/Users/IPresence.cs rename to src/Discord.Net.Core/Entities/Users/IPresence.cs index af7be998a..7f182241b 100644 --- a/src/Discord.Net/Entities/Users/IPresence.cs +++ b/src/Discord.Net.Core/Entities/Users/IPresence.cs @@ -3,7 +3,7 @@ public interface IPresence { /// Gets the game this user is currently playing, if any. - Game Game { get; } + Game? Game { get; } /// Gets the current status of this user. UserStatus Status { get; } } diff --git a/src/Discord.Net/Entities/Users/ISelfUser.cs b/src/Discord.Net.Core/Entities/Users/ISelfUser.cs similarity index 91% rename from src/Discord.Net/Entities/Users/ISelfUser.cs rename to src/Discord.Net.Core/Entities/Users/ISelfUser.cs index b6803ccf6..dea95b566 100644 --- a/src/Discord.Net/Entities/Users/ISelfUser.cs +++ b/src/Discord.Net.Core/Entities/Users/ISelfUser.cs @@ -4,7 +4,7 @@ using System.Threading.Tasks; namespace Discord { - public interface ISelfUser : IUser, IUpdateable + public interface ISelfUser : IUser { /// Gets the email associated with this user. string Email { get; } diff --git a/src/Discord.Net/Entities/Users/IUser.cs b/src/Discord.Net.Core/Entities/Users/IUser.cs similarity index 57% rename from src/Discord.Net/Entities/Users/IUser.cs rename to src/Discord.Net.Core/Entities/Users/IUser.cs index 5eef8231c..30aa974cf 100644 --- a/src/Discord.Net/Entities/Users/IUser.cs +++ b/src/Discord.Net.Core/Entities/Users/IUser.cs @@ -1,7 +1,11 @@ +using System.Threading.Tasks; + namespace Discord { public interface IUser : ISnowflakeEntity, IMentionable, IPresence { + /// Gets the id of this user's avatar. + string AvatarId { get; } /// Gets the url to this user's avatar. string AvatarUrl { get; } /// Gets the per-username unique id for this user. @@ -12,5 +16,10 @@ namespace Discord bool IsBot { get; } /// Gets the username for this user. string Username { get; } + + /// Returns a private message channel to this user, returning null if one does not exist or is not cached. + IDMChannel GetCachedDMChannel(); + /// Returns a private message channel to this user, creating one if it does not already exist. + Task CreateDMChannelAsync(); } } diff --git a/src/Discord.Net/Entities/Users/IVoiceState.cs b/src/Discord.Net.Core/Entities/Users/IVoiceState.cs similarity index 100% rename from src/Discord.Net/Entities/Users/IVoiceState.cs rename to src/Discord.Net.Core/Entities/Users/IVoiceState.cs diff --git a/src/Discord.Net/Entities/Users/StreamType.cs b/src/Discord.Net.Core/Entities/Users/StreamType.cs similarity index 100% rename from src/Discord.Net/Entities/Users/StreamType.cs rename to src/Discord.Net.Core/Entities/Users/StreamType.cs diff --git a/src/Discord.Net/Entities/Users/UserStatus.cs b/src/Discord.Net.Core/Entities/Users/UserStatus.cs similarity index 100% rename from src/Discord.Net/Entities/Users/UserStatus.cs rename to src/Discord.Net.Core/Entities/Users/UserStatus.cs diff --git a/src/Discord.Net/Extensions/DiscordClientExtensions.cs b/src/Discord.Net.Core/Extensions/DiscordClientExtensions.cs similarity index 100% rename from src/Discord.Net/Extensions/DiscordClientExtensions.cs rename to src/Discord.Net.Core/Extensions/DiscordClientExtensions.cs diff --git a/src/Discord.Net/Extensions/GuildExtensions.cs b/src/Discord.Net.Core/Extensions/GuildExtensions.cs similarity index 100% rename from src/Discord.Net/Extensions/GuildExtensions.cs rename to src/Discord.Net.Core/Extensions/GuildExtensions.cs diff --git a/src/Discord.Net/Extensions/GuildUserExtensions.cs b/src/Discord.Net.Core/Extensions/GuildUserExtensions.cs similarity index 75% rename from src/Discord.Net/Extensions/GuildUserExtensions.cs rename to src/Discord.Net.Core/Extensions/GuildUserExtensions.cs index 492b5b76b..57d6a13dc 100644 --- a/src/Discord.Net/Extensions/GuildUserExtensions.cs +++ b/src/Discord.Net.Core/Extensions/GuildUserExtensions.cs @@ -9,11 +9,11 @@ namespace Discord public static Task AddRolesAsync(this IGuildUser user, params IRole[] roles) => AddRolesAsync(user, (IEnumerable)roles); public static Task AddRolesAsync(this IGuildUser user, IEnumerable roles) - => user.ModifyAsync(x => x.Roles = user.Roles.Concat(roles)); + => user.ModifyAsync(x => x.RoleIds = user.RoleIds.Concat(roles.Select(y => y.Id)).ToArray()); public static Task RemoveRolesAsync(this IGuildUser user, params IRole[] roles) => RemoveRolesAsync(user, (IEnumerable)roles); public static Task RemoveRolesAsync(this IGuildUser user, IEnumerable roles) - => user.ModifyAsync(x => x.Roles = user.Roles.Except(roles)); + => user.ModifyAsync(x => x.RoleIds = user.RoleIds.Except(roles.Select(y => y.Id)).ToArray()); } } diff --git a/src/Discord.Net.Core/Extensions/SnowflakeEntityExtensions.cs b/src/Discord.Net.Core/Extensions/SnowflakeEntityExtensions.cs new file mode 100644 index 000000000..adbf31a7e --- /dev/null +++ b/src/Discord.Net.Core/Extensions/SnowflakeEntityExtensions.cs @@ -0,0 +1,10 @@ +using System; + +namespace Discord.Extensions +{ + public static class SnowflakeEntityExtensions + { + //TODO: C#7 Candidate for Extension Property. + public static DateTimeOffset GetCreatedAt(this ISnowflakeEntity entity) => DateTimeUtils.FromSnowflake(entity.Id); + } +} diff --git a/src/Discord.Net/Format.cs b/src/Discord.Net.Core/Format.cs similarity index 100% rename from src/Discord.Net/Format.cs rename to src/Discord.Net.Core/Format.cs diff --git a/src/Discord.Net/IDiscordClient.cs b/src/Discord.Net.Core/IDiscordClient.cs similarity index 92% rename from src/Discord.Net/IDiscordClient.cs rename to src/Discord.Net.Core/IDiscordClient.cs index 0b3474ed9..accd9080c 100644 --- a/src/Discord.Net/IDiscordClient.cs +++ b/src/Discord.Net.Core/IDiscordClient.cs @@ -1,5 +1,4 @@ using Discord.API; -using Discord.Logging; using System; using System.Collections.Generic; using System.IO; @@ -12,9 +11,8 @@ namespace Discord public interface IDiscordClient : IDisposable { ConnectionState ConnectionState { get; } - DiscordRestApiClient ApiClient { get; } - ILogManager LogManager { get; } + ISelfUser CurrentUser { get; } Task ConnectAsync(); Task DisconnectAsync(); @@ -35,7 +33,6 @@ namespace Discord Task GetUserAsync(ulong id); Task GetUserAsync(string username, string discriminator); - Task GetCurrentUserAsync(); Task> QueryUsersAsync(string query, int limit); Task> GetVoiceRegionsAsync(); diff --git a/src/Discord.Net/Logging/LogMessage.cs b/src/Discord.Net.Core/Logging/LogMessage.cs similarity index 100% rename from src/Discord.Net/Logging/LogMessage.cs rename to src/Discord.Net.Core/Logging/LogMessage.cs diff --git a/src/Discord.Net/LogSeverity.cs b/src/Discord.Net.Core/Logging/LogSeverity.cs similarity index 100% rename from src/Discord.Net/LogSeverity.cs rename to src/Discord.Net.Core/Logging/LogSeverity.cs diff --git a/src/Discord.Net/LoginState.cs b/src/Discord.Net.Core/LoginState.cs similarity index 100% rename from src/Discord.Net/LoginState.cs rename to src/Discord.Net.Core/LoginState.cs diff --git a/src/Discord.Net/Net/Converters/DiscordContractResolver.cs b/src/Discord.Net.Core/Net/Converters/DiscordContractResolver.cs similarity index 100% rename from src/Discord.Net/Net/Converters/DiscordContractResolver.cs rename to src/Discord.Net.Core/Net/Converters/DiscordContractResolver.cs diff --git a/src/Discord.Net/Net/Converters/ImageConverter.cs b/src/Discord.Net.Core/Net/Converters/ImageConverter.cs similarity index 100% rename from src/Discord.Net/Net/Converters/ImageConverter.cs rename to src/Discord.Net.Core/Net/Converters/ImageConverter.cs diff --git a/src/Discord.Net/Net/Converters/NullableUInt64Converter.cs b/src/Discord.Net.Core/Net/Converters/NullableUInt64Converter.cs similarity index 100% rename from src/Discord.Net/Net/Converters/NullableUInt64Converter.cs rename to src/Discord.Net.Core/Net/Converters/NullableUInt64Converter.cs diff --git a/src/Discord.Net/Net/Converters/OptionalConverter.cs b/src/Discord.Net.Core/Net/Converters/OptionalConverter.cs similarity index 100% rename from src/Discord.Net/Net/Converters/OptionalConverter.cs rename to src/Discord.Net.Core/Net/Converters/OptionalConverter.cs diff --git a/src/Discord.Net/Net/Converters/PermissionTargetConverter.cs b/src/Discord.Net.Core/Net/Converters/PermissionTargetConverter.cs similarity index 100% rename from src/Discord.Net/Net/Converters/PermissionTargetConverter.cs rename to src/Discord.Net.Core/Net/Converters/PermissionTargetConverter.cs diff --git a/src/Discord.Net/Net/Converters/StringEntityConverter.cs b/src/Discord.Net.Core/Net/Converters/StringEntityConverter.cs similarity index 100% rename from src/Discord.Net/Net/Converters/StringEntityConverter.cs rename to src/Discord.Net.Core/Net/Converters/StringEntityConverter.cs diff --git a/src/Discord.Net/Net/Converters/UInt64ArrayConverter.cs b/src/Discord.Net.Core/Net/Converters/UInt64ArrayConverter.cs similarity index 100% rename from src/Discord.Net/Net/Converters/UInt64ArrayConverter.cs rename to src/Discord.Net.Core/Net/Converters/UInt64ArrayConverter.cs diff --git a/src/Discord.Net/Net/Converters/UInt64Converter.cs b/src/Discord.Net.Core/Net/Converters/UInt64Converter.cs similarity index 100% rename from src/Discord.Net/Net/Converters/UInt64Converter.cs rename to src/Discord.Net.Core/Net/Converters/UInt64Converter.cs diff --git a/src/Discord.Net/Net/Converters/UInt64EntityConverter.cs b/src/Discord.Net.Core/Net/Converters/UInt64EntityConverter.cs similarity index 100% rename from src/Discord.Net/Net/Converters/UInt64EntityConverter.cs rename to src/Discord.Net.Core/Net/Converters/UInt64EntityConverter.cs diff --git a/src/Discord.Net/Net/Converters/UserStatusConverter.cs b/src/Discord.Net.Core/Net/Converters/UserStatusConverter.cs similarity index 100% rename from src/Discord.Net/Net/Converters/UserStatusConverter.cs rename to src/Discord.Net.Core/Net/Converters/UserStatusConverter.cs diff --git a/src/Discord.Net/Net/HttpException.cs b/src/Discord.Net.Core/Net/HttpException.cs similarity index 100% rename from src/Discord.Net/Net/HttpException.cs rename to src/Discord.Net.Core/Net/HttpException.cs diff --git a/src/Discord.Net/Net/Queue/Definitions/BucketDefinition.cs b/src/Discord.Net.Core/Net/Queue/Definitions/BucketDefinition.cs similarity index 100% rename from src/Discord.Net/Net/Queue/Definitions/BucketDefinition.cs rename to src/Discord.Net.Core/Net/Queue/Definitions/BucketDefinition.cs diff --git a/src/Discord.Net/Net/Queue/Definitions/BucketGroup.cs b/src/Discord.Net.Core/Net/Queue/Definitions/BucketGroup.cs similarity index 100% rename from src/Discord.Net/Net/Queue/Definitions/BucketGroup.cs rename to src/Discord.Net.Core/Net/Queue/Definitions/BucketGroup.cs diff --git a/src/Discord.Net/Net/Queue/Definitions/BucketTarget.cs b/src/Discord.Net.Core/Net/Queue/Definitions/BucketTarget.cs similarity index 100% rename from src/Discord.Net/Net/Queue/Definitions/BucketTarget.cs rename to src/Discord.Net.Core/Net/Queue/Definitions/BucketTarget.cs diff --git a/src/Discord.Net/Net/Queue/Definitions/ChannelBucket.cs b/src/Discord.Net.Core/Net/Queue/Definitions/ChannelBucket.cs similarity index 100% rename from src/Discord.Net/Net/Queue/Definitions/ChannelBucket.cs rename to src/Discord.Net.Core/Net/Queue/Definitions/ChannelBucket.cs diff --git a/src/Discord.Net/Net/Queue/Definitions/GlobalBucket.cs b/src/Discord.Net.Core/Net/Queue/Definitions/GlobalBucket.cs similarity index 100% rename from src/Discord.Net/Net/Queue/Definitions/GlobalBucket.cs rename to src/Discord.Net.Core/Net/Queue/Definitions/GlobalBucket.cs diff --git a/src/Discord.Net/Net/Queue/Definitions/GuildBucket.cs b/src/Discord.Net.Core/Net/Queue/Definitions/GuildBucket.cs similarity index 100% rename from src/Discord.Net/Net/Queue/Definitions/GuildBucket.cs rename to src/Discord.Net.Core/Net/Queue/Definitions/GuildBucket.cs diff --git a/src/Discord.Net/Net/Queue/IQueuedRequest.cs b/src/Discord.Net.Core/Net/Queue/IQueuedRequest.cs similarity index 89% rename from src/Discord.Net/Net/Queue/IQueuedRequest.cs rename to src/Discord.Net.Core/Net/Queue/IQueuedRequest.cs index ad0c8fcb6..cacc58bb7 100644 --- a/src/Discord.Net/Net/Queue/IQueuedRequest.cs +++ b/src/Discord.Net.Core/Net/Queue/IQueuedRequest.cs @@ -6,7 +6,7 @@ namespace Discord.Net.Queue { //TODO: Allow user-supplied canceltoken //TODO: Allow specifying timeout via DiscordApiClient - internal interface IQueuedRequest + public interface IQueuedRequest { CancellationToken CancelToken { get; } int? TimeoutTick { get; } diff --git a/src/Discord.Net/Net/Queue/RequestQueue.cs b/src/Discord.Net.Core/Net/Queue/RequestQueue.cs similarity index 97% rename from src/Discord.Net/Net/Queue/RequestQueue.cs rename to src/Discord.Net.Core/Net/Queue/RequestQueue.cs index 37e5f816c..5300905c8 100644 --- a/src/Discord.Net/Net/Queue/RequestQueue.cs +++ b/src/Discord.Net.Core/Net/Queue/RequestQueue.cs @@ -107,13 +107,13 @@ namespace Discord.Net.Queue finally { _lock.Release(); } } - internal async Task SendAsync(RestRequest request, BucketGroup group, int bucketId, ulong objId) + public async Task SendAsync(RestRequest request, BucketGroup group, int bucketId, ulong objId) { request.CancelToken = _cancelToken; var bucket = GetBucket(group, bucketId, objId); return await bucket.SendAsync(request).ConfigureAwait(false); } - internal async Task SendAsync(WebSocketRequest request, BucketGroup group, int bucketId, ulong objId) + public async Task SendAsync(WebSocketRequest request, BucketGroup group, int bucketId, ulong objId) { request.CancelToken = _cancelToken; var bucket = GetBucket(group, bucketId, objId); diff --git a/src/Discord.Net/Net/Queue/RequestQueueBucket.cs b/src/Discord.Net.Core/Net/Queue/RequestQueueBucket.cs similarity index 100% rename from src/Discord.Net/Net/Queue/RequestQueueBucket.cs rename to src/Discord.Net.Core/Net/Queue/RequestQueueBucket.cs diff --git a/src/Discord.Net/Net/Queue/RestRequest.cs b/src/Discord.Net.Core/Net/Queue/RestRequest.cs similarity index 97% rename from src/Discord.Net/Net/Queue/RestRequest.cs rename to src/Discord.Net.Core/Net/Queue/RestRequest.cs index 59a106e96..4be4c746c 100644 --- a/src/Discord.Net/Net/Queue/RestRequest.cs +++ b/src/Discord.Net.Core/Net/Queue/RestRequest.cs @@ -7,7 +7,7 @@ using System.Threading.Tasks; namespace Discord.Net.Queue { - internal class RestRequest : IQueuedRequest + public class RestRequest : IQueuedRequest { public IRestClient Client { get; } public string Method { get; } diff --git a/src/Discord.Net/Net/Queue/WebSocketRequest.cs b/src/Discord.Net.Core/Net/Queue/WebSocketRequest.cs similarity index 65% rename from src/Discord.Net/Net/Queue/WebSocketRequest.cs rename to src/Discord.Net.Core/Net/Queue/WebSocketRequest.cs index a7cffbcf9..83f40a4d6 100644 --- a/src/Discord.Net/Net/Queue/WebSocketRequest.cs +++ b/src/Discord.Net.Core/Net/Queue/WebSocketRequest.cs @@ -6,27 +6,22 @@ using System.Threading.Tasks; namespace Discord.Net.Queue { - internal class WebSocketRequest : IQueuedRequest + public class WebSocketRequest : IQueuedRequest { public IWebSocketClient Client { get; } public byte[] Data { get; } - public int DataIndex { get; } - public int DataCount { get; } public bool IsText { get; } public int? TimeoutTick { get; } public TaskCompletionSource Promise { get; } public CancellationToken CancelToken { get; set; } - - public WebSocketRequest(IWebSocketClient client, byte[] data, bool isText, RequestOptions options) : this(client, data, 0, data.Length, isText, options) { } - public WebSocketRequest(IWebSocketClient client, byte[] data, int index, int count, bool isText, RequestOptions options) + + public WebSocketRequest(IWebSocketClient client, byte[] data, bool isText, RequestOptions options) { if (options == null) options = RequestOptions.Default; Client = client; Data = data; - DataIndex = index; - DataCount = count; IsText = isText; TimeoutTick = options.Timeout.HasValue ? (int?)unchecked(Environment.TickCount + options.Timeout.Value) : null; Promise = new TaskCompletionSource(); @@ -34,7 +29,7 @@ namespace Discord.Net.Queue public async Task SendAsync() { - await Client.SendAsync(Data, DataIndex, DataCount, IsText).ConfigureAwait(false); + await Client.SendAsync(Data, 0, Data.Length, IsText).ConfigureAwait(false); return null; } } diff --git a/src/Discord.Net/Net/RateLimitException.cs b/src/Discord.Net.Core/Net/RateLimitException.cs similarity index 100% rename from src/Discord.Net/Net/RateLimitException.cs rename to src/Discord.Net.Core/Net/RateLimitException.cs diff --git a/src/Discord.Net/Net/Rest/DefaultRestClient.cs b/src/Discord.Net.Core/Net/Rest/DefaultRestClient.cs similarity index 100% rename from src/Discord.Net/Net/Rest/DefaultRestClient.cs rename to src/Discord.Net.Core/Net/Rest/DefaultRestClient.cs diff --git a/src/Discord.Net/Net/Rest/IRestClient.cs b/src/Discord.Net.Core/Net/Rest/IRestClient.cs similarity index 100% rename from src/Discord.Net/Net/Rest/IRestClient.cs rename to src/Discord.Net.Core/Net/Rest/IRestClient.cs diff --git a/src/Discord.Net/Net/Rest/MultipartFile.cs b/src/Discord.Net.Core/Net/Rest/MultipartFile.cs similarity index 100% rename from src/Discord.Net/Net/Rest/MultipartFile.cs rename to src/Discord.Net.Core/Net/Rest/MultipartFile.cs diff --git a/src/Discord.Net/Net/Rest/RestClientProvider.cs b/src/Discord.Net.Core/Net/Rest/RestClientProvider.cs similarity index 100% rename from src/Discord.Net/Net/Rest/RestClientProvider.cs rename to src/Discord.Net.Core/Net/Rest/RestClientProvider.cs diff --git a/src/Discord.Net/Net/RpcException.cs b/src/Discord.Net.Core/Net/RpcException.cs similarity index 100% rename from src/Discord.Net/Net/RpcException.cs rename to src/Discord.Net.Core/Net/RpcException.cs diff --git a/src/Discord.Net/Net/WebSocketException.cs b/src/Discord.Net.Core/Net/WebSocketException.cs similarity index 100% rename from src/Discord.Net/Net/WebSocketException.cs rename to src/Discord.Net.Core/Net/WebSocketException.cs diff --git a/src/Discord.Net/Net/WebSockets/DefaultWebsocketClient.cs b/src/Discord.Net.Core/Net/WebSockets/DefaultWebSocketClient.cs similarity index 100% rename from src/Discord.Net/Net/WebSockets/DefaultWebsocketClient.cs rename to src/Discord.Net.Core/Net/WebSockets/DefaultWebSocketClient.cs diff --git a/src/Discord.Net/Net/WebSockets/IWebSocketClient.cs b/src/Discord.Net.Core/Net/WebSockets/IWebSocketClient.cs similarity index 100% rename from src/Discord.Net/Net/WebSockets/IWebSocketClient.cs rename to src/Discord.Net.Core/Net/WebSockets/IWebSocketClient.cs diff --git a/src/Discord.Net/Net/WebSockets/WebSocketProvider.cs b/src/Discord.Net.Core/Net/WebSockets/WebSocketProvider.cs similarity index 100% rename from src/Discord.Net/Net/WebSockets/WebSocketProvider.cs rename to src/Discord.Net.Core/Net/WebSockets/WebSocketProvider.cs diff --git a/src/Discord.Net/RequestOptions.cs b/src/Discord.Net.Core/RequestOptions.cs similarity index 100% rename from src/Discord.Net/RequestOptions.cs rename to src/Discord.Net.Core/RequestOptions.cs diff --git a/src/Discord.Net/TokenType.cs b/src/Discord.Net.Core/TokenType.cs similarity index 100% rename from src/Discord.Net/TokenType.cs rename to src/Discord.Net.Core/TokenType.cs diff --git a/src/Discord.Net.Core/Utils/MentionUtils.cs b/src/Discord.Net.Core/Utils/MentionUtils.cs new file mode 100644 index 000000000..acfe35a03 --- /dev/null +++ b/src/Discord.Net.Core/Utils/MentionUtils.cs @@ -0,0 +1,85 @@ +using System; +using System.Globalization; + +namespace Discord +{ + public static class MentionUtils + { + //If the system can't be positive a user doesn't have a nickname, assume useNickname = true (source: Jake) + public static string MentionUser(ulong id) => MentionsHelper.MentionUser(id, true); + public static string MentionChannel(ulong id) => MentionsHelper.MentionChannel(id); + public static string MentionRole(ulong id) => MentionsHelper.MentionRole(id); + + /// Parses a provided user mention string. + public static ulong ParseUser(string mentionText) + { + ulong id; + if (TryParseUser(mentionText, out id)) + return id; + throw new ArgumentException("Invalid mention format", nameof(mentionText)); + } + /// Tries to parse a provided user mention string. + public static bool TryParseUser(string mentionText, out ulong userId) + { + mentionText = mentionText.Trim(); + if (mentionText.Length >= 3 && mentionText[0] == '<' && mentionText[1] == '@' && mentionText[mentionText.Length - 1] == '>') + { + if (mentionText.Length >= 4 && mentionText[2] == '!') + mentionText = mentionText.Substring(3, mentionText.Length - 4); //<@!123> + else + mentionText = mentionText.Substring(2, mentionText.Length - 3); //<@123> + + if (ulong.TryParse(mentionText, NumberStyles.None, CultureInfo.InvariantCulture, out userId)) + return true; + } + userId = 0; + return false; + } + + /// Parses a provided channel mention string. + public static ulong ParseChannel(string mentionText) + { + ulong id; + if (TryParseChannel(mentionText, out id)) + return id; + throw new ArgumentException("Invalid mention format", nameof(mentionText)); + } + /// Tries to parse a provided channel mention string. + public static bool TryParseChannel(string mentionText, out ulong channelId) + { + mentionText = mentionText.Trim(); + if (mentionText.Length >= 3 && mentionText[0] == '<' && mentionText[1] == '#' && mentionText[mentionText.Length - 1] == '>') + { + mentionText = mentionText.Substring(2, mentionText.Length - 3); //<#123> + + if (ulong.TryParse(mentionText, NumberStyles.None, CultureInfo.InvariantCulture, out channelId)) + return true; + } + channelId = 0; + return false; + } + + /// Parses a provided role mention string. + public static ulong ParseRole(string mentionText) + { + ulong id; + if (TryParseRole(mentionText, out id)) + return id; + throw new ArgumentException("Invalid mention format", nameof(mentionText)); + } + /// Tries to parse a provided role mention string. + public static bool TryParseRole(string mentionText, out ulong roleId) + { + mentionText = mentionText.Trim(); + if (mentionText.Length >= 4 && mentionText[0] == '<' && mentionText[1] == '@' && mentionText[2] == '&' && mentionText[mentionText.Length - 1] == '>') + { + mentionText = mentionText.Substring(3, mentionText.Length - 4); //<@&123> + + if (ulong.TryParse(mentionText, NumberStyles.None, CultureInfo.InvariantCulture, out roleId)) + return true; + } + roleId = 0; + return false; + } + } +} diff --git a/src/Discord.Net/Utilities/Optional.cs b/src/Discord.Net.Core/Utils/Optional.cs similarity index 100% rename from src/Discord.Net/Utilities/Optional.cs rename to src/Discord.Net.Core/Utils/Optional.cs diff --git a/src/Discord.Net.Core/project.json b/src/Discord.Net.Core/project.json new file mode 100644 index 000000000..c7b95dcae --- /dev/null +++ b/src/Discord.Net.Core/project.json @@ -0,0 +1,62 @@ +{ + "version": "1.0.0-beta2-*", + "description": "An unofficial .Net API wrapper for the Discord service.", + "authors": [ "RogueException" ], + + "packOptions": { + "tags": [ "discord", "discordapp" ], + "licenseUrl": "http://opensource.org/licenses/MIT", + "projectUrl": "https://github.com/RogueException/Discord.Net", + "repository": { + "type": "git", + "url": "git://github.com/RogueException/Discord.Net" + } + }, + + "buildOptions": { + "compile": { + "include": [ "../Discord.Net.Utils/**.cs" ] + } + }, + + "configurations": { + "Release": { + "buildOptions": { + "define": [ "RELEASE" ], + "nowarn": [ "CS1573", "CS1591" ], + "optimize": true, + "warningsAsErrors": true, + "xmlDoc": true + } + } + }, + + "dependencies": { + "Microsoft.Win32.Primitives": "4.0.1", + "Newtonsoft.Json": "8.0.3", + "System.Collections.Concurrent": "4.0.12", + "System.Collections.Immutable": "1.2.0", + "System.Interactive.Async": "3.0.0", + "System.IO.Compression": "4.1.0", + "System.IO.FileSystem": "4.0.1", + "System.Net.Http": "4.1.0", + "System.Net.NameResolution": "4.0.0", + "System.Net.Sockets": "4.1.0", + "System.Net.WebSockets.Client": "4.0.0", + "System.Reflection.Extensions": "4.0.1", + "System.Runtime.InteropServices": "4.1.0", + "System.Runtime.InteropServices.RuntimeInformation": "4.0.0", + "System.Runtime.Serialization.Primitives": "4.1.1", + "System.Text.RegularExpressions": "4.1.0" + }, + + "frameworks": { + "netstandard1.3": { + "imports": [ + "dotnet5.4", + "dnxcore50", + "portable-net45+win8" + ] + } + } +} diff --git a/src/Discord.Net.Rest/Discord.Net.Rest.xproj b/src/Discord.Net.Rest/Discord.Net.Rest.xproj new file mode 100644 index 000000000..6a5d3e2b8 --- /dev/null +++ b/src/Discord.Net.Rest/Discord.Net.Rest.xproj @@ -0,0 +1,19 @@ + + + + 14.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + bfc6dc28-0351-4573-926a-d4124244c04f + Discord.Rest + .\obj + .\bin\ + v4.6.1 + + + 2.0 + + + \ No newline at end of file diff --git a/src/Discord.Net/Rest/DiscordRestClient.cs b/src/Discord.Net.Rest/DiscordRestClient.cs similarity index 65% rename from src/Discord.Net/Rest/DiscordRestClient.cs rename to src/Discord.Net.Rest/DiscordRestClient.cs index 11cf10747..b1668250d 100644 --- a/src/Discord.Net/Rest/DiscordRestClient.cs +++ b/src/Discord.Net.Rest/DiscordRestClient.cs @@ -1,5 +1,4 @@ using Discord.API.Rest; -using Discord.Logging; using Discord.Net; using Discord.Net.Queue; using System; @@ -10,8 +9,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using System.Runtime.InteropServices; -using Discord.Rpc; -using Discord.WebSocket; +using Discord.Logging; namespace Discord.Rest { @@ -27,15 +25,15 @@ namespace Discord.Rest public event Func LoggedOut { add { _loggedOutEvent.Add(value); } remove { _loggedOutEvent.Remove(value); } } private readonly AsyncEvent> _loggedOutEvent = new AsyncEvent>(); - internal readonly ILogger _clientLogger, _restLogger, _queueLogger; + internal readonly Logger _clientLogger, _restLogger, _queueLogger; internal readonly SemaphoreSlim _connectionLock; - internal SelfUser _currentUser; private bool _isFirstLogSub; internal bool _isDisposed; public API.DiscordRestApiClient ApiClient { get; } internal LogManager LogManager { get; } public LoginState LoginState { get; private set; } + public RestSelfUser CurrentUser { get; private set; } /// Creates a new REST-only discord client. public DiscordRestClient() : this(new DiscordRestConfig()) { } @@ -62,7 +60,7 @@ namespace Discord.Rest ApiClient.SentRequest += async (method, endpoint, millis) => await _restLogger.VerboseAsync($"{method} {endpoint}: {millis} ms").ConfigureAwait(false); } private static API.DiscordRestApiClient CreateApiClient(DiscordRestConfig config) - => new API.DiscordRestApiClient(config.RestClientProvider, requestQueue: new RequestQueue()); + => new API.DiscordRestApiClient(config.RestClientProvider, DiscordRestConfig.UserAgent, requestQueue: new RequestQueue()); /// public async Task LoginAsync(TokenType tokenType, string token, bool validateToken = true) @@ -70,11 +68,11 @@ namespace Discord.Rest await _connectionLock.WaitAsync().ConfigureAwait(false); try { - await LoginInternalAsync(tokenType, token, validateToken).ConfigureAwait(false); + await LoginInternalAsync(tokenType, token).ConfigureAwait(false); } finally { _connectionLock.Release(); } } - private async Task LoginInternalAsync(TokenType tokenType, string token, bool validateToken) + private async Task LoginInternalAsync(TokenType tokenType, string token) { if (_isFirstLogSub) { @@ -89,10 +87,9 @@ namespace Discord.Rest try { await ApiClient.LoginAsync(tokenType, token).ConfigureAwait(false); - if (validateToken) - await ValidateTokenAsync(tokenType, token).ConfigureAwait(false); - await OnLoginAsync(tokenType, token).ConfigureAwait(false); + CurrentUser = RestSelfUser.Create(this, ApiClient.CurrentUser); + await OnLoginAsync(tokenType, token).ConfigureAwait(false); LoginState = LoginState.LoggedIn; } catch (Exception) @@ -103,27 +100,8 @@ namespace Discord.Rest await _loggedInEvent.InvokeAsync().ConfigureAwait(false); } - protected virtual async Task ValidateTokenAsync(TokenType tokenType, string token) - { - try - { - var user = await GetCurrentUserAsync().ConfigureAwait(false); - if (user == null) //Is using a cached DiscordClient - user = new SelfUser(this, await ApiClient.GetMyUserAsync().ConfigureAwait(false)); - - if (user.IsBot && tokenType == TokenType.User) - throw new InvalidOperationException($"A bot token used provided with {nameof(TokenType)}.{nameof(TokenType.User)}"); - else if (!user.IsBot && tokenType == TokenType.Bot) //Discord currently sends a 401 in this case - throw new InvalidOperationException($"A user token used provided with {nameof(TokenType)}.{nameof(TokenType.Bot)}"); - } - catch (HttpException ex) - { - throw new ArgumentException("Token validation failed", nameof(token), ex); - } - } protected virtual Task OnLoginAsync(TokenType tokenType, string token) => Task.CompletedTask; - /// public async Task LogoutAsync() { @@ -139,12 +117,10 @@ namespace Discord.Rest if (LoginState == LoginState.LoggedOut) return; LoginState = LoginState.LoggingOut; - await ApiClient.LogoutAsync().ConfigureAwait(false); - + await ApiClient.LogoutAsync().ConfigureAwait(false); await OnLogoutAsync().ConfigureAwait(false); - _currentUser = null; - + CurrentUser = null; LoginState = LoginState.LoggedOut; await _loggedOutEvent.InvokeAsync().ConfigureAwait(false); @@ -155,7 +131,7 @@ namespace Discord.Rest public async Task GetApplicationInfoAsync() { var model = await ApiClient.GetMyApplicationAsync().ConfigureAwait(false); - return new Application(this, model); + return RestApplication.Create(this, model); } /// @@ -164,25 +140,19 @@ namespace Discord.Rest var model = await ApiClient.GetChannelAsync(id).ConfigureAwait(false); if (model != null) { - if (model.GuildId.IsSpecified) - { - var guildModel = await ApiClient.GetGuildAsync(model.GuildId.Value).ConfigureAwait(false); - if (guildModel != null) - { - var guild = new Guild(this, guildModel); - return guild.ToChannel(model); - } - } - else if (model.Type == ChannelType.DM) - return new DMChannel(this, new User(model.Recipients.Value[0]), model); - else if (model.Type == ChannelType.Group) + switch (model.Type) { - var channel = new GroupChannel(this, model); - channel.UpdateUsers(model.Recipients.Value, UpdateSource.Creation); - return channel; + case ChannelType.Text: + return RestTextChannel.Create(this, model); + case ChannelType.Voice: + return RestVoiceChannel.Create(this, model); + case ChannelType.DM: + return RestDMChannel.Create(this, model); + case ChannelType.Group: + return RestGroupChannel.Create(this, model); + default: + throw new InvalidOperationException($"Unexpected channel type: {model.Type}"); } - else - throw new InvalidOperationException($"Unexpected channel type: {model.Type}"); } return null; } @@ -190,115 +160,103 @@ namespace Discord.Rest public virtual async Task> GetPrivateChannelsAsync() { var models = await ApiClient.GetMyPrivateChannelsAsync().ConfigureAwait(false); - return models.Select(x => new DMChannel(this, new User(x.Recipients.Value[0]), x)).ToImmutableArray(); + return models.Select(x => RestDMChannel.Create(this, x)).ToImmutableArray(); } /// - public async Task> GetConnectionsAsync() + public async Task> GetConnectionsAsync() { var models = await ApiClient.GetMyConnectionsAsync().ConfigureAwait(false); - return models.Select(x => new Connection(x)).ToImmutableArray(); + return models.Select(x => RestConnection.Create(x)).ToImmutableArray(); } /// - public virtual async Task GetInviteAsync(string inviteId) + public virtual async Task GetInviteAsync(string inviteId) { var model = await ApiClient.GetInviteAsync(inviteId).ConfigureAwait(false); if (model != null) - return new Invite(this, model); + return RestInvite.Create(this, model); return null; } /// - public virtual async Task GetGuildAsync(ulong id) + public virtual async Task GetGuildAsync(ulong id) { var model = await ApiClient.GetGuildAsync(id).ConfigureAwait(false); if (model != null) - return new Guild(this, model); + return RestGuild.Create(this, model); return null; } /// - public virtual async Task GetGuildEmbedAsync(ulong id) + public virtual async Task GetGuildEmbedAsync(ulong id) { var model = await ApiClient.GetGuildEmbedAsync(id).ConfigureAwait(false); if (model != null) - return new GuildEmbed(model); + return RestGuildEmbed.Create(model); return null; } /// - public virtual async Task> GetGuildSummariesAsync() + public virtual async Task> GetGuildSummariesAsync() { var models = await ApiClient.GetMyGuildsAsync().ConfigureAwait(false); - return models.Select(x => new UserGuild(this, x)).ToImmutableArray(); + return models.Select(x => RestUserGuild.Create(this, x)).ToImmutableArray(); } /// - public virtual async Task> GetGuildsAsync() + public virtual async Task> GetGuildsAsync() { var summaryModels = await ApiClient.GetMyGuildsAsync().ConfigureAwait(false); - var guilds = ImmutableArray.CreateBuilder(summaryModels.Count); + var guilds = ImmutableArray.CreateBuilder(summaryModels.Count); foreach (var summaryModel in summaryModels) { var guildModel = await ApiClient.GetGuildAsync(summaryModel.Id).ConfigureAwait(false); if (guildModel != null) - guilds.Add(new Guild(this, guildModel)); + guilds.Add(RestGuild.Create(this, guildModel)); } return guilds.ToImmutable(); } /// - public virtual async Task CreateGuildAsync(string name, IVoiceRegion region, Stream jpegIcon = null) + public virtual async Task CreateGuildAsync(string name, IVoiceRegion region, Stream jpegIcon = null) { - var args = new CreateGuildParams(); + var args = new CreateGuildParams(name, region.Id); var model = await ApiClient.CreateGuildAsync(args).ConfigureAwait(false); - return new Guild(this, model); + return RestGuild.Create(this, model); } /// - public virtual async Task GetUserAsync(ulong id) + public virtual async Task GetUserAsync(ulong id) { var model = await ApiClient.GetUserAsync(id).ConfigureAwait(false); if (model != null) - return new User(model); + return RestUser.Create(this, model); return null; } /// - public virtual async Task GetUserAsync(string username, string discriminator) + public virtual async Task GetUserAsync(string username, string discriminator) { var model = await ApiClient.GetUserAsync(username, discriminator).ConfigureAwait(false); if (model != null) - return new User(model); + return RestUser.Create(this, model); return null; } - /// - public virtual async Task GetCurrentUserAsync() - { - var user = _currentUser; - if (user == null) - { - var model = await ApiClient.GetMyUserAsync().ConfigureAwait(false); - user = new SelfUser(this, model); - _currentUser = user; - } - return user; - } /// - public virtual async Task> QueryUsersAsync(string query, int limit) + public virtual async Task> QueryUsersAsync(string query, int limit) { var models = await ApiClient.QueryUsersAsync(query, limit).ConfigureAwait(false); - return models.Select(x => new User(x)).ToImmutableArray(); + return models.Select(x => RestUser.Create(this, x)).ToImmutableArray(); } /// - public virtual async Task> GetVoiceRegionsAsync() + public virtual async Task> GetVoiceRegionsAsync() { var models = await ApiClient.GetVoiceRegionsAsync().ConfigureAwait(false); - return models.Select(x => new VoiceRegion(x)).ToImmutableArray(); + return models.Select(x => RestVoiceRegion.Create(this, x)).ToImmutableArray(); } /// - public virtual async Task GetVoiceRegionAsync(string id) + public virtual async Task GetVoiceRegionAsync(string id) { var models = await ApiClient.GetVoiceRegionsAsync().ConfigureAwait(false); - return models.Select(x => new VoiceRegion(x)).Where(x => x.Id == id).FirstOrDefault(); + return models.Select(x => RestVoiceRegion.Create(this, x)).Where(x => x.Id == id).FirstOrDefault(); } internal virtual void Dispose(bool disposing) @@ -314,17 +272,15 @@ namespace Discord.Rest private async Task WriteInitialLog() { - if (this is DiscordSocketClient) + /*if (this is DiscordSocketClient) await _clientLogger.InfoAsync($"DiscordSocketClient v{DiscordConfig.Version} (API v{DiscordConfig.APIVersion}, {DiscordSocketConfig.GatewayEncoding})").ConfigureAwait(false); else if (this is DiscordRpcClient) - await _clientLogger.InfoAsync($"DiscordRpcClient v{DiscordConfig.Version} (API v{DiscordConfig.APIVersion}, RPC API v{DiscordRpcConfig.RpcAPIVersion})").ConfigureAwait(false); - else - await _clientLogger.InfoAsync($"DiscordClient v{DiscordConfig.Version} (API v{DiscordConfig.APIVersion})").ConfigureAwait(false); + await _clientLogger.InfoAsync($"DiscordRpcClient v{DiscordConfig.Version} (API v{DiscordConfig.APIVersion}, RPC API v{DiscordRpcConfig.RpcAPIVersion})").ConfigureAwait(false);*/ + await _clientLogger.InfoAsync($"Discord.Net v{DiscordConfig.Version} (API v{DiscordConfig.APIVersion})").ConfigureAwait(false); await _clientLogger.VerboseAsync($"Runtime: {RuntimeInformation.FrameworkDescription.Trim()} ({ToArchString(RuntimeInformation.ProcessArchitecture)})").ConfigureAwait(false); await _clientLogger.VerboseAsync($"OS: {RuntimeInformation.OSDescription.Trim()} ({ToArchString(RuntimeInformation.OSArchitecture)})").ConfigureAwait(false); await _clientLogger.VerboseAsync($"Processors: {Environment.ProcessorCount}").ConfigureAwait(false); } - private static string ToArchString(Architecture arch) { switch (arch) @@ -335,8 +291,32 @@ namespace Discord.Rest } } + //IDiscordClient ConnectionState IDiscordClient.ConnectionState => ConnectionState.Disconnected; - ILogManager IDiscordClient.LogManager => LogManager; + ISelfUser IDiscordClient.CurrentUser => CurrentUser; + + async Task> IDiscordClient.GetConnectionsAsync() + => await GetConnectionsAsync().ConfigureAwait(false); + async Task IDiscordClient.GetInviteAsync(string inviteId) + => await GetInviteAsync(inviteId).ConfigureAwait(false); + async Task IDiscordClient.GetGuildAsync(ulong id) + => await GetGuildAsync(id).ConfigureAwait(false); + async Task> IDiscordClient.GetGuildSummariesAsync() + => await GetGuildSummariesAsync().ConfigureAwait(false); + async Task> IDiscordClient.GetGuildsAsync() + => await GetGuildsAsync().ConfigureAwait(false); + async Task IDiscordClient.CreateGuildAsync(string name, IVoiceRegion region, Stream jpegIcon) + => await CreateGuildAsync(name, region, jpegIcon).ConfigureAwait(false); + async Task IDiscordClient.GetUserAsync(ulong id) + => await GetUserAsync(id).ConfigureAwait(false); + async Task IDiscordClient.GetUserAsync(string username, string discriminator) + => await GetUserAsync(username, discriminator).ConfigureAwait(false); + async Task> IDiscordClient.QueryUsersAsync(string query, int limit) + => await QueryUsersAsync(query, limit).ConfigureAwait(false); + async Task> IDiscordClient.GetVoiceRegionsAsync() + => await GetVoiceRegionsAsync().ConfigureAwait(false); + async Task IDiscordClient.GetVoiceRegionAsync(string id) + => await GetVoiceRegionAsync(id).ConfigureAwait(false); Task IDiscordClient.ConnectAsync() { throw new NotSupportedException(); } Task IDiscordClient.DisconnectAsync() { throw new NotSupportedException(); } diff --git a/src/Discord.Net/Rest/DiscordRestConfig.cs b/src/Discord.Net.Rest/DiscordRestConfig.cs similarity index 100% rename from src/Discord.Net/Rest/DiscordRestConfig.cs rename to src/Discord.Net.Rest/DiscordRestConfig.cs diff --git a/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs b/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs new file mode 100644 index 000000000..26512acaf --- /dev/null +++ b/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs @@ -0,0 +1,211 @@ +using Discord.API.Rest; +using System.IO; +using System.Threading.Tasks; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Collections.Immutable; +using Model = Discord.API.Channel; + +namespace Discord.Rest +{ + internal static class ChannelHelper + { + //General + public static async Task GetAsync(IGuildChannel channel, DiscordRestClient client) + { + return await client.ApiClient.GetChannelAsync(channel.GuildId, channel.Id).ConfigureAwait(false); + } + public static async Task GetAsync(IPrivateChannel channel, DiscordRestClient client) + { + return await client.ApiClient.GetChannelAsync(channel.Id).ConfigureAwait(false); + } + public static async Task DeleteAsync(IChannel channel, DiscordRestClient client) + { + await client.ApiClient.DeleteChannelAsync(channel.Id).ConfigureAwait(false); + } + public static async Task ModifyAsync(IGuildChannel channel, DiscordRestClient client, + Action func) + { + var args = new ModifyGuildChannelParams(); + func(args); + await client.ApiClient.ModifyGuildChannelAsync(channel.Id, args); + } + public static async Task ModifyAsync(ITextChannel channel, DiscordRestClient client, + Action func) + { + var args = new ModifyTextChannelParams(); + func(args); + await client.ApiClient.ModifyGuildChannelAsync(channel.Id, args); + } + public static async Task ModifyAsync(IVoiceChannel channel, DiscordRestClient client, + Action func) + { + var args = new ModifyVoiceChannelParams(); + func(args); + await client.ApiClient.ModifyGuildChannelAsync(channel.Id, args); + } + + //Invites + public static async Task> GetInvitesAsync(IChannel channel, DiscordRestClient client) + { + var models = await client.ApiClient.GetChannelInvitesAsync(channel.Id); + return models.Select(x => RestInviteMetadata.Create(client, x)).ToImmutableArray(); + } + public static async Task CreateInviteAsync(IChannel channel, DiscordRestClient client, + int? maxAge, int? maxUses, bool isTemporary) + { + var args = new CreateChannelInviteParams { IsTemporary = isTemporary }; + if (maxAge.HasValue) + args.MaxAge = maxAge.Value; + if (maxUses.HasValue) + args.MaxUses = maxUses.Value; + var model = await client.ApiClient.CreateChannelInviteAsync(channel.Id, args); + return RestInviteMetadata.Create(client, model); + } + + //Messages + public static async Task GetMessageAsync(IChannel channel, DiscordRestClient client, + ulong id) + { + var model = await client.ApiClient.GetChannelMessageAsync(channel.Id, id).ConfigureAwait(false); + return RestMessage.Create(client, model); + } + public static PagedAsyncEnumerable GetMessagesAsync(IChannel channel, DiscordRestClient client, + ulong? fromMessageId = null, Direction dir = Direction.Before, int limit = DiscordConfig.MaxMessagesPerBatch) + { + //TODO: Test this with Around direction + return new PagedAsyncEnumerable( + DiscordConfig.MaxMessagesPerBatch, + async (info, ct) => + { + var args = new GetChannelMessagesParams + { + RelativeDirection = dir, + Limit = info.PageSize + }; + if (info.Position != null) + args.RelativeMessageId = info.Position.Value; + var models = await client.ApiClient.GetChannelMessagesAsync(channel.Id, args); + return models.Select(x => RestMessage.Create(client, x)).ToImmutableArray(); ; + }, + nextPage: (info, lastPage) => + { + if (dir == Direction.Before) + info.Position = lastPage.Min(x => x.Id); + else + info.Position = lastPage.Max(x => x.Id); + if (lastPage.Count != DiscordConfig.MaxMessagesPerBatch) + info.Remaining = 0; + }, + start: fromMessageId, + count: (uint)limit + ); + } + public static async Task> GetPinnedMessagesAsync(IChannel channel, DiscordRestClient client) + { + var models = await client.ApiClient.GetPinsAsync(channel.Id).ConfigureAwait(false); + return models.Select(x => RestMessage.Create(client, x)).ToImmutableArray(); + } + + public static async Task SendMessageAsync(IChannel channel, DiscordRestClient client, + string text, bool isTTS) + { + var args = new CreateMessageParams(text) { IsTTS = isTTS }; + var model = await client.ApiClient.CreateMessageAsync(channel.Id, args).ConfigureAwait(false); + return RestUserMessage.Create(client, model); + } + + public static Task SendFileAsync(IChannel channel, DiscordRestClient client, + string filePath, string text, bool isTTS) + { + string filename = Path.GetFileName(filePath); + using (var file = File.OpenRead(filePath)) + return SendFileAsync(channel, client, file, filename, text, isTTS); + } + public static async Task SendFileAsync(IChannel channel, DiscordRestClient client, + Stream stream, string filename, string text, bool isTTS) + { + var args = new UploadFileParams(stream) { Filename = filename, Content = text, IsTTS = isTTS }; + var model = await client.ApiClient.UploadFileAsync(channel.Id, args).ConfigureAwait(false); + return RestUserMessage.Create(client, model); + } + + public static async Task DeleteMessagesAsync(IChannel channel, DiscordRestClient client, + IEnumerable messages) + { + var args = new DeleteMessagesParams(messages.Select(x => x.Id).ToArray()); + await client.ApiClient.DeleteMessagesAsync(channel.Id, args).ConfigureAwait(false); + } + + //Permission Overwrites + public static async Task AddPermissionOverwriteAsync(IGuildChannel channel, DiscordRestClient client, + IUser user, OverwritePermissions perms) + { + var args = new ModifyChannelPermissionsParams("member", perms.AllowValue, perms.DenyValue); + await client.ApiClient.ModifyChannelPermissionsAsync(channel.Id, user.Id, args).ConfigureAwait(false); + } + public static async Task AddPermissionOverwriteAsync(IGuildChannel channel, DiscordRestClient client, + IRole role, OverwritePermissions perms) + { + var args = new ModifyChannelPermissionsParams("role", perms.AllowValue, perms.DenyValue); + await client.ApiClient.ModifyChannelPermissionsAsync(channel.Id, role.Id, args).ConfigureAwait(false); + } + public static async Task RemovePermissionOverwriteAsync(IGuildChannel channel, DiscordRestClient client, + IUser user) + { + await client.ApiClient.DeleteChannelPermissionAsync(channel.Id, user.Id).ConfigureAwait(false); + } + public static async Task RemovePermissionOverwriteAsync(IGuildChannel channel, DiscordRestClient client, + IRole role) + { + await client.ApiClient.DeleteChannelPermissionAsync(channel.Id, role.Id).ConfigureAwait(false); + } + + //Users + public static async Task GetUserAsync(IGuildChannel channel, DiscordRestClient client, + ulong id) + { + var model = await client.ApiClient.GetGuildMemberAsync(channel.GuildId, id); + if (model == null) + return null; + var user = RestGuildUser.Create(client, model); + if (!user.GetPermissions(channel).ReadMessages) + return null; + + return user; + } + public static IAsyncEnumerable> GetUsersAsync(IGuildChannel channel, DiscordRestClient client, + ulong? froUserId = null, uint? limit = DiscordConfig.MaxUsersPerBatch) + { + return new PagedAsyncEnumerable( + DiscordConfig.MaxUsersPerBatch, + async (info, ct) => + { + var args = new GetGuildMembersParams + { + Limit = info.PageSize + }; + if (info.Position != null) + args.AfterUserId = info.Position.Value; + var models = await client.ApiClient.GetGuildMembersAsync(channel.GuildId, args); + return models.Select(x => RestGuildUser.Create(client, x)).ToImmutableArray(); ; + }, + nextPage: (info, lastPage) => + { + info.Position = lastPage.Max(x => x.Id); + if (lastPage.Count != DiscordConfig.MaxMessagesPerBatch) + info.Remaining = 0; + }, + start: froUserId, + count: limit + ); + } + + //Typing + public static IDisposable EnterTypingState(IChannel channel, DiscordRestClient client) + { + throw new NotImplementedException(); //TODO: Impl + } + } +} diff --git a/src/Discord.Net.Rest/Entities/Channels/RestDMChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RestDMChannel.cs new file mode 100644 index 000000000..796ec23cb --- /dev/null +++ b/src/Discord.Net.Rest/Entities/Channels/RestDMChannel.cs @@ -0,0 +1,113 @@ +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Model = Discord.API.Channel; + +namespace Discord.Rest +{ + [DebuggerDisplay(@"{DebuggerDisplay,nq}")] + public class RestDMChannel : RestEntity, IDMChannel, IUpdateable + { + public RestUser Recipient { get; } + + public IReadOnlyCollection Users => ImmutableArray.Create(Discord.CurrentUser, Recipient); + + internal RestDMChannel(DiscordRestClient discord, ulong id) + : base(discord, id) + { + } + internal static RestDMChannel Create(DiscordRestClient discord, Model model) + { + var entity = new RestDMChannel(discord, model.Id); + entity.Update(model); + return entity; + } + internal void Update(Model model) + { + Recipient.Update(model.Recipients.Value[0]); + } + + public async Task UpdateAsync() + => Update(await ChannelHelper.GetAsync(this, Discord)); + public Task CloseAsync() + => ChannelHelper.DeleteAsync(this, Discord); + + public RestUser GetUser(ulong id) + { + if (id == Recipient.Id) + return Recipient; + else if (id == Discord.CurrentUser.Id) + return Discord.CurrentUser; + else + return null; + } + + public Task GetMessageAsync(ulong id) + => ChannelHelper.GetMessageAsync(this, Discord, id); + public IAsyncEnumerable> GetMessagesAsync(int limit = DiscordConfig.MaxMessagesPerBatch) + => ChannelHelper.GetMessagesAsync(this, Discord, limit: limit); + public IAsyncEnumerable> GetMessagesAsync(ulong fromMessageId, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch) + => ChannelHelper.GetMessagesAsync(this, Discord, fromMessageId, dir, limit); + public IAsyncEnumerable> GetMessagesAsync(IMessage fromMessage, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch) + => ChannelHelper.GetMessagesAsync(this, Discord, fromMessage.Id, dir, limit); + public Task> GetPinnedMessagesAsync() + => ChannelHelper.GetPinnedMessagesAsync(this, Discord); + + public Task SendMessageAsync(string text, bool isTTS) + => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS); + public Task SendFileAsync(string filePath, string text, bool isTTS) + => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS); + public Task SendFileAsync(Stream stream, string filename, string text, bool isTTS) + => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS); + + public Task DeleteMessagesAsync(IEnumerable messages) + => ChannelHelper.DeleteMessagesAsync(this, Discord, messages); + + public IDisposable EnterTypingState() + => ChannelHelper.EnterTypingState(this, Discord); + + public override string ToString() => $"@{Recipient}"; + private string DebuggerDisplay => $"@{Recipient} ({Id}, DM)"; + + //IDMChannel + IUser IDMChannel.Recipient => Recipient; + + //IPrivateChannel + IReadOnlyCollection IPrivateChannel.Recipients => ImmutableArray.Create(Recipient); + + //IMessageChannel + IReadOnlyCollection IMessageChannel.CachedMessages => ImmutableArray.Create(); + IMessage IMessageChannel.GetCachedMessage(ulong id) => null; + + async Task IMessageChannel.GetMessageAsync(ulong id) + => await GetMessageAsync(id); + IAsyncEnumerable> IMessageChannel.GetMessagesAsync(int limit) + => GetMessagesAsync(limit); + IAsyncEnumerable> IMessageChannel.GetMessagesAsync(ulong fromMessageId, Direction dir, int limit) + => GetMessagesAsync(fromMessageId, dir, limit); + async Task> IMessageChannel.GetPinnedMessagesAsync() + => await GetPinnedMessagesAsync().ConfigureAwait(false); + async Task IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS) + => await SendFileAsync(filePath, text, isTTS); + async Task IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS) + => await SendFileAsync(stream, filename, text, isTTS); + async Task IMessageChannel.SendMessageAsync(string text, bool isTTS) + => await SendMessageAsync(text, isTTS); + IDisposable IMessageChannel.EnterTypingState() + => EnterTypingState(); + + //IChannel + IReadOnlyCollection IChannel.CachedUsers => Users; + + IUser IChannel.GetCachedUser(ulong id) + => GetUser(id); + Task IChannel.GetUserAsync(ulong id) + => Task.FromResult(GetUser(id)); + IAsyncEnumerable> IChannel.GetUsersAsync() + => ImmutableArray.Create>().ToAsyncEnumerable(); + } +} diff --git a/src/Discord.Net.Rest/Entities/Channels/RestGroupChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RestGroupChannel.cs new file mode 100644 index 000000000..b95cfce14 --- /dev/null +++ b/src/Discord.Net.Rest/Entities/Channels/RestGroupChannel.cs @@ -0,0 +1,125 @@ +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Model = Discord.API.Channel; + +namespace Discord.Rest +{ + [DebuggerDisplay(@"{DebuggerDisplay,nq}")] + public class RestGroupChannel : RestEntity, IGroupChannel, IUpdateable + { + private string _iconId; + private ImmutableDictionary _users; + + public string Name { get; private set; } + + public IReadOnlyCollection Users => _users.ToReadOnlyCollection(); + public IReadOnlyCollection Recipients + => _users.Select(x => x.Value).Where(x => x.Id != Discord.CurrentUser.Id).ToReadOnlyCollection(() => _users.Count - 1); + + internal RestGroupChannel(DiscordRestClient discord, ulong id) + : base(discord, id) + { + } + internal static RestGroupChannel Create(DiscordRestClient discord, Model model) + { + var entity = new RestGroupChannel(discord, model.Id); + entity.Update(model); + return entity; + } + internal void Update(Model model) + { + if (model.Name.IsSpecified) + Name = model.Name.Value; + if (model.Icon.IsSpecified) + _iconId = model.Icon.Value; + + if (model.Recipients.IsSpecified) + UpdateUsers(model.Recipients.Value); + } + internal virtual void UpdateUsers(API.User[] models) + { + var users = ImmutableDictionary.CreateBuilder(); + for (int i = 0; i < models.Length; i++) + users[models[i].Id] = RestGroupUser.Create(Discord, models[i]); + _users = users.ToImmutable(); + } + + public async Task UpdateAsync() + => Update(await ChannelHelper.GetAsync(this, Discord)); + public Task LeaveAsync() + => ChannelHelper.DeleteAsync(this, Discord); + + public IUser GetUser(ulong id) + { + RestGroupUser user; + if (_users.TryGetValue(id, out user)) + return user; + return null; + } + + public Task GetMessageAsync(ulong id) + => ChannelHelper.GetMessageAsync(this, Discord, id); + public IAsyncEnumerable> GetMessagesAsync(int limit = DiscordConfig.MaxMessagesPerBatch) + => ChannelHelper.GetMessagesAsync(this, Discord, limit: limit); + public IAsyncEnumerable> GetMessagesAsync(ulong fromMessageId, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch) + => ChannelHelper.GetMessagesAsync(this, Discord, fromMessageId, dir, limit); + public IAsyncEnumerable> GetMessagesAsync(IMessage fromMessage, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch) + => ChannelHelper.GetMessagesAsync(this, Discord, fromMessage.Id, dir, limit); + public Task> GetPinnedMessagesAsync() + => ChannelHelper.GetPinnedMessagesAsync(this, Discord); + + public Task SendMessageAsync(string text, bool isTTS) + => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS); + public Task SendFileAsync(string filePath, string text, bool isTTS) + => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS); + public Task SendFileAsync(Stream stream, string filename, string text, bool isTTS) + => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS); + + public Task DeleteMessagesAsync(IEnumerable messages) + => ChannelHelper.DeleteMessagesAsync(this, Discord, messages); + + public IDisposable EnterTypingState() + => ChannelHelper.EnterTypingState(this, Discord); + + //IPrivateChannel + IReadOnlyCollection IPrivateChannel.Recipients => Recipients; + + //IMessageChannel + IReadOnlyCollection IMessageChannel.CachedMessages => ImmutableArray.Create(); + + IMessage IMessageChannel.GetCachedMessage(ulong id) + => null; + async Task IMessageChannel.GetMessageAsync(ulong id) + => await GetMessageAsync(id); + IAsyncEnumerable> IMessageChannel.GetMessagesAsync(int limit) + => GetMessagesAsync(limit); + IAsyncEnumerable> IMessageChannel.GetMessagesAsync(ulong fromMessageId, Direction dir, int limit) + => GetMessagesAsync(fromMessageId, dir, limit); + async Task> IMessageChannel.GetPinnedMessagesAsync() + => await GetPinnedMessagesAsync(); + + async Task IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS) + => await SendFileAsync(filePath, text, isTTS); + async Task IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS) + => await SendFileAsync(stream, filename, text, isTTS); + async Task IMessageChannel.SendMessageAsync(string text, bool isTTS) + => await SendMessageAsync(text, isTTS); + IDisposable IMessageChannel.EnterTypingState() + => EnterTypingState(); + + //IChannel + IReadOnlyCollection IChannel.CachedUsers => Users; + + IUser IChannel.GetCachedUser(ulong id) + => GetUser(id); + Task IChannel.GetUserAsync(ulong id) + => Task.FromResult(GetUser(id)); + IAsyncEnumerable> IChannel.GetUsersAsync() + => ImmutableArray.Create>(Users).ToAsyncEnumerable(); + } +} diff --git a/src/Discord.Net.Rest/Entities/Channels/RestGuildChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RestGuildChannel.cs new file mode 100644 index 000000000..62422efe1 --- /dev/null +++ b/src/Discord.Net.Rest/Entities/Channels/RestGuildChannel.cs @@ -0,0 +1,158 @@ +using Discord.API.Rest; +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Linq; +using System.Threading.Tasks; +using Model = Discord.API.Channel; + +namespace Discord.Rest +{ + [DebuggerDisplay(@"{DebuggerDisplay,nq}")] + public abstract class RestGuildChannel : RestEntity, IGuildChannel, IUpdateable + { + private ImmutableArray _overwrites; + + public IReadOnlyCollection PermissionOverwrites => _overwrites; + + public ulong GuildId { get; } + + public string Name { get; private set; } + public int Position { get; private set; } + + internal RestGuildChannel(DiscordRestClient discord, ulong id, ulong guildId) + : base(discord, id) + { + GuildId = guildId; + } + internal static RestGuildChannel Create(DiscordRestClient discord, Model model) + { + switch (model.Type) + { + case ChannelType.Text: + return RestTextChannel.Create(discord, model); + case ChannelType.Voice: + return RestVoiceChannel.Create(discord, model); + default: + throw new InvalidOperationException("Unknown guild channel type"); + } + } + internal virtual void Update(Model model) + { + Name = model.Name.Value; + Position = model.Position.Value; + + var overwrites = model.PermissionOverwrites.Value; + var newOverwrites = ImmutableArray.CreateBuilder(overwrites.Length); + for (int i = 0; i < overwrites.Length; i++) + newOverwrites.Add(new Overwrite(overwrites[i])); + _overwrites = newOverwrites.ToImmutable(); + } + + public async Task UpdateAsync() + => Update(await ChannelHelper.GetAsync(this, Discord)); + public Task ModifyAsync(Action func) + => ChannelHelper.ModifyAsync(this, Discord, func); + public Task DeleteAsync() + => ChannelHelper.DeleteAsync(this, Discord); + + public OverwritePermissions? GetPermissionOverwrite(IUser user) + { + for (int i = 0; i < _overwrites.Length; i++) + { + if (_overwrites[i].TargetId == user.Id) + return _overwrites[i].Permissions; + } + return null; + } + public OverwritePermissions? GetPermissionOverwrite(IRole role) + { + for (int i = 0; i < _overwrites.Length; i++) + { + if (_overwrites[i].TargetId == role.Id) + return _overwrites[i].Permissions; + } + return null; + } + public async Task AddPermissionOverwriteAsync(IUser user, OverwritePermissions perms) + { + await ChannelHelper.AddPermissionOverwriteAsync(this, Discord, user, perms).ConfigureAwait(false); + _overwrites = _overwrites.Add(new Overwrite(new API.Overwrite { Allow = perms.AllowValue, Deny = perms.DenyValue, TargetId = user.Id, TargetType = PermissionTarget.User })); + } + public async Task AddPermissionOverwriteAsync(IRole role, OverwritePermissions perms) + { + await ChannelHelper.AddPermissionOverwriteAsync(this, Discord, role, perms).ConfigureAwait(false); + _overwrites.Add(new Overwrite(new API.Overwrite { Allow = perms.AllowValue, Deny = perms.DenyValue, TargetId = role.Id, TargetType = PermissionTarget.Role })); + } + public async Task RemovePermissionOverwriteAsync(IUser user) + { + await ChannelHelper.RemovePermissionOverwriteAsync(this, Discord, user).ConfigureAwait(false); + + for (int i = 0; i < _overwrites.Length; i++) + { + if (_overwrites[i].TargetId == user.Id) + { + _overwrites = _overwrites.RemoveAt(i); + return; + } + } + } + public async Task RemovePermissionOverwriteAsync(IRole role) + { + await ChannelHelper.RemovePermissionOverwriteAsync(this, Discord, role).ConfigureAwait(false); + + for (int i = 0; i < _overwrites.Length; i++) + { + if (_overwrites[i].TargetId == role.Id) + { + _overwrites = _overwrites.RemoveAt(i); + return; + } + } + } + + public async Task> GetInvitesAsync() + => await ChannelHelper.GetInvitesAsync(this, Discord); + public async Task CreateInviteAsync(int? maxAge = 3600, int? maxUses = null, bool isTemporary = true) + => await ChannelHelper.CreateInviteAsync(this, Discord, maxAge, maxUses, isTemporary); + + //IGuildChannel + async Task> IGuildChannel.GetInvitesAsync() + => await GetInvitesAsync(); + async Task IGuildChannel.CreateInviteAsync(int? maxAge, int? maxUses, bool isTemporary) + => await CreateInviteAsync(maxAge, maxUses, isTemporary); + + OverwritePermissions? IGuildChannel.GetPermissionOverwrite(IRole role) + => GetPermissionOverwrite(role); + OverwritePermissions? IGuildChannel.GetPermissionOverwrite(IUser user) + => GetPermissionOverwrite(user); + async Task IGuildChannel.AddPermissionOverwriteAsync(IRole role, OverwritePermissions permissions) + => await AddPermissionOverwriteAsync(role, permissions); + async Task IGuildChannel.AddPermissionOverwriteAsync(IUser user, OverwritePermissions permissions) + => await AddPermissionOverwriteAsync(user, permissions); + async Task IGuildChannel.RemovePermissionOverwriteAsync(IRole role) + => await RemovePermissionOverwriteAsync(role); + async Task IGuildChannel.RemovePermissionOverwriteAsync(IUser user) + => await RemovePermissionOverwriteAsync(user); + + IReadOnlyCollection IGuildChannel.CachedUsers + => ImmutableArray.Create(); + IAsyncEnumerable> IGuildChannel.GetUsersAsync() + => ImmutableArray.Create>().ToAsyncEnumerable(); //Overriden in Text/Voice //TODO: Does this actually override? + Task IGuildChannel.GetUserAsync(ulong id) + => Task.FromResult(null); //Overriden in Text/Voice //TODO: Does this actually override? + IGuildUser IGuildChannel.GetCachedUser(ulong id) + => null; + + //IChannel + IReadOnlyCollection IChannel.CachedUsers + => ImmutableArray.Create(); + IUser IChannel.GetCachedUser(ulong id) + => null; + IAsyncEnumerable> IChannel.GetUsersAsync() + => ImmutableArray.Create>().ToAsyncEnumerable(); //Overriden in Text/Voice //TODO: Does this actually override? + Task IChannel.GetUserAsync(ulong id) + => Task.FromResult(null); //Overriden in Text/Voice //TODO: Does this actually override? + } +} diff --git a/src/Discord.Net.Rest/Entities/Channels/RestTextChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RestTextChannel.cs new file mode 100644 index 000000000..65877868c --- /dev/null +++ b/src/Discord.Net.Rest/Entities/Channels/RestTextChannel.cs @@ -0,0 +1,98 @@ +using Discord.API.Rest; +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics; +using System.IO; +using System.Threading.Tasks; +using Model = Discord.API.Channel; + +namespace Discord.Rest +{ + [DebuggerDisplay(@"{DebuggerDisplay,nq}")] + public class RestTextChannel : RestGuildChannel, ITextChannel + { + public string Topic { get; private set; } + + public string Mention => MentionUtils.MentionChannel(Id); + + internal RestTextChannel(DiscordRestClient discord, ulong id, ulong guildId) + : base(discord, id, guildId) + { + } + internal new static RestTextChannel Create(DiscordRestClient discord, Model model) + { + var entity = new RestTextChannel(discord, model.Id, model.GuildId.Value); + entity.Update(model); + return entity; + } + internal override void Update(Model model) + { + base.Update(model); + + Topic = model.Topic.Value; + } + + + public Task ModifyAsync(Action func) + => ChannelHelper.ModifyAsync(this, Discord, func); + + public Task GetUserAsync(ulong id) + => ChannelHelper.GetUserAsync(this, Discord, id); + public IAsyncEnumerable> GetUsersAsync() + => ChannelHelper.GetUsersAsync(this, Discord); + + public Task GetMessageAsync(ulong id) + => ChannelHelper.GetMessageAsync(this, Discord, id); + public IAsyncEnumerable> GetMessagesAsync(int limit = DiscordConfig.MaxMessagesPerBatch) + => ChannelHelper.GetMessagesAsync(this, Discord, limit: limit); + public IAsyncEnumerable> GetMessagesAsync(ulong fromMessageId, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch) + => ChannelHelper.GetMessagesAsync(this, Discord, fromMessageId, dir, limit); + public IAsyncEnumerable> GetMessagesAsync(IMessage fromMessage, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch) + => ChannelHelper.GetMessagesAsync(this, Discord, fromMessage.Id, dir, limit); + public Task> GetPinnedMessagesAsync() + => ChannelHelper.GetPinnedMessagesAsync(this, Discord); + + public Task SendMessageAsync(string text, bool isTTS) + => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS); + public Task SendFileAsync(string filePath, string text, bool isTTS) + => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS); + public Task SendFileAsync(Stream stream, string filename, string text, bool isTTS) + => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS); + + public Task DeleteMessagesAsync(IEnumerable messages) + => ChannelHelper.DeleteMessagesAsync(this, Discord, messages); + + public IDisposable EnterTypingState() + => ChannelHelper.EnterTypingState(this, Discord); + + //IGuildChannel + async Task IGuildChannel.GetUserAsync(ulong id) + => await GetUserAsync(id); + IAsyncEnumerable> IGuildChannel.GetUsersAsync() + => GetUsersAsync(); + + //IMessageChannel + IReadOnlyCollection IMessageChannel.CachedMessages + => ImmutableArray.Create(); + IMessage IMessageChannel.GetCachedMessage(ulong id) + => null; + async Task IMessageChannel.GetMessageAsync(ulong id) + => await GetMessageAsync(id); + IAsyncEnumerable> IMessageChannel.GetMessagesAsync(int limit) + => GetMessagesAsync(limit); + IAsyncEnumerable> IMessageChannel.GetMessagesAsync(ulong fromMessageId, Direction dir, int limit) + => GetMessagesAsync(fromMessageId, dir, limit); + async Task> IMessageChannel.GetPinnedMessagesAsync() + => await GetPinnedMessagesAsync(); + + async Task IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS) + => await SendFileAsync(filePath, text, isTTS); + async Task IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS) + => await SendFileAsync(stream, filename, text, isTTS); + async Task IMessageChannel.SendMessageAsync(string text, bool isTTS) + => await SendMessageAsync(text, isTTS); + IDisposable IMessageChannel.EnterTypingState() + => EnterTypingState(); + } +} diff --git a/src/Discord.Net.Rest/Entities/Channels/RestVoiceChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RestVoiceChannel.cs new file mode 100644 index 000000000..a3187ff12 --- /dev/null +++ b/src/Discord.Net.Rest/Entities/Channels/RestVoiceChannel.cs @@ -0,0 +1,49 @@ +using Discord.API.Rest; +using Discord.Audio; +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Linq; +using System.Threading.Tasks; +using Model = Discord.API.Channel; + +namespace Discord.Rest +{ + [DebuggerDisplay(@"{DebuggerDisplay,nq}")] + public class RestVoiceChannel : RestGuildChannel, IVoiceChannel + { + public int Bitrate { get; private set; } + public int UserLimit { get; private set; } + + internal RestVoiceChannel(DiscordRestClient discord, ulong id, ulong guildId) + : base(discord, id, guildId) + { + } + internal new static RestVoiceChannel Create(DiscordRestClient discord, Model model) + { + var entity = new RestVoiceChannel(discord, model.Id, model.GuildId.Value); + entity.Update(model); + return entity; + } + internal override void Update(Model model) + { + base.Update(model); + + Bitrate = model.Bitrate.Value; + UserLimit = model.UserLimit.Value; + } + + public Task ModifyAsync(Action func) + => ChannelHelper.ModifyAsync(this, Discord, func); + + //IVoiceChannel + Task IVoiceChannel.ConnectAsync() { throw new NotSupportedException(); } + + //IGuildChannel + Task IGuildChannel.GetUserAsync(ulong id) + => Task.FromResult(null); + IAsyncEnumerable> IGuildChannel.GetUsersAsync() + => ImmutableArray.Create>().ToAsyncEnumerable(); + } +} diff --git a/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs b/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs new file mode 100644 index 000000000..02fb76868 --- /dev/null +++ b/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs @@ -0,0 +1,183 @@ +using Discord.API.Rest; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Model = Discord.API.Guild; +using EmbedModel = Discord.API.GuildEmbed; +using RoleModel = Discord.API.Role; +using System.Linq; +using System.Collections.Immutable; + +namespace Discord.Rest +{ + internal static class GuildHelper + { + //General + public static async Task ModifyAsync(IGuild guild, DiscordRestClient client, + Action func) + { + if (func == null) throw new NullReferenceException(nameof(func)); + + var args = new ModifyGuildParams(); + func(args); + + if (args.Splash.IsSpecified && guild.SplashId != null) + args.Splash = new API.Image(guild.SplashId); + if (args.Icon.IsSpecified && guild.IconId != null) + args.Icon = new API.Image(guild.IconId); + + return await client.ApiClient.ModifyGuildAsync(guild.Id, args).ConfigureAwait(false); + } + public static async Task ModifyEmbedAsync(IGuild guild, DiscordRestClient client, + Action func) + { + if (func == null) throw new NullReferenceException(nameof(func)); + + var args = new ModifyGuildEmbedParams(); + func(args); + return await client.ApiClient.ModifyGuildEmbedAsync(guild.Id, args).ConfigureAwait(false); + } + public static async Task ModifyChannelsAsync(IGuild guild, DiscordRestClient client, + IEnumerable args) + { + await client.ApiClient.ModifyGuildChannelsAsync(guild.Id, args).ConfigureAwait(false); + } + public static async Task> ModifyRolesAsync(IGuild guild, DiscordRestClient client, + IEnumerable args) + { + return await client.ApiClient.ModifyGuildRolesAsync(guild.Id, args).ConfigureAwait(false); + } + public static async Task LeaveAsync(IGuild guild, DiscordRestClient client) + { + await client.ApiClient.LeaveGuildAsync(guild.Id).ConfigureAwait(false); + } + public static async Task DeleteAsync(IGuild guild, DiscordRestClient client) + { + await client.ApiClient.DeleteGuildAsync(guild.Id).ConfigureAwait(false); + } + + //Bans + public static async Task> GetBansAsync(IGuild guild, DiscordRestClient client) + { + var models = await client.ApiClient.GetGuildBansAsync(guild.Id); + return models.Select(x => RestBan.Create(client, x)).ToImmutableArray(); + } + + public static async Task AddBanAsync(IGuild guild, DiscordRestClient client, + ulong userId, int pruneDays) + { + var args = new CreateGuildBanParams { DeleteMessageDays = pruneDays }; + await client.ApiClient.CreateGuildBanAsync(guild.Id, userId, args); + } + public static async Task RemoveBanAsync(IGuild guild, DiscordRestClient client, + ulong userId) + { + await client.ApiClient.RemoveGuildBanAsync(guild.Id, userId); + } + + //Channels + public static async Task GetChannelAsync(IGuild guild, DiscordRestClient client, + ulong id) + { + var model = await client.ApiClient.GetChannelAsync(guild.Id, id).ConfigureAwait(false); + if (model != null) + return RestGuildChannel.Create(client, model); + return null; + } + public static async Task> GetChannelsAsync(IGuild guild, DiscordRestClient client) + { + var models = await client.ApiClient.GetGuildChannelsAsync(guild.Id).ConfigureAwait(false); + return models.Select(x => RestGuildChannel.Create(client, x)).ToImmutableArray(); + } + public static async Task CreateTextChannelAsync(IGuild guild, DiscordRestClient client, + string name) + { + if (name == null) throw new ArgumentNullException(nameof(name)); + + var args = new CreateGuildChannelParams(name, ChannelType.Text); + var model = await client.ApiClient.CreateGuildChannelAsync(guild.Id, args).ConfigureAwait(false); + return RestTextChannel.Create(client, model); + } + public static async Task CreateVoiceChannelAsync(IGuild guild, DiscordRestClient client, + string name) + { + if (name == null) throw new ArgumentNullException(nameof(name)); + + var args = new CreateGuildChannelParams(name, ChannelType.Voice); + var model = await client.ApiClient.CreateGuildChannelAsync(guild.Id, args).ConfigureAwait(false); + return RestVoiceChannel.Create(client, model); + } + + //Integrations + public static async Task> GetIntegrationsAsync(IGuild guild, DiscordRestClient client) + { + var models = await client.ApiClient.GetGuildIntegrationsAsync(guild.Id).ConfigureAwait(false); + return models.Select(x => RestGuildIntegration.Create(client, x)).ToImmutableArray(); + } + public static async Task CreateIntegrationAsync(IGuild guild, DiscordRestClient client, + ulong id, string type) + { + var args = new CreateGuildIntegrationParams(id, type); + var model = await client.ApiClient.CreateGuildIntegrationAsync(guild.Id, args).ConfigureAwait(false); + return RestGuildIntegration.Create(client, model); + } + + //Invites + public static async Task> GetInvitesAsync(IGuild guild, DiscordRestClient client) + { + var models = await client.ApiClient.GetGuildInvitesAsync(guild.Id).ConfigureAwait(false); + return models.Select(x => RestInviteMetadata.Create(client, x)).ToImmutableArray(); + } + + //Roles + public static async Task CreateRoleAsync(IGuild guild, DiscordRestClient client, + string name, GuildPermissions? permissions = null, Color? color = null, bool isHoisted = false) + { + if (name == null) throw new ArgumentNullException(nameof(name)); + + var model = await client.ApiClient.CreateGuildRoleAsync(guild.Id).ConfigureAwait(false); + var role = RestRole.Create(client, model); + + await role.ModifyAsync(x => + { + x.Name = name; + x.Permissions = (permissions ?? role.Permissions).RawValue; + x.Color = (color ?? Color.Default).RawValue; + x.Hoist = isHoisted; + }).ConfigureAwait(false); + + return role; + } + + //Users + public static async Task GetUserAsync(IGuild guild, DiscordRestClient client, + ulong id) + { + var model = await client.ApiClient.GetGuildMemberAsync(guild.Id, id).ConfigureAwait(false); + if (model != null) + return RestGuildUser.Create(client, model); + return null; + } + public static async Task GetCurrentUserAsync(IGuild guild, DiscordRestClient client) + { + return await GetUserAsync(guild, client, client.CurrentUser.Id).ConfigureAwait(false); + } + public static async Task> GetUsersAsync(IGuild guild, DiscordRestClient client) + { + var args = new GetGuildMembersParams(); + var models = await client.ApiClient.GetGuildMembersAsync(guild.Id, args).ConfigureAwait(false); + return models.Select(x => RestGuildUser.Create(client, x)).ToImmutableArray(); + } + public static async Task PruneUsersAsync(IGuild guild, DiscordRestClient client, + int days = 30, bool simulate = false) + { + var args = new GuildPruneParams(days); + GetGuildPruneCountResponse model; + if (simulate) + model = await client.ApiClient.GetGuildPruneCountAsync(guild.Id, args).ConfigureAwait(false); + else + model = await client.ApiClient.BeginGuildPruneAsync(guild.Id, args).ConfigureAwait(false); + return model.Pruned; + } + } +} diff --git a/src/Discord.Net.Rest/Entities/Guilds/RestBan.cs b/src/Discord.Net.Rest/Entities/Guilds/RestBan.cs new file mode 100644 index 000000000..8e2b65576 --- /dev/null +++ b/src/Discord.Net.Rest/Entities/Guilds/RestBan.cs @@ -0,0 +1,27 @@ +using System.Diagnostics; +using Model = Discord.API.Ban; + +namespace Discord.Rest +{ + [DebuggerDisplay(@"{DebuggerDisplay,nq}")] + public class RestBan : IBan + { + public RestUser User { get; } + public string Reason { get; } + + internal RestBan(RestUser user, string reason) + { + User = user; + Reason = reason; + } + internal static RestBan Create(DiscordRestClient client, Model model) + { + return new RestBan(RestUser.Create(client, model.User), model.Reason); + } + + public override string ToString() => User.ToString(); + private string DebuggerDisplay => $"{User}: {Reason}"; + + IUser IBan.User => User; + } +} diff --git a/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs b/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs new file mode 100644 index 000000000..4735debf9 --- /dev/null +++ b/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs @@ -0,0 +1,212 @@ +using System.Diagnostics; +using System.Collections.Immutable; +using Discord.Audio; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Discord.API.Rest; +using Model = Discord.API.Guild; + +namespace Discord.Rest +{ + [DebuggerDisplay(@"{DebuggerDisplay,nq}")] + public class RestGuild : RestEntity, IGuild, IUpdateable + { + private ImmutableDictionary _roles; + private ImmutableArray _emojis; + private ImmutableArray _features; + + public string Name { get; private set; } + public int AFKTimeout { get; private set; } + public bool IsEmbeddable { get; private set; } + public VerificationLevel VerificationLevel { get; private set; } + public MfaLevel MfaLevel { get; private set; } + public DefaultMessageNotifications DefaultMessageNotifications { get; private set; } + + public ulong? AFKChannelId { get; private set; } + public ulong? EmbedChannelId { get; private set; } + public ulong OwnerId { get; private set; } + public string VoiceRegionId { get; private set; } + public string IconId { get; private set; } + public string SplashId { get; private set; } + + public ulong DefaultChannelId => Id; + public string IconUrl => API.CDN.GetGuildIconUrl(Id, IconId); + public string SplashUrl => API.CDN.GetGuildSplashUrl(Id, SplashId); + + public RestRole EveryoneRole => GetRole(Id); + public IReadOnlyCollection Roles => _roles.ToReadOnlyCollection(); + public IReadOnlyCollection Emojis => _emojis; + public IReadOnlyCollection Features => _features; + + internal RestGuild(DiscordRestClient client, ulong id) + : base(client, id) + { + } + internal static RestGuild Create(DiscordRestClient discord, Model model) + { + var entity = new RestGuild(discord, model.Id); + entity.Update(model); + return entity; + } + internal void Update(Model model) + { + AFKChannelId = model.AFKChannelId; + EmbedChannelId = model.EmbedChannelId; + AFKTimeout = model.AFKTimeout; + IsEmbeddable = model.EmbedEnabled; + IconId = model.Icon; + Name = model.Name; + OwnerId = model.OwnerId; + VoiceRegionId = model.Region; + SplashId = model.Splash; + VerificationLevel = model.VerificationLevel; + MfaLevel = model.MfaLevel; + DefaultMessageNotifications = model.DefaultMessageNotifications; + + if (model.Emojis != null) + { + var emojis = ImmutableArray.CreateBuilder(model.Emojis.Length); + for (int i = 0; i < model.Emojis.Length; i++) + emojis.Add(Emoji.Create(model.Emojis[i])); + _emojis = emojis.ToImmutableArray(); + } + else + _emojis = ImmutableArray.Create(); + + if (model.Features != null) + _features = model.Features.ToImmutableArray(); + else + _features = ImmutableArray.Create(); + + var roles = ImmutableDictionary.CreateBuilder(); + if (model.Roles != null) + { + for (int i = 0; i < model.Roles.Length; i++) + roles[model.Roles[i].Id] = RestRole.Create(Discord, model.Roles[i]); + } + _roles = roles.ToImmutable(); + } + + //General + public async Task UpdateAsync() + => Update(await Discord.ApiClient.GetGuildAsync(Id)); + public Task DeleteAsync() + => GuildHelper.DeleteAsync(this, Discord); + + public Task ModifyAsync(Action func) + => GuildHelper.ModifyAsync(this, Discord, func); + public Task ModifyEmbedAsync(Action func) + => GuildHelper.ModifyEmbedAsync(this, Discord, func); + public Task ModifyChannelsAsync(IEnumerable args) + => GuildHelper.ModifyChannelsAsync(this, Discord, args); + public Task ModifyRolesAsync(IEnumerable args) + => GuildHelper.ModifyRolesAsync(this, Discord, args); + + public Task LeaveAsync() + => GuildHelper.LeaveAsync(this, Discord); + + //Bans + public Task> GetBansAsync() + => GuildHelper.GetBansAsync(this, Discord); + + public Task AddBanAsync(IUser user, int pruneDays = 0) + => GuildHelper.AddBanAsync(this, Discord, user.Id, pruneDays); + public Task AddBanAsync(ulong userId, int pruneDays = 0) + => GuildHelper.AddBanAsync(this, Discord, userId, pruneDays); + + public Task RemoveBanAsync(IUser user) + => GuildHelper.RemoveBanAsync(this, Discord, user.Id); + public Task RemoveBanAsync(ulong userId) + => GuildHelper.RemoveBanAsync(this, Discord, userId); + + //Channels + public Task> GetChannelsAsync() + => GuildHelper.GetChannelsAsync(this, Discord); + public Task GetChannelAsync(ulong id) + => GuildHelper.GetChannelAsync(this, Discord, id); + public Task CreateTextChannelAsync(string name) + => GuildHelper.CreateTextChannelAsync(this, Discord, name); + public Task CreateVoiceChannelAsync(string name) + => GuildHelper.CreateVoiceChannelAsync(this, Discord, name); + + //Integrations + public Task> GetIntegrationsAsync() + => GuildHelper.GetIntegrationsAsync(this, Discord); + public Task CreateIntegrationAsync(ulong id, string type) + => GuildHelper.CreateIntegrationAsync(this, Discord, id, type); + + //Invites + public Task> GetInvitesAsync() + => GuildHelper.GetInvitesAsync(this, Discord); + + //Roles + public RestRole GetRole(ulong id) + { + RestRole value; + if (_roles.TryGetValue(id, out value)) + return value; + return null; + } + + public async Task CreateRoleAsync(string name, GuildPermissions? permissions = default(GuildPermissions?), Color? color = default(Color?), bool isHoisted = false) + { + var role = await GuildHelper.CreateRoleAsync(this, Discord, name, permissions, color, isHoisted); + _roles = _roles.Add(role.Id, role); + return role; + } + + //Users + public Task> GetUsersAsync() + => GuildHelper.GetUsersAsync(this, Discord); + public Task GetUserAsync(ulong id) + => GuildHelper.GetUserAsync(this, Discord, id); + public Task GetCurrentUserAsync() + => GuildHelper.GetUserAsync(this, Discord, Discord.CurrentUser.Id); + + public Task PruneUsersAsync(int days = 30, bool simulate = false) + => GuildHelper.PruneUsersAsync(this, Discord, days, simulate); + + //IGuild + bool IGuild.Available => true; + IAudioClient IGuild.AudioClient => null; + IReadOnlyCollection IGuild.CachedUsers => ImmutableArray.Create(); + IRole IGuild.EveryoneRole => EveryoneRole; + IReadOnlyCollection IGuild.Roles => Roles; + + async Task> IGuild.GetBansAsync() + => await GetBansAsync(); + + async Task> IGuild.GetChannelsAsync() + => await GetChannelsAsync(); + async Task IGuild.GetChannelAsync(ulong id) + => await GetChannelAsync(id); + IGuildChannel IGuild.GetCachedChannel(ulong id) + => null; + async Task IGuild.CreateTextChannelAsync(string name) + => await CreateTextChannelAsync(name); + async Task IGuild.CreateVoiceChannelAsync(string name) + => await CreateVoiceChannelAsync(name); + + async Task> IGuild.GetIntegrationsAsync() + => await GetIntegrationsAsync(); + async Task IGuild.CreateIntegrationAsync(ulong id, string type) + => await CreateIntegrationAsync(id, type); + + async Task> IGuild.GetInvitesAsync() + => await GetInvitesAsync(); + + IRole IGuild.GetRole(ulong id) + => GetRole(id); + + async Task> IGuild.GetUsersAsync() + => await GetUsersAsync(); + async Task IGuild.GetUserAsync(ulong id) + => await GetUserAsync(id); + IGuildUser IGuild.GetCachedUser(ulong id) + => null; + async Task IGuild.GetCurrentUserAsync() + => await GetCurrentUserAsync(); + Task IGuild.DownloadUsersAsync() { throw new NotSupportedException(); } + } +} diff --git a/src/Discord.Net/Entities/Guilds/GuildEmbed.cs b/src/Discord.Net.Rest/Entities/Guilds/RestGuildEmbed.cs similarity index 67% rename from src/Discord.Net/Entities/Guilds/GuildEmbed.cs rename to src/Discord.Net.Rest/Entities/Guilds/RestGuildEmbed.cs index f912fb076..f26a62d8d 100644 --- a/src/Discord.Net/Entities/Guilds/GuildEmbed.cs +++ b/src/Discord.Net.Rest/Entities/Guilds/RestGuildEmbed.cs @@ -4,18 +4,20 @@ using Model = Discord.API.GuildEmbed; namespace Discord { [DebuggerDisplay(@"{DebuggerDisplay,nq}")] - public struct GuildEmbed + public struct RestGuildEmbed { public bool IsEnabled { get; private set; } public ulong? ChannelId { get; private set; } - public GuildEmbed(bool isEnabled, ulong? channelId) + internal RestGuildEmbed(bool isEnabled, ulong? channelId) { ChannelId = channelId; IsEnabled = isEnabled; } - internal GuildEmbed(Model model) - : this(model.Enabled, model.ChannelId) { } + internal static RestGuildEmbed Create(Model model) + { + return new RestGuildEmbed(model.Enabled, model.ChannelId); + } public override string ToString() => ChannelId?.ToString(); private string DebuggerDisplay => $"{ChannelId} ({(IsEnabled ? "Enabled" : "Disabled")})"; diff --git a/src/Discord.Net.Rest/Entities/Guilds/RestGuildIntegration.cs b/src/Discord.Net.Rest/Entities/Guilds/RestGuildIntegration.cs new file mode 100644 index 000000000..bbbe57a0d --- /dev/null +++ b/src/Discord.Net.Rest/Entities/Guilds/RestGuildIntegration.cs @@ -0,0 +1,77 @@ +using Discord.API.Rest; +using System; +using System.Diagnostics; +using System.Threading.Tasks; +using Model = Discord.API.Integration; + +namespace Discord.Rest +{ + [DebuggerDisplay(@"{DebuggerDisplay,nq}")] + public class RestGuildIntegration : RestEntity, IGuildIntegration + { + private long _syncedAtTicks; + + public string Name { get; private set; } + public string Type { get; private set; } + public bool IsEnabled { get; private set; } + public bool IsSyncing { get; private set; } + public ulong ExpireBehavior { get; private set; } + public ulong ExpireGracePeriod { get; private set; } + + public ulong GuildId { get; private set; } + public ulong RoleId { get; private set; } + public RestUser User { get; private set; } + public IntegrationAccount Account { get; private set; } + + public DateTimeOffset SyncedAt => DateTimeUtils.FromTicks(_syncedAtTicks); + + internal RestGuildIntegration(DiscordRestClient discord, ulong id) + : base(discord, id) + { + } + internal static RestGuildIntegration Create(DiscordRestClient discord, Model model) + { + var entity = new RestGuildIntegration(discord, model.Id); + entity.Update(model); + return entity; + } + + public void Update(Model model) + { + Name = model.Name; + Type = model.Type; + IsEnabled = model.Enabled; + IsSyncing = model.Syncing; + ExpireBehavior = model.ExpireBehavior; + ExpireGracePeriod = model.ExpireGracePeriod; + _syncedAtTicks = model.SyncedAt.UtcTicks; + + RoleId = model.RoleId; + User = RestUser.Create(Discord, model.User); + } + + public async Task DeleteAsync() + { + await Discord.ApiClient.DeleteGuildIntegrationAsync(GuildId, Id).ConfigureAwait(false); + } + public async Task ModifyAsync(Action func) + { + if (func == null) throw new NullReferenceException(nameof(func)); + + var args = new ModifyGuildIntegrationParams(); + func(args); + var model = await Discord.ApiClient.ModifyGuildIntegrationAsync(GuildId, Id, args).ConfigureAwait(false); + + Update(model); + } + public async Task SyncAsync() + { + await Discord.ApiClient.SyncGuildIntegrationAsync(GuildId, Id).ConfigureAwait(false); + } + + public override string ToString() => Name; + private string DebuggerDisplay => $"{Name} ({Id}{(IsEnabled ? ", Enabled" : "")})"; + + IUser IGuildIntegration.User => User; + } +} diff --git a/src/Discord.Net/Rest/Entities/Guilds/UserGuild.cs b/src/Discord.Net.Rest/Entities/Guilds/RestUserGuild.cs similarity index 69% rename from src/Discord.Net/Rest/Entities/Guilds/UserGuild.cs rename to src/Discord.Net.Rest/Entities/Guilds/RestUserGuild.cs index 3e8818a41..e1f51b4a9 100644 --- a/src/Discord.Net/Rest/Entities/Guilds/UserGuild.cs +++ b/src/Discord.Net.Rest/Entities/Guilds/RestUserGuild.cs @@ -1,12 +1,11 @@ -using Discord.Rest; -using System.Diagnostics; +using System.Diagnostics; using System.Threading.Tasks; using Model = Discord.API.UserGuild; namespace Discord.Rest { [DebuggerDisplay(@"{DebuggerDisplay,nq}")] - internal class UserGuild : SnowflakeEntity, IUserGuild + public class RestUserGuild : RestEntity, ISnowflakeEntity, IUserGuild { private string _iconId; @@ -14,20 +13,21 @@ namespace Discord.Rest public bool IsOwner { get; private set; } public GuildPermissions Permissions { get; private set; } - public override DiscordRestClient Discord { get; } - public string IconUrl => API.CDN.GetGuildIconUrl(Id, _iconId); - public UserGuild(DiscordRestClient discord, Model model) - : base(model.Id) + internal RestUserGuild(DiscordRestClient discord, ulong id) + : base(discord, id) { - Discord = discord; - Update(model, UpdateSource.Creation); } - public void Update(Model model, UpdateSource source) + internal static RestUserGuild Create(DiscordRestClient discord, Model model) { - if (source == UpdateSource.Rest && IsAttached) return; + var entity = new RestUserGuild(discord, model.Id); + entity.Update(model); + return entity; + } + public void Update(Model model) + { _iconId = model.Icon; IsOwner = model.Owner; Name = model.Name; diff --git a/src/Discord.Net.Rest/Entities/Guilds/RestVoiceRegion.cs b/src/Discord.Net.Rest/Entities/Guilds/RestVoiceRegion.cs new file mode 100644 index 000000000..b6e4c2a26 --- /dev/null +++ b/src/Discord.Net.Rest/Entities/Guilds/RestVoiceRegion.cs @@ -0,0 +1,38 @@ +using Discord.Rest; +using System.Diagnostics; +using Model = Discord.API.VoiceRegion; + +namespace Discord +{ + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class RestVoiceRegion : RestEntity, IVoiceRegion + { + public string Name { get; private set; } + public bool IsVip { get; private set; } + public bool IsOptimal { get; private set; } + public string SampleHostname { get; private set; } + public int SamplePort { get; private set; } + + internal RestVoiceRegion(DiscordRestClient client, string id) + : base(client, id) + { + } + internal static RestVoiceRegion Create(DiscordRestClient client, Model model) + { + var entity = new RestVoiceRegion(client, model.Id); + entity.Update(model); + return entity; + } + internal void Update(Model model) + { + Name = model.Name; + IsVip = model.IsVip; + IsOptimal = model.IsOptimal; + SampleHostname = model.SampleHostname; + SamplePort = model.SamplePort; + } + + public override string ToString() => Name; + private string DebuggerDisplay => $"{Name} ({Id}{(IsVip ? ", VIP" : "")}{(IsOptimal ? ", Optimal" : "")})"; + } +} diff --git a/src/Discord.Net.Rest/Entities/Invites/InviteHelper.cs b/src/Discord.Net.Rest/Entities/Invites/InviteHelper.cs new file mode 100644 index 000000000..40bbe3632 --- /dev/null +++ b/src/Discord.Net.Rest/Entities/Invites/InviteHelper.cs @@ -0,0 +1,21 @@ +using System.Threading.Tasks; +using Model = Discord.API.Invite; + +namespace Discord.Rest +{ + internal static class InviteHelper + { + public static async Task GetAsync(IInvite invite, DiscordRestClient client) + { + return await client.ApiClient.GetInviteAsync(invite.Code).ConfigureAwait(false); + } + public static async Task AcceptAsync(IInvite invite, DiscordRestClient client) + { + await client.ApiClient.AcceptInviteAsync(invite.Code).ConfigureAwait(false); + } + public static async Task DeleteAsync(IInvite invite, DiscordRestClient client) + { + await client.ApiClient.DeleteInviteAsync(invite.Code).ConfigureAwait(false); + } + } +} diff --git a/src/Discord.Net/Rest/Entities/Invites/Invite.cs b/src/Discord.Net.Rest/Entities/Invites/RestInvite.cs similarity index 50% rename from src/Discord.Net/Rest/Entities/Invites/Invite.cs rename to src/Discord.Net.Rest/Entities/Invites/RestInvite.cs index c69fcbad4..84af7e029 100644 --- a/src/Discord.Net/Rest/Entities/Invites/Invite.cs +++ b/src/Discord.Net.Rest/Entities/Invites/RestInvite.cs @@ -1,50 +1,50 @@ -using Discord.Rest; -using System.Diagnostics; +using System.Diagnostics; using System.Threading.Tasks; using Model = Discord.API.Invite; namespace Discord.Rest { [DebuggerDisplay(@"{DebuggerDisplay,nq}")] - internal class Invite : Entity, IInvite + public class RestInvite : RestEntity, IInvite, IUpdateable { public string ChannelName { get; private set; } public string GuildName { get; private set; } public ulong ChannelId { get; private set; } public ulong GuildId { get; private set; } - public override DiscordRestClient Discord { get; } public string Code => Id; public string Url => $"{DiscordConfig.InviteUrl}/{Code}"; - public Invite(DiscordRestClient discord, Model model) - : base(model.Code) + internal RestInvite(DiscordRestClient discord, string id) + : base(discord, id) { - Discord = discord; - - Update(model, UpdateSource.Creation); } - public void Update(Model model, UpdateSource source) + internal static RestInvite Create(DiscordRestClient discord, Model model) + { + var entity = new RestInvite(discord, model.Code); + entity.Update(model); + return entity; + } + internal void Update(Model model) { - if (source == UpdateSource.Rest && IsAttached) return; - GuildId = model.Guild.Id; ChannelId = model.Channel.Id; GuildName = model.Guild.Name; ChannelName = model.Channel.Name; } - public async Task AcceptAsync() - { - await Discord.ApiClient.AcceptInviteAsync(Code).ConfigureAwait(false); - } - public async Task DeleteAsync() - { - await Discord.ApiClient.DeleteInviteAsync(Code).ConfigureAwait(false); - } + public async Task UpdateAsync() + => Update(await InviteHelper.GetAsync(this, Discord).ConfigureAwait(false)); + public Task DeleteAsync() + => InviteHelper.DeleteAsync(this, Discord); + + public Task AcceptAsync() + => InviteHelper.AcceptAsync(this, Discord); public override string ToString() => Url; private string DebuggerDisplay => $"{Url} ({GuildName} / {ChannelName})"; + + string IEntity.Id => Code; } } diff --git a/src/Discord.Net/Rest/Entities/Invites/InviteMetadata.cs b/src/Discord.Net.Rest/Entities/Invites/RestInviteMetadata.cs similarity index 50% rename from src/Discord.Net/Rest/Entities/Invites/InviteMetadata.cs rename to src/Discord.Net.Rest/Entities/Invites/RestInviteMetadata.cs index 05a10514c..138fd6f66 100644 --- a/src/Discord.Net/Rest/Entities/Invites/InviteMetadata.cs +++ b/src/Discord.Net.Rest/Entities/Invites/RestInviteMetadata.cs @@ -1,10 +1,11 @@ -using Discord.Rest; -using System; +using System; +using System.Diagnostics; using Model = Discord.API.InviteMetadata; namespace Discord.Rest { - internal class InviteMetadata : Invite, IInviteMetadata + [DebuggerDisplay(@"{DebuggerDisplay,nq}")] + public class RestInviteMetadata : RestInvite, IInviteMetadata { private long _createdAtTicks; @@ -13,20 +14,24 @@ namespace Discord.Rest public int? MaxAge { get; private set; } public int? MaxUses { get; private set; } public int Uses { get; private set; } - public IUser Inviter { get; private set; } + public RestUser Inviter { get; private set; } public DateTimeOffset CreatedAt => DateTimeUtils.FromTicks(_createdAtTicks); - public InviteMetadata(DiscordRestClient client, Model model) - : base(client, model) + internal RestInviteMetadata(DiscordRestClient discord, string id) + : base(discord, id) { - Update(model, UpdateSource.Creation); } - public void Update(Model model, UpdateSource source) + internal static RestInviteMetadata Create(DiscordRestClient discord, Model model) { - if (source == UpdateSource.Rest && IsAttached) return; - - Inviter = new User(model.Inviter); + var entity = new RestInviteMetadata(discord, model.Code); + entity.Update(model); + return entity; + } + internal void Update(Model model) + { + base.Update(model); + Inviter = RestUser.Create(Discord, model.Inviter); IsRevoked = model.Revoked; IsTemporary = model.Temporary; MaxAge = model.MaxAge != 0 ? model.MaxAge : (int?)null; @@ -34,5 +39,7 @@ namespace Discord.Rest Uses = model.Uses; _createdAtTicks = model.CreatedAt.UtcTicks; } + + IUser IInviteMetadata.Inviter => Inviter; } } diff --git a/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs b/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs new file mode 100644 index 000000000..54f0982b1 --- /dev/null +++ b/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs @@ -0,0 +1,33 @@ +using Discord.API.Rest; +using System; +using System.Threading.Tasks; + +namespace Discord.Rest +{ + internal static class MessageHelper + { + public static async Task GetAsync(IMessage msg, DiscordRestClient client) + { + await client.ApiClient.GetChannelMessageAsync(msg.ChannelId, msg.Id); + } + public static async Task ModifyAsync(IMessage msg, DiscordRestClient client, Action func) + { + var args = new ModifyMessageParams(); + func(args); + await client.ApiClient.ModifyMessageAsync(msg.ChannelId, msg.Id, args); + } + public static async Task DeleteAsync(IMessage msg, DiscordRestClient client) + { + await client.ApiClient.DeleteMessageAsync(msg.ChannelId, msg.Id); + } + + public static async Task PinAsync(IMessage msg, DiscordRestClient client) + { + await client.ApiClient.AddPinAsync(msg.ChannelId, msg.Id); + } + public static async Task UnpinAsync(IMessage msg, DiscordRestClient client) + { + await client.ApiClient.RemovePinAsync(msg.ChannelId, msg.Id); + } + } +} diff --git a/src/Discord.Net.Rest/Entities/Messages/RestAttachment.cs b/src/Discord.Net.Rest/Entities/Messages/RestAttachment.cs new file mode 100644 index 000000000..c22bf4504 --- /dev/null +++ b/src/Discord.Net.Rest/Entities/Messages/RestAttachment.cs @@ -0,0 +1,32 @@ +using Model = Discord.API.Attachment; + +namespace Discord +{ + public class RestAttachment : IAttachment + { + public ulong Id { get; } + public string Filename { get; } + public string Url { get; } + public string ProxyUrl { get; } + public int Size { get; } + public int? Height { get; } + public int? Width { get; } + + internal RestAttachment(ulong id, string filename, string url, string proxyUrl, int size, int? height, int? width) + { + Id = id; + Filename = filename; + Url = url; + ProxyUrl = proxyUrl; + Size = size; + Height = height; + Width = width; + } + internal static RestAttachment Create(Model model) + { + return new RestAttachment(model.Id, model.Filename, model.Url, model.ProxyUrl, model.Size, + model.Height.IsSpecified ? model.Height.Value : (int?)null, + model.Width.IsSpecified ? model.Width.Value : (int?)null); + } + } +} diff --git a/src/Discord.Net.Rest/Entities/Messages/RestEmbed.cs b/src/Discord.Net.Rest/Entities/Messages/RestEmbed.cs new file mode 100644 index 000000000..bc7b09faf --- /dev/null +++ b/src/Discord.Net.Rest/Entities/Messages/RestEmbed.cs @@ -0,0 +1,30 @@ +using Model = Discord.API.Embed; + +namespace Discord +{ + public class RestEmbed : IEmbed + { + public string Description { get; } + public string Url { get; } + public string Title { get; } + public string Type { get; } + public EmbedProvider? Provider { get; } + public EmbedThumbnail? Thumbnail { get; } + + internal RestEmbed(string type, string title, string description, string url, EmbedProvider? provider, EmbedThumbnail? thumbnail) + { + Type = type; + Title = title; + Description = description; + Url = url; + Provider = provider; + Thumbnail = thumbnail; + } + internal static RestEmbed Create(Model model) + { + return new RestEmbed(model.Type, model.Title, model.Description, model.Url, + model.Provider.IsSpecified ? EmbedProvider.Create(model.Provider.Value) : (EmbedProvider?)null, + model.Thumbnail.IsSpecified ? EmbedThumbnail.Create(model.Thumbnail.Value) : (EmbedThumbnail?)null); + } + } +} diff --git a/src/Discord.Net.Rest/Entities/Messages/RestMessage.cs b/src/Discord.Net.Rest/Entities/Messages/RestMessage.cs new file mode 100644 index 000000000..58ab4c80b --- /dev/null +++ b/src/Discord.Net.Rest/Entities/Messages/RestMessage.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Threading.Tasks; +using Model = Discord.API.Message; + +namespace Discord.Rest +{ + [DebuggerDisplay(@"{DebuggerDisplay,nq}")] + public abstract class RestMessage : RestEntity, IMessage, IUpdateable + { + private long _timestampTicks; + + public ulong ChannelId { get; } + public IUser Author { get; } + + public string Content { get; private set; } + + public virtual bool IsTTS => false; + public virtual bool IsPinned => false; + public virtual DateTimeOffset? EditedTimestamp => null; + + public virtual IReadOnlyCollection Attachments => ImmutableArray.Create(); + public virtual IReadOnlyCollection Embeds => ImmutableArray.Create(); + public virtual IReadOnlyCollection MentionedChannelIds => ImmutableArray.Create(); + public virtual IReadOnlyCollection MentionedRoles => ImmutableArray.Create(); + public virtual IReadOnlyCollection MentionedUsers => ImmutableArray.Create(); + + public DateTimeOffset Timestamp => DateTimeUtils.FromTicks(_timestampTicks); + + internal RestMessage(DiscordRestClient discord, ulong id, ulong channelId) + : base(discord, id) + { + ChannelId = channelId; + } + internal static RestMessage Create(DiscordRestClient discord, Model model) + { + if (model.Type == MessageType.Default) + return RestUserMessage.Create(discord, model); + else + return RestSystemMessage.Create(discord, model); + } + internal virtual void Update(Model model) + { + if (model.Timestamp.IsSpecified) + _timestampTicks = model.Timestamp.Value.UtcTicks; + + if (model.Content.IsSpecified) + Content = model.Content.Value; + } + + public async Task UpdateAsync() + { + var model = await Discord.ApiClient.GetChannelMessageAsync(ChannelId, Id).ConfigureAwait(false); + Update(model); + } + + MessageType IMessage.Type => MessageType.Default; + } +} diff --git a/src/Discord.Net.Rest/Entities/Messages/RestSystemMessage.cs b/src/Discord.Net.Rest/Entities/Messages/RestSystemMessage.cs new file mode 100644 index 000000000..3b805696f --- /dev/null +++ b/src/Discord.Net.Rest/Entities/Messages/RestSystemMessage.cs @@ -0,0 +1,28 @@ +using System.Diagnostics; +using Model = Discord.API.Message; + +namespace Discord.Rest +{ + [DebuggerDisplay(@"{DebuggerDisplay,nq}")] + public class RestSystemMessage : RestMessage, ISystemMessage + { + public MessageType Type { get; private set; } + + internal RestSystemMessage(DiscordRestClient discord, ulong id, ulong channelId) + : base(discord, id, channelId) + { + } + internal new static RestSystemMessage Create(DiscordRestClient discord, Model model) + { + var entity = new RestSystemMessage(discord, model.Id, model.ChannelId); + entity.Update(model); + return entity; + } + internal override void Update(Model model) + { + base.Update(model); + + Type = model.Type; + } + } +} diff --git a/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs b/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs new file mode 100644 index 000000000..4db6cc372 --- /dev/null +++ b/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs @@ -0,0 +1,135 @@ +using Discord.API.Rest; +using System; +using System.Diagnostics; +using System.Threading.Tasks; +using Model = Discord.API.Message; +using Discord.API; +using System.Collections.Generic; +using System.Collections.Immutable; + +namespace Discord.Rest +{ + [DebuggerDisplay(@"{DebuggerDisplay,nq}")] + public class RestUserMessage : RestMessage, IUserMessage + { + private bool _isMentioningEveryone, _isTTS, _isPinned; + private long? _editedTimestampTicks; + private ImmutableArray _attachments; + private ImmutableArray _embeds; + private ImmutableArray _mentionedChannelIds; + private ImmutableArray _mentionedRoles; + private ImmutableArray _mentionedUsers; + + public override bool IsTTS => _isTTS; + public override bool IsPinned => _isPinned; + public override DateTimeOffset? EditedTimestamp => DateTimeUtils.FromTicks(_editedTimestampTicks); + + public override IReadOnlyCollection Attachments => _attachments; + public override IReadOnlyCollection Embeds => _embeds; + public override IReadOnlyCollection MentionedChannelIds => _mentionedChannelIds; + public override IReadOnlyCollection MentionedRoles => _mentionedRoles; + public override IReadOnlyCollection MentionedUsers => _mentionedUsers; + + internal RestUserMessage(DiscordRestClient discord, ulong id, ulong channelId) + : base(discord, id, channelId) + { + } + internal new static RestUserMessage Create(DiscordRestClient discord, Model model) + { + var entity = new RestUserMessage(discord, model.Id, model.ChannelId); + entity.Update(model); + return entity; + } + + internal override void Update(Model model) + { + base.Update(model); + + if (model.IsTextToSpeech.IsSpecified) + _isTTS = model.IsTextToSpeech.Value; + if (model.Pinned.IsSpecified) + _isPinned = model.Pinned.Value; + if (model.EditedTimestamp.IsSpecified) + _editedTimestampTicks = model.EditedTimestamp.Value?.UtcTicks; + if (model.MentionEveryone.IsSpecified) + _isMentioningEveryone = model.MentionEveryone.Value; + + if (model.Attachments.IsSpecified) + { + var value = model.Attachments.Value; + if (value.Length > 0) + { + var attachments = ImmutableArray.CreateBuilder(value.Length); + for (int i = 0; i < value.Length; i++) + attachments.Add(RestAttachment.Create(value[i])); + _attachments = attachments.ToImmutable(); + } + else + _attachments = ImmutableArray.Create(); + } + + if (model.Embeds.IsSpecified) + { + var value = model.Embeds.Value; + if (value.Length > 0) + { + var embeds = ImmutableArray.CreateBuilder(value.Length); + for (int i = 0; i < value.Length; i++) + embeds.Add(RestEmbed.Create(value[i])); + _embeds = embeds.ToImmutable(); + } + else + _embeds = ImmutableArray.Create(); + } + + ImmutableArray mentions = ImmutableArray.Create(); + if (model.Mentions.IsSpecified) + { + var value = model.Mentions.Value; + if (value.Length > 0) + { + var newMentions = ImmutableArray.CreateBuilder(value.Length); + for (int i = 0; i < value.Length; i++) + newMentions.Add(RestUser.Create(Discord, value[i])); + mentions = newMentions.ToImmutable(); + } + } + + if (model.Content.IsSpecified) + { + var text = model.Content.Value; + + _mentionedUsers = MentionsHelper.GetUserMentions(text, null, mentions); + _mentionedChannelIds = MentionsHelper.GetChannelMentions(text, null); + _mentionedRoles = MentionsHelper.GetRoleMentions(text, null); + model.Content = text; + } + } + + public Task ModifyAsync(Action func) + => MessageHelper.ModifyAsync(this, Discord, func); + public Task DeleteAsync() + => MessageHelper.DeleteAsync(this, Discord); + + public Task PinAsync() + => MessageHelper.PinAsync(this, Discord); + public Task UnpinAsync() + => MessageHelper.UnpinAsync(this, Discord); + + public string Resolve(UserMentionHandling userHandling = UserMentionHandling.Name, ChannelMentionHandling channelHandling = ChannelMentionHandling.Name, + RoleMentionHandling roleHandling = RoleMentionHandling.Name, EveryoneMentionHandling everyoneHandling = EveryoneMentionHandling.Ignore) + => Resolve(Content, userHandling, channelHandling, roleHandling, everyoneHandling); + public string Resolve(int startIndex, int length, UserMentionHandling userHandling = UserMentionHandling.Name, ChannelMentionHandling channelHandling = ChannelMentionHandling.Name, + RoleMentionHandling roleHandling = RoleMentionHandling.Name, EveryoneMentionHandling everyoneHandling = EveryoneMentionHandling.Ignore) + => Resolve(Content.Substring(startIndex, length), userHandling, channelHandling, roleHandling, everyoneHandling); + public string Resolve(string text, UserMentionHandling userHandling, ChannelMentionHandling channelHandling, + RoleMentionHandling roleHandling, EveryoneMentionHandling everyoneHandling) + { + text = MentionsHelper.ResolveUserMentions(text, null, MentionedUsers, userHandling); + text = MentionsHelper.ResolveChannelMentions(text, null, channelHandling); + text = MentionsHelper.ResolveRoleMentions(text, MentionedRoles, roleHandling); + text = MentionsHelper.ResolveEveryoneMentions(text, everyoneHandling); + return text; + } + } +} diff --git a/src/Discord.Net/Rest/Entities/Application.cs b/src/Discord.Net.Rest/Entities/RestApplication.cs similarity index 61% rename from src/Discord.Net/Rest/Entities/Application.cs rename to src/Discord.Net.Rest/Entities/RestApplication.cs index 2e743daf4..3131cc96d 100644 --- a/src/Discord.Net/Rest/Entities/Application.cs +++ b/src/Discord.Net.Rest/Entities/RestApplication.cs @@ -1,11 +1,10 @@ -using Discord.Rest; -using System; +using System; using System.Threading.Tasks; using Model = Discord.API.Application; namespace Discord.Rest { - internal class Application : SnowflakeEntity, IApplication + public class RestApplication : RestEntity, IApplication { protected string _iconId; @@ -14,39 +13,37 @@ namespace Discord.Rest public string[] RPCOrigins { get; private set; } public ulong Flags { get; private set; } - public override DiscordRestClient Discord { get; } public IUser Owner { get; private set; } public string IconUrl => API.CDN.GetApplicationIconUrl(Id, _iconId); - public Application(DiscordRestClient discord, Model model) - : base(model.Id) + internal RestApplication(DiscordRestClient discord, ulong id) + : base(discord, id) { - Discord = discord; - - Update(model, UpdateSource.Creation); } - - internal void Update(Model model, UpdateSource source) + internal static RestApplication Create(DiscordRestClient discord, Model model) { - if (source == UpdateSource.Rest && IsAttached) return; - + var entity = new RestApplication(discord, model.Id); + entity.Update(model); + return entity; + } + + internal void Update(Model model) + { Description = model.Description; RPCOrigins = model.RPCOrigins; Name = model.Name; Flags = model.Flags; - Owner = new User(model.Owner); + Owner = RestUser.Create(Discord, model.Owner); _iconId = model.Icon; } public async Task UpdateAsync() { - if (IsAttached) throw new NotSupportedException(); - var response = await Discord.ApiClient.GetMyApplicationAsync().ConfigureAwait(false); if (response.Id != Id) throw new InvalidOperationException("Unable to update this object from a different application token."); - Update(response, UpdateSource.Rest); + Update(response); } } } diff --git a/src/Discord.Net.Rest/Entities/RestEntity.cs b/src/Discord.Net.Rest/Entities/RestEntity.cs new file mode 100644 index 000000000..496dfe9c6 --- /dev/null +++ b/src/Discord.Net.Rest/Entities/RestEntity.cs @@ -0,0 +1,19 @@ +using System; + +namespace Discord.Rest +{ + public abstract class RestEntity : IEntity + where T : IEquatable + { + public T Id { get; } + public DiscordRestClient Discord { get; } + + public RestEntity(DiscordRestClient discord, T id) + { + Discord = discord; + Id = id; + } + + IDiscordClient IEntity.Discord => Discord; + } +} diff --git a/src/Discord.Net.Rest/Entities/Roles/RestRole.cs b/src/Discord.Net.Rest/Entities/Roles/RestRole.cs new file mode 100644 index 000000000..ccac5f3ad --- /dev/null +++ b/src/Discord.Net.Rest/Entities/Roles/RestRole.cs @@ -0,0 +1,52 @@ +using Discord.API.Rest; +using System; +using System.Diagnostics; +using System.Threading.Tasks; +using Model = Discord.API.Role; + +namespace Discord.Rest +{ + [DebuggerDisplay(@"{DebuggerDisplay,nq}")] + public class RestRole : RestEntity, IRole + { + public RestGuild Guild { get; } + + public Color Color { get; private set; } + public bool IsHoisted { get; private set; } + public bool IsManaged { get; private set; } + public string Name { get; private set; } + public GuildPermissions Permissions { get; private set; } + public int Position { get; private set; } + + public bool IsEveryone => Id == Guild.Id; + public string Mention => MentionUtils.MentionRole(Id); + + internal RestRole(DiscordRestClient discord, ulong id) + : base(discord, id) + { + } + internal static RestRole Create(DiscordRestClient discord, Model model) + { + var entity = new RestRole(discord, model.Id); + entity.Update(model); + return entity; + } + internal void Update(Model model) + { + Name = model.Name; + IsHoisted = model.Hoist; + IsManaged = model.Managed; + Position = model.Position; + Color = new Color(model.Color); + Permissions = new GuildPermissions(model.Permissions); + } + + public Task ModifyAsync(Action func) + => RoleHelper.ModifyAsync(this, Discord, func); + public Task DeleteAsync() + => RoleHelper.DeleteAsync(this, Discord); + + //IRole + IGuild IRole.Guild => Guild; + } +} diff --git a/src/Discord.Net.Rest/Entities/Roles/RoleHelper.cs b/src/Discord.Net.Rest/Entities/Roles/RoleHelper.cs new file mode 100644 index 000000000..864e81630 --- /dev/null +++ b/src/Discord.Net.Rest/Entities/Roles/RoleHelper.cs @@ -0,0 +1,22 @@ +using Discord.API.Rest; +using System; +using System.Threading.Tasks; + +namespace Discord.Rest +{ + internal static class RoleHelper + { + //General + public static async Task DeleteAsync(IRole role, DiscordRestClient client) + { + await client.ApiClient.DeleteGuildRoleAsync(role.Guild.Id, role.Id).ConfigureAwait(false); + } + public static async Task ModifyAsync(IRole role, DiscordRestClient client, + Action func) + { + var args = new ModifyGuildRoleParams(); + func(args); + await client.ApiClient.ModifyGuildRoleAsync(role.Guild.Id, role.Id, args); + } + } +} diff --git a/src/Discord.Net.Rest/Entities/Users/RestConnection.cs b/src/Discord.Net.Rest/Entities/Users/RestConnection.cs new file mode 100644 index 000000000..a369edd96 --- /dev/null +++ b/src/Discord.Net.Rest/Entities/Users/RestConnection.cs @@ -0,0 +1,35 @@ +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics; +using Model = Discord.API.Connection; + +namespace Discord +{ + [DebuggerDisplay(@"{DebuggerDisplay,nq}")] + public class RestConnection : IConnection + { + public string Id { get; } + public string Type { get; } + public string Name { get; } + public bool IsRevoked { get; } + + public IReadOnlyCollection IntegrationIds { get; } + + internal RestConnection(string id, string type, string name, bool isRevoked, IReadOnlyCollection integrationIds) + { + Id = id; + Type = type; + Name = name; + IsRevoked = isRevoked; + + IntegrationIds = integrationIds; + } + internal static RestConnection Create(Model model) + { + return new RestConnection(model.Id, model.Type, model.Name, model.Revoked, model.Integrations.ToImmutableArray()); + } + + public override string ToString() => Name; + private string DebuggerDisplay => $"{Name} ({Id}, Type = {Type}{(IsRevoked ? ", Revoked" : "")})"; + } +} diff --git a/src/Discord.Net.Rest/Entities/Users/RestGroupUser.cs b/src/Discord.Net.Rest/Entities/Users/RestGroupUser.cs new file mode 100644 index 000000000..a8ea34550 --- /dev/null +++ b/src/Discord.Net.Rest/Entities/Users/RestGroupUser.cs @@ -0,0 +1,29 @@ +using System.Diagnostics; +using Model = Discord.API.User; + +namespace Discord.Rest +{ + [DebuggerDisplay(@"{DebuggerDisplay,nq}")] + public class RestGroupUser : RestUser, IGroupUser + { + internal RestGroupUser(DiscordRestClient discord, ulong id) + : base(discord, id) + { + } + internal new static RestGroupUser Create(DiscordRestClient discord, Model model) + { + var entity = new RestGroupUser(discord, model.Id); + entity.Update(model); + return entity; + } + + //IVoiceState + bool IVoiceState.IsDeafened => false; + bool IVoiceState.IsMuted => false; + bool IVoiceState.IsSelfDeafened => false; + bool IVoiceState.IsSelfMuted => false; + bool IVoiceState.IsSuppressed => false; + IVoiceChannel IVoiceState.VoiceChannel => null; + string IVoiceState.VoiceSessionId => null; + } +} diff --git a/src/Discord.Net.Rest/Entities/Users/RestGuildUser.cs b/src/Discord.Net.Rest/Entities/Users/RestGuildUser.cs new file mode 100644 index 000000000..c2e25f735 --- /dev/null +++ b/src/Discord.Net.Rest/Entities/Users/RestGuildUser.cs @@ -0,0 +1,74 @@ +using Discord.API.Rest; +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Threading.Tasks; +using Model = Discord.API.GuildMember; + +namespace Discord.Rest +{ + [DebuggerDisplay(@"{DebuggerDisplay,nq}")] + public class RestGuildUser : RestUser, IGuildUser, IUpdateable + { + private long? _joinedAtTicks; + private ImmutableArray _roleIds; + + public string Nickname { get; private set; } + public ulong GuildId { get; private set; } + + public IReadOnlyCollection RoleIds => _roleIds; + + public DateTimeOffset? JoinedAt => DateTimeUtils.FromTicks(_joinedAtTicks); + + internal RestGuildUser(DiscordRestClient discord, ulong id) + : base(discord, id) + { + } + internal static RestGuildUser Create(DiscordRestClient discord, Model model) + { + var entity = new RestGuildUser(discord, model.User.Id); + entity.Update(model); + return entity; + } + internal void Update(Model model) + { + _joinedAtTicks = model.JoinedAt.UtcTicks; + if (model.Nick.IsSpecified) + Nickname = model.Nick.Value; + UpdateRoles(model.Roles); + } + private void UpdateRoles(ulong[] roleIds) + { + var roles = ImmutableArray.CreateBuilder(roleIds.Length + 1); + roles.Add(GuildId); + for (int i = 0; i < roleIds.Length; i++) + roles.Add(roleIds[i]); + _roleIds = roles.ToImmutable(); + } + + public override async Task UpdateAsync() + => Update(await UserHelper.GetAsync(this, Discord)); + public Task ModifyAsync(Action func) + => UserHelper.ModifyAsync(this, Discord, func); + public Task KickAsync() + => UserHelper.KickAsync(this, Discord); + + public ChannelPermissions GetPermissions(IGuildChannel channel) + { + throw new NotImplementedException(); //TODO: Impl + } + + //IGuildUser + IReadOnlyCollection IGuildUser.RoleIds => RoleIds; + + //IVoiceState + bool IVoiceState.IsDeafened => false; + bool IVoiceState.IsMuted => false; + bool IVoiceState.IsSelfDeafened => false; + bool IVoiceState.IsSelfMuted => false; + bool IVoiceState.IsSuppressed => false; + IVoiceChannel IVoiceState.VoiceChannel => null; + string IVoiceState.VoiceSessionId => null; + } +} diff --git a/src/Discord.Net.Rest/Entities/Users/RestSelfUser.cs b/src/Discord.Net.Rest/Entities/Users/RestSelfUser.cs new file mode 100644 index 000000000..389a2d57d --- /dev/null +++ b/src/Discord.Net.Rest/Entities/Users/RestSelfUser.cs @@ -0,0 +1,45 @@ +using Discord.API.Rest; +using System; +using System.Diagnostics; +using System.Threading.Tasks; +using Model = Discord.API.User; + +namespace Discord.Rest +{ + [DebuggerDisplay(@"{DebuggerDisplay,nq}")] + public class RestSelfUser : RestUser, ISelfUser + { + public string Email { get; private set; } + public bool IsVerified { get; private set; } + public bool IsMfaEnabled { get; private set; } + + internal RestSelfUser(DiscordRestClient discord, ulong id) + : base(discord, id) + { + } + internal new static RestSelfUser Create(DiscordRestClient discord, Model model) + { + var entity = new RestSelfUser(discord, model.Id); + entity.Update(model); + return entity; + } + internal override void Update(Model model) + { + base.Update(model); + + if (model.Email.IsSpecified) + Email = model.Email.Value; + if (model.Verified.IsSpecified) + IsVerified = model.Verified.Value; + if (model.MfaEnabled.IsSpecified) + IsMfaEnabled = model.MfaEnabled.Value; + } + + public override async Task UpdateAsync() + => Update(await UserHelper.GetAsync(this, Discord)); + public Task ModifyAsync(Action func) + => UserHelper.ModifyAsync(this, Discord, func); + + Task ISelfUser.ModifyStatusAsync(Action func) { throw new NotSupportedException(); } + } +} diff --git a/src/Discord.Net.Rest/Entities/Users/RestUser.cs b/src/Discord.Net.Rest/Entities/Users/RestUser.cs new file mode 100644 index 000000000..05e4aa3b2 --- /dev/null +++ b/src/Discord.Net.Rest/Entities/Users/RestUser.cs @@ -0,0 +1,51 @@ +using System.Diagnostics; +using System.Threading.Tasks; +using Model = Discord.API.User; + +namespace Discord.Rest +{ + [DebuggerDisplay(@"{DebuggerDisplay,nq}")] + public class RestUser : RestEntity, IUser, IUpdateable + { + public bool IsBot { get; private set; } + public string Username { get; private set; } + public ushort DiscriminatorValue { get; private set; } + public string AvatarId { get; private set; } + + public string AvatarUrl => API.CDN.GetUserAvatarUrl(Id, AvatarId); + public string Discriminator => DiscriminatorValue.ToString("D4"); + public string Mention => MentionUtils.MentionUser(Id); + public virtual Game? Game => null; + public virtual UserStatus Status => UserStatus.Unknown; + + internal RestUser(DiscordRestClient discord, ulong id) + : base(discord, id) + { + } + internal static RestUser Create(DiscordRestClient discord, Model model) + { + var entity = new RestUser(discord, model.Id); + entity.Update(model); + return entity; + } + internal virtual void Update(Model model) + { + if (model.Avatar.IsSpecified) + AvatarId = model.Avatar.Value; + if (model.Discriminator.IsSpecified) + DiscriminatorValue = ushort.Parse(model.Discriminator.Value); + if (model.Bot.IsSpecified) + IsBot = model.Bot.Value; + if (model.Username.IsSpecified) + Username = model.Username.Value; + } + + public virtual async Task UpdateAsync() + => Update(await UserHelper.GetAsync(this, Discord)); + + public Task CreateDMChannelAsync() + => UserHelper.CreateDMChannelAsync(this, Discord); + + IDMChannel IUser.GetCachedDMChannel() => null; + } +} diff --git a/src/Discord.Net.Rest/Entities/Users/UserHelper.cs b/src/Discord.Net.Rest/Entities/Users/UserHelper.cs new file mode 100644 index 000000000..08a59f2c0 --- /dev/null +++ b/src/Discord.Net.Rest/Entities/Users/UserHelper.cs @@ -0,0 +1,53 @@ +using Discord.API.Rest; +using System.Threading.Tasks; +using Model = Discord.API.User; +using MemberModel = Discord.API.GuildMember; +using System; + +namespace Discord.Rest +{ + internal static class UserHelper + { + public static async Task GetAsync(IUser user, DiscordRestClient client) + { + return await client.ApiClient.GetUserAsync(user.Id); + } + public static async Task GetAsync(ISelfUser user, DiscordRestClient client) + { + var model = await client.ApiClient.GetMyUserAsync(); + if (model.Id != user.Id) + throw new InvalidOperationException("Unable to update this object using a different token."); + return model; + } + public static async Task GetAsync(IGuildUser user, DiscordRestClient client) + { + return await client.ApiClient.GetGuildMemberAsync(user.GuildId, user.Id); + } + public static async Task ModifyAsync(ISelfUser user, DiscordRestClient client, Action func) + { + if (user.Id != client.CurrentUser.Id) + throw new InvalidOperationException("Unable to modify this object using a different token."); + + var args = new ModifyCurrentUserParams(); + func(args); + await client.ApiClient.ModifySelfAsync(args); + } + public static async Task ModifyAsync(IGuildUser user, DiscordRestClient client, Action func) + { + var args = new ModifyGuildMemberParams(); + func(args); + await client.ApiClient.ModifyGuildMemberAsync(user.GuildId, user.Id, args); + } + + public static async Task KickAsync(IGuildUser user, DiscordRestClient client) + { + await client.ApiClient.RemoveGuildMemberAsync(user.GuildId, user.Id); + } + + public static async Task CreateDMChannelAsync(IUser user, DiscordRestClient client) + { + var args = new CreateDMChannelParams(user.Id); + return RestDMChannel.Create(client, await client.ApiClient.CreateDMChannelAsync(args)); + } + } +} diff --git a/src/Discord.Net.Rest/Properties/AssemblyInfo.cs b/src/Discord.Net.Rest/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..f6a5354d2 --- /dev/null +++ b/src/Discord.Net.Rest/Properties/AssemblyInfo.cs @@ -0,0 +1,19 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Discord.Net.Rest")] +[assembly: AssemblyTrademark("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("bfc6dc28-0351-4573-926a-d4124244c04f")] diff --git a/src/Discord.Net.Rest/project.json b/src/Discord.Net.Rest/project.json new file mode 100644 index 000000000..330cf0beb --- /dev/null +++ b/src/Discord.Net.Rest/project.json @@ -0,0 +1,39 @@ +{ + "version": "1.0.0-*", + + "buildOptions": { + "compile": { + "include": [ "../Discord.Net.Utils/**.cs" ] + }, + "define": [ "REST" ] + }, + + "configurations": { + "Release": { + "buildOptions": { + "define": [ "RELEASE" ], + "nowarn": [ "CS1573", "CS1591" ], + "optimize": true, + "warningsAsErrors": true, + "xmlDoc": true + } + } + }, + + "dependencies": { + "Discord.Net.Core": { + "target": "project" + }, + "NETStandard.Library": "1.6.0" + }, + + "frameworks": { + "netstandard1.3": { + "imports": [ + "dotnet5.4", + "dnxcore50", + "portable-net45+win8" + ] + } + } +} diff --git a/src/Discord.Net/API/DiscordRpcAPIClient.cs b/src/Discord.Net.Rpc/API/DiscordRpcApiClient.cs similarity index 99% rename from src/Discord.Net/API/DiscordRpcAPIClient.cs rename to src/Discord.Net.Rpc/API/DiscordRpcApiClient.cs index 37a2aa5a6..b128f13b0 100644 --- a/src/Discord.Net/API/DiscordRpcAPIClient.cs +++ b/src/Discord.Net.Rpc/API/DiscordRpcApiClient.cs @@ -3,7 +3,6 @@ using Discord.API.Rpc; using Discord.Net.Queue; using Discord.Net.Rest; using Discord.Net.WebSockets; -using Discord.Rpc; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System; diff --git a/src/Discord.Net/API/Rpc/Application.cs b/src/Discord.Net.Rpc/API/Rpc/Application.cs similarity index 100% rename from src/Discord.Net/API/Rpc/Application.cs rename to src/Discord.Net.Rpc/API/Rpc/Application.cs diff --git a/src/Discord.Net/API/Rpc/AuthenticateParams.cs b/src/Discord.Net.Rpc/API/Rpc/AuthenticateParams.cs similarity index 100% rename from src/Discord.Net/API/Rpc/AuthenticateParams.cs rename to src/Discord.Net.Rpc/API/Rpc/AuthenticateParams.cs diff --git a/src/Discord.Net/API/Rpc/AuthenticateResponse.cs b/src/Discord.Net.Rpc/API/Rpc/AuthenticateResponse.cs similarity index 100% rename from src/Discord.Net/API/Rpc/AuthenticateResponse.cs rename to src/Discord.Net.Rpc/API/Rpc/AuthenticateResponse.cs diff --git a/src/Discord.Net/API/Rpc/AuthorizeParams.cs b/src/Discord.Net.Rpc/API/Rpc/AuthorizeParams.cs similarity index 100% rename from src/Discord.Net/API/Rpc/AuthorizeParams.cs rename to src/Discord.Net.Rpc/API/Rpc/AuthorizeParams.cs diff --git a/src/Discord.Net/API/Rpc/AuthorizeResponse.cs b/src/Discord.Net.Rpc/API/Rpc/AuthorizeResponse.cs similarity index 100% rename from src/Discord.Net/API/Rpc/AuthorizeResponse.cs rename to src/Discord.Net.Rpc/API/Rpc/AuthorizeResponse.cs diff --git a/src/Discord.Net/API/Rpc/ChannelSubscriptionParams.cs b/src/Discord.Net.Rpc/API/Rpc/ChannelSubscriptionParams.cs similarity index 100% rename from src/Discord.Net/API/Rpc/ChannelSubscriptionParams.cs rename to src/Discord.Net.Rpc/API/Rpc/ChannelSubscriptionParams.cs diff --git a/src/Discord.Net/API/Rpc/ErrorEvent.cs b/src/Discord.Net.Rpc/API/Rpc/ErrorEvent.cs similarity index 100% rename from src/Discord.Net/API/Rpc/ErrorEvent.cs rename to src/Discord.Net.Rpc/API/Rpc/ErrorEvent.cs diff --git a/src/Discord.Net/API/Rpc/GetChannelParams.cs b/src/Discord.Net.Rpc/API/Rpc/GetChannelParams.cs similarity index 100% rename from src/Discord.Net/API/Rpc/GetChannelParams.cs rename to src/Discord.Net.Rpc/API/Rpc/GetChannelParams.cs diff --git a/src/Discord.Net/API/Rpc/GetChannelsParams.cs b/src/Discord.Net.Rpc/API/Rpc/GetChannelsParams.cs similarity index 100% rename from src/Discord.Net/API/Rpc/GetChannelsParams.cs rename to src/Discord.Net.Rpc/API/Rpc/GetChannelsParams.cs diff --git a/src/Discord.Net/API/Rpc/GetChannelsResponse.cs b/src/Discord.Net.Rpc/API/Rpc/GetChannelsResponse.cs similarity index 100% rename from src/Discord.Net/API/Rpc/GetChannelsResponse.cs rename to src/Discord.Net.Rpc/API/Rpc/GetChannelsResponse.cs diff --git a/src/Discord.Net/API/Rpc/GetGuildParams.cs b/src/Discord.Net.Rpc/API/Rpc/GetGuildParams.cs similarity index 100% rename from src/Discord.Net/API/Rpc/GetGuildParams.cs rename to src/Discord.Net.Rpc/API/Rpc/GetGuildParams.cs diff --git a/src/Discord.Net/API/Rpc/GetGuildsParams.cs b/src/Discord.Net.Rpc/API/Rpc/GetGuildsParams.cs similarity index 100% rename from src/Discord.Net/API/Rpc/GetGuildsParams.cs rename to src/Discord.Net.Rpc/API/Rpc/GetGuildsParams.cs diff --git a/src/Discord.Net/API/Rpc/GetGuildsResponse.cs b/src/Discord.Net.Rpc/API/Rpc/GetGuildsResponse.cs similarity index 100% rename from src/Discord.Net/API/Rpc/GetGuildsResponse.cs rename to src/Discord.Net.Rpc/API/Rpc/GetGuildsResponse.cs diff --git a/src/Discord.Net/API/Rpc/GuildStatusEvent.cs b/src/Discord.Net.Rpc/API/Rpc/GuildStatusEvent.cs similarity index 100% rename from src/Discord.Net/API/Rpc/GuildStatusEvent.cs rename to src/Discord.Net.Rpc/API/Rpc/GuildStatusEvent.cs diff --git a/src/Discord.Net/API/Rpc/GuildSubscriptionParams.cs b/src/Discord.Net.Rpc/API/Rpc/GuildSubscriptionParams.cs similarity index 100% rename from src/Discord.Net/API/Rpc/GuildSubscriptionParams.cs rename to src/Discord.Net.Rpc/API/Rpc/GuildSubscriptionParams.cs diff --git a/src/Discord.Net/API/Rpc/MessageEvent.cs b/src/Discord.Net.Rpc/API/Rpc/MessageEvent.cs similarity index 100% rename from src/Discord.Net/API/Rpc/MessageEvent.cs rename to src/Discord.Net.Rpc/API/Rpc/MessageEvent.cs diff --git a/src/Discord.Net/API/Rpc/ReadyEvent.cs b/src/Discord.Net.Rpc/API/Rpc/ReadyEvent.cs similarity index 100% rename from src/Discord.Net/API/Rpc/ReadyEvent.cs rename to src/Discord.Net.Rpc/API/Rpc/ReadyEvent.cs diff --git a/src/Discord.Net/API/Rpc/RpcChannel.cs b/src/Discord.Net.Rpc/API/Rpc/RpcChannel.cs similarity index 100% rename from src/Discord.Net/API/Rpc/RpcChannel.cs rename to src/Discord.Net.Rpc/API/Rpc/RpcChannel.cs diff --git a/src/Discord.Net/API/Rpc/RpcConfig.cs b/src/Discord.Net.Rpc/API/Rpc/RpcConfig.cs similarity index 100% rename from src/Discord.Net/API/Rpc/RpcConfig.cs rename to src/Discord.Net.Rpc/API/Rpc/RpcConfig.cs diff --git a/src/Discord.Net/API/Rpc/RpcGuild.cs b/src/Discord.Net.Rpc/API/Rpc/RpcGuild.cs similarity index 100% rename from src/Discord.Net/API/Rpc/RpcGuild.cs rename to src/Discord.Net.Rpc/API/Rpc/RpcGuild.cs diff --git a/src/Discord.Net/API/Rpc/RpcMessage.cs b/src/Discord.Net.Rpc/API/Rpc/RpcMessage.cs similarity index 100% rename from src/Discord.Net/API/Rpc/RpcMessage.cs rename to src/Discord.Net.Rpc/API/Rpc/RpcMessage.cs diff --git a/src/Discord.Net/API/Rpc/RpcUserGuild.cs b/src/Discord.Net.Rpc/API/Rpc/RpcUserGuild.cs similarity index 100% rename from src/Discord.Net/API/Rpc/RpcUserGuild.cs rename to src/Discord.Net.Rpc/API/Rpc/RpcUserGuild.cs diff --git a/src/Discord.Net/API/Rpc/SelectVoiceChannelParams.cs b/src/Discord.Net.Rpc/API/Rpc/SelectVoiceChannelParams.cs similarity index 100% rename from src/Discord.Net/API/Rpc/SelectVoiceChannelParams.cs rename to src/Discord.Net.Rpc/API/Rpc/SelectVoiceChannelParams.cs diff --git a/src/Discord.Net/API/Rpc/SetLocalVolumeParams.cs b/src/Discord.Net.Rpc/API/Rpc/SetLocalVolumeParams.cs similarity index 100% rename from src/Discord.Net/API/Rpc/SetLocalVolumeParams.cs rename to src/Discord.Net.Rpc/API/Rpc/SetLocalVolumeParams.cs diff --git a/src/Discord.Net/API/Rpc/SetLocalVolumeResponse.cs b/src/Discord.Net.Rpc/API/Rpc/SetLocalVolumeResponse.cs similarity index 100% rename from src/Discord.Net/API/Rpc/SetLocalVolumeResponse.cs rename to src/Discord.Net.Rpc/API/Rpc/SetLocalVolumeResponse.cs diff --git a/src/Discord.Net/API/Rpc/SpeakingEvent.cs b/src/Discord.Net.Rpc/API/Rpc/SpeakingEvent.cs similarity index 100% rename from src/Discord.Net/API/Rpc/SpeakingEvent.cs rename to src/Discord.Net.Rpc/API/Rpc/SpeakingEvent.cs diff --git a/src/Discord.Net/API/Rpc/SubscriptionResponse.cs b/src/Discord.Net.Rpc/API/Rpc/SubscriptionResponse.cs similarity index 100% rename from src/Discord.Net/API/Rpc/SubscriptionResponse.cs rename to src/Discord.Net.Rpc/API/Rpc/SubscriptionResponse.cs diff --git a/src/Discord.Net/API/Rpc/VoiceStateEvent.cs b/src/Discord.Net.Rpc/API/Rpc/VoiceStateEvent.cs similarity index 100% rename from src/Discord.Net/API/Rpc/VoiceStateEvent.cs rename to src/Discord.Net.Rpc/API/Rpc/VoiceStateEvent.cs diff --git a/src/Discord.Net.Rpc/Discord.Net.Rpc.xproj b/src/Discord.Net.Rpc/Discord.Net.Rpc.xproj new file mode 100644 index 000000000..c6ea6658f --- /dev/null +++ b/src/Discord.Net.Rpc/Discord.Net.Rpc.xproj @@ -0,0 +1,21 @@ + + + + 14.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + + 5688a353-121e-40a1-8bfa-b17b91fb48fb + Discord.Net.Rpc + .\obj + .\bin\ + v4.6.1 + + + + 2.0 + + + diff --git a/src/Discord.Net/Rpc/DiscordRpcClient.Events.cs b/src/Discord.Net.Rpc/DiscordRpcClient.Events.cs similarity index 100% rename from src/Discord.Net/Rpc/DiscordRpcClient.Events.cs rename to src/Discord.Net.Rpc/DiscordRpcClient.Events.cs diff --git a/src/Discord.Net/Rpc/DiscordRpcClient.cs b/src/Discord.Net.Rpc/DiscordRpcClient.cs similarity index 100% rename from src/Discord.Net/Rpc/DiscordRpcClient.cs rename to src/Discord.Net.Rpc/DiscordRpcClient.cs diff --git a/src/Discord.Net/Rpc/DiscordRpcConfig.cs b/src/Discord.Net.Rpc/DiscordRpcConfig.cs similarity index 100% rename from src/Discord.Net/Rpc/DiscordRpcConfig.cs rename to src/Discord.Net.Rpc/DiscordRpcConfig.cs diff --git a/src/Discord.Net/Rpc/Entities/IRemoteUserGuild.cs b/src/Discord.Net.Rpc/Entities/IRemoteUserGuild.cs similarity index 100% rename from src/Discord.Net/Rpc/Entities/IRemoteUserGuild.cs rename to src/Discord.Net.Rpc/Entities/IRemoteUserGuild.cs diff --git a/src/Discord.Net/Rpc/Entities/RemoteUserGuild.cs b/src/Discord.Net.Rpc/Entities/RemoteUserGuild.cs similarity index 78% rename from src/Discord.Net/Rpc/Entities/RemoteUserGuild.cs rename to src/Discord.Net.Rpc/Entities/RemoteUserGuild.cs index e0337237a..98bb1c027 100644 --- a/src/Discord.Net/Rpc/Entities/RemoteUserGuild.cs +++ b/src/Discord.Net.Rpc/Entities/RemoteUserGuild.cs @@ -16,12 +16,10 @@ namespace Discord.Rpc { Id = model.Id; Discord = discord; - Update(model, UpdateSource.Creation); + Update(model); } - public void Update(Model model, UpdateSource source) + public void Update(Model model) { - if (source == UpdateSource.Rest) return; - Name = model.Name; } diff --git a/src/Discord.Net/Rpc/Entities/RpcMessage.cs b/src/Discord.Net.Rpc/Entities/RpcMessage.cs similarity index 100% rename from src/Discord.Net/Rpc/Entities/RpcMessage.cs rename to src/Discord.Net.Rpc/Entities/RpcMessage.cs diff --git a/src/Discord.Net.Rpc/Properties/AssemblyInfo.cs b/src/Discord.Net.Rpc/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..9cfcc4aa1 --- /dev/null +++ b/src/Discord.Net.Rpc/Properties/AssemblyInfo.cs @@ -0,0 +1,19 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Discord.Net.Rpc")] +[assembly: AssemblyTrademark("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("5688a353-121e-40a1-8bfa-b17b91fb48fb")] diff --git a/src/Discord.Net/Rpc/RpcChannelEvent.cs b/src/Discord.Net.Rpc/RpcChannelEvent.cs similarity index 100% rename from src/Discord.Net/Rpc/RpcChannelEvent.cs rename to src/Discord.Net.Rpc/RpcChannelEvent.cs diff --git a/src/Discord.Net/Rpc/RpcGuildEvent.cs b/src/Discord.Net.Rpc/RpcGuildEvent.cs similarity index 100% rename from src/Discord.Net/Rpc/RpcGuildEvent.cs rename to src/Discord.Net.Rpc/RpcGuildEvent.cs diff --git a/src/Discord.Net.Rpc/project.json b/src/Discord.Net.Rpc/project.json new file mode 100644 index 000000000..de1e3725c --- /dev/null +++ b/src/Discord.Net.Rpc/project.json @@ -0,0 +1,20 @@ +{ + "version": "1.0.0-*", + + "buildOptions": { + "compile": { + "include": [ "../Discord.Net.Entities/**.cs", "../Discord.Net.Utils/**.cs" ] + }, + "define": [ "RPC" ] + }, + + "dependencies": { + "NETStandard.Library": "1.6.0" + }, + + "frameworks": { + "netstandard1.6": { + "imports": "dnxcore50" + } + } +} diff --git a/src/Discord.Net/Utilities/AsyncEvent.cs b/src/Discord.Net.Utils/AsyncEvent.cs similarity index 100% rename from src/Discord.Net/Utilities/AsyncEvent.cs rename to src/Discord.Net.Utils/AsyncEvent.cs diff --git a/src/Discord.Net/Utilities/ConcurrentHashSet.cs b/src/Discord.Net.Utils/ConcurrentHashSet.cs similarity index 100% rename from src/Discord.Net/Utilities/ConcurrentHashSet.cs rename to src/Discord.Net.Utils/ConcurrentHashSet.cs diff --git a/src/Discord.Net/Utilities/DateTimeUtils.cs b/src/Discord.Net.Utils/DateTimeUtils.cs similarity index 100% rename from src/Discord.Net/Utilities/DateTimeUtils.cs rename to src/Discord.Net.Utils/DateTimeUtils.cs diff --git a/src/Discord.Net.Utils/Discord.Net.Utils.projitems b/src/Discord.Net.Utils/Discord.Net.Utils.projitems new file mode 100644 index 000000000..2a493f22a --- /dev/null +++ b/src/Discord.Net.Utils/Discord.Net.Utils.projitems @@ -0,0 +1,26 @@ + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + true + 2b75119c-9893-4aaa-8d38-6176eeb09060 + + + Discord + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Discord.Net.Utils/Discord.Net.Utils.shproj b/src/Discord.Net.Utils/Discord.Net.Utils.shproj new file mode 100644 index 000000000..e3beed6a1 --- /dev/null +++ b/src/Discord.Net.Utils/Discord.Net.Utils.shproj @@ -0,0 +1,13 @@ + + + + 2b75119c-9893-4aaa-8d38-6176eeb09060 + 14.0 + + + + + + + + diff --git a/src/Discord.Net/Extensions/CollectionExtensions.cs b/src/Discord.Net.Utils/Extensions/CollectionExtensions.cs similarity index 100% rename from src/Discord.Net/Extensions/CollectionExtensions.cs rename to src/Discord.Net.Utils/Extensions/CollectionExtensions.cs diff --git a/src/Discord.Net.Utils/Extensions/Permissions.cs b/src/Discord.Net.Utils/Extensions/Permissions.cs new file mode 100644 index 000000000..b8593a31d --- /dev/null +++ b/src/Discord.Net.Utils/Extensions/Permissions.cs @@ -0,0 +1,152 @@ +using System.Runtime.CompilerServices; + +namespace Discord.Extensions +{ + internal static class Permissions + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static PermValue GetValue(ulong allow, ulong deny, ChannelPermission bit) + => GetValue(allow, deny, (byte)bit); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static PermValue GetValue(ulong allow, ulong deny, GuildPermission bit) + => GetValue(allow, deny, (byte)bit); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static PermValue GetValue(ulong allow, ulong deny, byte bit) + { + if (HasBit(allow, bit)) + return PermValue.Allow; + else if (HasBit(deny, bit)) + return PermValue.Deny; + else + return PermValue.Inherit; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool GetValue(ulong value, ChannelPermission bit) + => GetValue(value, (byte)bit); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool GetValue(ulong value, GuildPermission bit) + => GetValue(value, (byte)bit); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool GetValue(ulong value, byte bit) => HasBit(value, bit); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void SetValue(ref ulong rawValue, bool? value, ChannelPermission bit) + => SetValue(ref rawValue, value, (byte)bit); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void SetValue(ref ulong rawValue, bool? value, GuildPermission bit) + => SetValue(ref rawValue, value, (byte)bit); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void SetValue(ref ulong rawValue, bool? value, byte bit) + { + if (value.HasValue) + { + if (value == true) + SetBit(ref rawValue, bit); + else + UnsetBit(ref rawValue, bit); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void SetValue(ref ulong allow, ref ulong deny, PermValue? value, ChannelPermission bit) + => SetValue(ref allow, ref deny, value, (byte)bit); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void SetValue(ref ulong allow, ref ulong deny, PermValue? value, GuildPermission bit) + => SetValue(ref allow, ref deny, value, (byte)bit); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void SetValue(ref ulong allow, ref ulong deny, PermValue? value, byte bit) + { + if (value.HasValue) + { + switch (value) + { + case PermValue.Allow: + SetBit(ref allow, bit); + UnsetBit(ref deny, bit); + break; + case PermValue.Deny: + UnsetBit(ref allow, bit); + SetBit(ref deny, bit); + break; + default: + UnsetBit(ref allow, bit); + UnsetBit(ref deny, bit); + break; + } + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static bool HasBit(ulong value, byte bit) => (value & (1U << bit)) != 0; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void SetBit(ref ulong value, byte bit) => value |= (1U << bit); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void UnsetBit(ref ulong value, byte bit) => value &= ~(1U << bit); + + public static ulong ResolveGuild(IGuild guild, IGuildUser user) + { + ulong resolvedPermissions = 0; + + if (user.Id == guild.OwnerId) + resolvedPermissions = GuildPermissions.All.RawValue; //Owners always have all permissions + else + { + foreach (var role in user.RoleIds) + resolvedPermissions |= guild.GetRole(role).Permissions.RawValue; + if (GetValue(resolvedPermissions, GuildPermission.Administrator)) + resolvedPermissions = GuildPermissions.All.RawValue; //Administrators always have all permissions + } + return resolvedPermissions; + } + + /*public static ulong ResolveChannel(IGuildUser user, IGuildChannel channel) + { + return ResolveChannel(user, channel, ResolveGuild(user)); + }*/ + public static ulong ResolveChannel(IGuild guild, IGuildChannel channel, IGuildUser user, ulong guildPermissions) + { + ulong resolvedPermissions = 0; + + ulong mask = ChannelPermissions.All(channel).RawValue; + if (/*user.Id == user.Guild.OwnerId || */GetValue(guildPermissions, GuildPermission.Administrator)) + resolvedPermissions = mask; //Owners and administrators always have all permissions + else + { + //Start with this user's guild permissions + resolvedPermissions = guildPermissions; + + OverwritePermissions? perms; + var roleIds = user.RoleIds; + if (roleIds.Count > 0) + { + ulong deniedPermissions = 0UL, allowedPermissions = 0UL; + foreach (var roleId in roleIds) + { + perms = channel.GetPermissionOverwrite(guild.GetRole(roleId)); + if (perms != null) + { + deniedPermissions |= perms.Value.DenyValue; + allowedPermissions |= perms.Value.AllowValue; + } + } + resolvedPermissions = (resolvedPermissions & ~deniedPermissions) | allowedPermissions; + } + perms = channel.GetPermissionOverwrite(user); + if (perms != null) + resolvedPermissions = (resolvedPermissions & ~perms.Value.DenyValue) | perms.Value.AllowValue; + + //TODO: C#7 Typeswitch candidate + var textChannel = channel as ITextChannel; + var voiceChannel = channel as IVoiceChannel; + if (textChannel != null && !GetValue(resolvedPermissions, ChannelPermission.ReadMessages)) + resolvedPermissions = 0; //No read permission on a text channel removes all other permissions + else if (voiceChannel != null && !GetValue(resolvedPermissions, ChannelPermission.Connect)) + resolvedPermissions = 0; //No connect permission on a voice channel removes all other permissions + resolvedPermissions &= mask; //Ensure we didnt get any permissions this channel doesnt support (from guildPerms, for example) + } + + return resolvedPermissions; + } + } +} diff --git a/src/Discord.Net/Extensions/TaskCompletionSourceExtensions.cs b/src/Discord.Net.Utils/Extensions/TaskCompletionSourceExtensions.cs similarity index 58% rename from src/Discord.Net/Extensions/TaskCompletionSourceExtensions.cs rename to src/Discord.Net.Utils/Extensions/TaskCompletionSourceExtensions.cs index a5a715b4c..be7c42e9c 100644 --- a/src/Discord.Net/Extensions/TaskCompletionSourceExtensions.cs +++ b/src/Discord.Net.Utils/Extensions/TaskCompletionSourceExtensions.cs @@ -5,18 +5,10 @@ namespace Discord { internal static class TaskCompletionSourceExtensions { - public static Task SetResultAsync(this TaskCompletionSource source, T result) - => Task.Run(() => source.SetResult(result)); public static Task TrySetResultAsync(this TaskCompletionSource source, T result) => Task.Run(() => source.TrySetResult(result)); - - public static Task SetExceptionAsync(this TaskCompletionSource source, Exception ex) - => Task.Run(() => source.SetException(ex)); public static Task TrySetExceptionAsync(this TaskCompletionSource source, Exception ex) => Task.Run(() => source.TrySetException(ex)); - - public static Task SetCanceledAsync(this TaskCompletionSource source) - => Task.Run(() => source.SetCanceled()); public static Task TrySetCanceledAsync(this TaskCompletionSource source) => Task.Run(() => source.TrySetCanceled()); } diff --git a/src/Discord.Net/Logging/LogManager.cs b/src/Discord.Net.Utils/Logging/LogManager.cs similarity index 55% rename from src/Discord.Net/Logging/LogManager.cs rename to src/Discord.Net.Utils/Logging/LogManager.cs index e37b2bce6..dfcc97d33 100644 --- a/src/Discord.Net/Logging/LogManager.cs +++ b/src/Discord.Net.Utils/Logging/LogManager.cs @@ -3,7 +3,7 @@ using System.Threading.Tasks; namespace Discord.Logging { - internal class LogManager : ILogManager, ILogger + internal class LogManager { public LogSeverity Level { get; } @@ -30,21 +30,6 @@ namespace Discord.Logging if (severity <= Level) await _messageEvent.InvokeAsync(new LogMessage(severity, source, null, ex)).ConfigureAwait(false); } - async Task ILogger.LogAsync(LogSeverity severity, string message, Exception ex) - { - if (severity <= Level) - await _messageEvent.InvokeAsync(new LogMessage(severity, "Discord", message, ex)).ConfigureAwait(false); - } - async Task ILogger.LogAsync(LogSeverity severity, FormattableString message, Exception ex) - { - if (severity <= Level) - await _messageEvent.InvokeAsync(new LogMessage(severity, "Discord", message.ToString(), ex)).ConfigureAwait(false); - } - async Task ILogger.LogAsync(LogSeverity severity, Exception ex) - { - if (severity <= Level) - await _messageEvent.InvokeAsync(new LogMessage(severity, "Discord", null, ex)).ConfigureAwait(false); - } public Task ErrorAsync(string source, string message, Exception ex = null) => LogAsync(LogSeverity.Error, source, message, ex); @@ -52,12 +37,6 @@ namespace Discord.Logging => LogAsync(LogSeverity.Error, source, message, ex); public Task ErrorAsync(string source, Exception ex) => LogAsync(LogSeverity.Error, source, ex); - Task ILogger.ErrorAsync(string message, Exception ex) - => LogAsync(LogSeverity.Error, "Discord", message, ex); - Task ILogger.ErrorAsync(FormattableString message, Exception ex) - => LogAsync(LogSeverity.Error, "Discord", message, ex); - Task ILogger.ErrorAsync(Exception ex) - => LogAsync(LogSeverity.Error, "Discord", ex); public Task WarningAsync(string source, string message, Exception ex = null) => LogAsync(LogSeverity.Warning, source, message, ex); @@ -65,12 +44,6 @@ namespace Discord.Logging => LogAsync(LogSeverity.Warning, source, message, ex); public Task WarningAsync(string source, Exception ex) => LogAsync(LogSeverity.Warning, source, ex); - Task ILogger.WarningAsync(string message, Exception ex) - => LogAsync(LogSeverity.Warning, "Discord", message, ex); - Task ILogger.WarningAsync(FormattableString message, Exception ex) - => LogAsync(LogSeverity.Warning, "Discord", message, ex); - Task ILogger.WarningAsync(Exception ex) - => LogAsync(LogSeverity.Warning, "Discord", ex); public Task InfoAsync(string source, string message, Exception ex = null) => LogAsync(LogSeverity.Info, source, message, ex); @@ -78,12 +51,6 @@ namespace Discord.Logging => LogAsync(LogSeverity.Info, source, message, ex); public Task InfoAsync(string source, Exception ex) => LogAsync(LogSeverity.Info, source, ex); - Task ILogger.InfoAsync(string message, Exception ex) - => LogAsync(LogSeverity.Info, "Discord", message, ex); - Task ILogger.InfoAsync(FormattableString message, Exception ex) - => LogAsync(LogSeverity.Info, "Discord", message, ex); - Task ILogger.InfoAsync(Exception ex) - => LogAsync(LogSeverity.Info, "Discord", ex); public Task VerboseAsync(string source, string message, Exception ex = null) => LogAsync(LogSeverity.Verbose, source, message, ex); @@ -91,12 +58,6 @@ namespace Discord.Logging => LogAsync(LogSeverity.Verbose, source, message, ex); public Task VerboseAsync(string source, Exception ex) => LogAsync(LogSeverity.Verbose, source, ex); - Task ILogger.VerboseAsync(string message, Exception ex) - => LogAsync(LogSeverity.Verbose, "Discord", message, ex); - Task ILogger.VerboseAsync(FormattableString message, Exception ex) - => LogAsync(LogSeverity.Verbose, "Discord", message, ex); - Task ILogger.VerboseAsync(Exception ex) - => LogAsync(LogSeverity.Verbose, "Discord", ex); public Task DebugAsync(string source, string message, Exception ex = null) => LogAsync(LogSeverity.Debug, source, message, ex); @@ -104,13 +65,7 @@ namespace Discord.Logging => LogAsync(LogSeverity.Debug, source, message, ex); public Task DebugAsync(string source, Exception ex) => LogAsync(LogSeverity.Debug, source, ex); - Task ILogger.DebugAsync(string message, Exception ex) - => LogAsync(LogSeverity.Debug, "Discord", message, ex); - Task ILogger.DebugAsync(FormattableString message, Exception ex) - => LogAsync(LogSeverity.Debug, "Discord", message, ex); - Task ILogger.DebugAsync(Exception ex) - => LogAsync(LogSeverity.Debug, "Discord", ex); - public ILogger CreateLogger(string name) => new Logger(this, name); + public Logger CreateLogger(string name) => new Logger(this, name); } } diff --git a/src/Discord.Net/Logging/Logger.cs b/src/Discord.Net.Utils/Logging/Logger.cs similarity index 98% rename from src/Discord.Net/Logging/Logger.cs rename to src/Discord.Net.Utils/Logging/Logger.cs index 2255f4451..c871c0b26 100644 --- a/src/Discord.Net/Logging/Logger.cs +++ b/src/Discord.Net.Utils/Logging/Logger.cs @@ -3,7 +3,7 @@ using System.Threading.Tasks; namespace Discord.Logging { - internal class Logger : ILogger + internal class Logger { private readonly LogManager _manager; diff --git a/src/Discord.Net/Utilities/MentionUtils.cs b/src/Discord.Net.Utils/MentionsHelper.cs similarity index 56% rename from src/Discord.Net/Utilities/MentionUtils.cs rename to src/Discord.Net.Utils/MentionsHelper.cs index dc2aeb6c2..58c0fe16d 100644 --- a/src/Discord.Net/Utilities/MentionUtils.cs +++ b/src/Discord.Net.Utils/MentionsHelper.cs @@ -1,6 +1,4 @@ -using Discord.WebSocket; -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Collections.Immutable; using System.Globalization; using System.Linq; @@ -8,112 +6,35 @@ using System.Text.RegularExpressions; namespace Discord { - public static class MentionUtils + internal static class MentionsHelper { private static readonly Regex _userRegex = new Regex(@"<@!?([0-9]+)>", RegexOptions.Compiled); private static readonly Regex _channelRegex = new Regex(@"<#([0-9]+)>", RegexOptions.Compiled); private static readonly Regex _roleRegex = new Regex(@"<@&([0-9]+)>", RegexOptions.Compiled); //If the system can't be positive a user doesn't have a nickname, assume useNickname = true (source: Jake) - internal static string Mention(IUser user, bool useNickname = true) => useNickname ? $"<@!{user.Id}>" : $"<@{user.Id}>"; - public static string MentionUser(ulong id) => $"<@!{id}>"; + internal static string MentionUser(ulong id, bool useNickname = true) => useNickname ? $"<@!{id}>" : $"<@{id}>"; + internal static string MentionChannel(ulong id) => $"<#{id}>"; + internal static string MentionRole(ulong id) => $"<@&{id}>"; - internal static string Mention(IChannel channel) => $"<#{channel.Id}>"; - public static string MentionChannel(ulong id) => $"<#{id}>"; - - internal static string Mention(IRole role) => $"<@&{role.Id}>"; - public static string MentionRole(ulong id) => $"<@&{id}>"; - - /// Parses a provided user mention string. - public static ulong ParseUser(string mentionText) - { - ulong id; - if (TryParseUser(mentionText, out id)) - return id; - throw new ArgumentException("Invalid mention format", nameof(mentionText)); - } - /// Tries to parse a provided user mention string. - public static bool TryParseUser(string mentionText, out ulong userId) - { - mentionText = mentionText.Trim(); - if (mentionText.Length >= 3 && mentionText[0] == '<' && mentionText[1] == '@' && mentionText[mentionText.Length - 1] == '>') - { - if (mentionText.Length >= 4 && mentionText[2] == '!') - mentionText = mentionText.Substring(3, mentionText.Length - 4); //<@!123> - else - mentionText = mentionText.Substring(2, mentionText.Length - 3); //<@123> - - if (ulong.TryParse(mentionText, NumberStyles.None, CultureInfo.InvariantCulture, out userId)) - return true; - } - userId = 0; - return false; - } - - /// Parses a provided channel mention string. - public static ulong ParseChannel(string mentionText) - { - ulong id; - if (TryParseChannel(mentionText, out id)) - return id; - throw new ArgumentException("Invalid mention format", nameof(mentionText)); - } - /// Tries to parse a provided channel mention string. - public static bool TryParseChannel(string mentionText, out ulong channelId) - { - mentionText = mentionText.Trim(); - if (mentionText.Length >= 3 && mentionText[0] == '<' && mentionText[1] == '#' && mentionText[mentionText.Length - 1] == '>') - { - mentionText = mentionText.Substring(2, mentionText.Length - 3); //<#123> - - if (ulong.TryParse(mentionText, NumberStyles.None, CultureInfo.InvariantCulture, out channelId)) - return true; - } - channelId = 0; - return false; - } - - /// Parses a provided role mention string. - public static ulong ParseRole(string mentionText) - { - ulong id; - if (TryParseRole(mentionText, out id)) - return id; - throw new ArgumentException("Invalid mention format", nameof(mentionText)); - } - /// Tries to parse a provided role mention string. - public static bool TryParseRole(string mentionText, out ulong roleId) - { - mentionText = mentionText.Trim(); - if (mentionText.Length >= 4 && mentionText[0] == '<' && mentionText[1] == '@' && mentionText[2] == '&' && mentionText[mentionText.Length - 1] == '>') - { - mentionText = mentionText.Substring(3, mentionText.Length - 4); //<@&123> - - if (ulong.TryParse(mentionText, NumberStyles.None, CultureInfo.InvariantCulture, out roleId)) - return true; - } - roleId = 0; - return false; - } - - internal static ImmutableArray GetUserMentions(string text, IMessageChannel channel, IReadOnlyCollection mentionedUsers) + internal static ImmutableArray GetUserMentions(string text, IMessageChannel channel, IReadOnlyCollection mentionedUsers) + where TUser : class, IUser { var matches = _userRegex.Matches(text); - var builder = ImmutableArray.CreateBuilder(matches.Count); + var builder = ImmutableArray.CreateBuilder(matches.Count); foreach (var match in matches.OfType()) { ulong id; if (ulong.TryParse(match.Groups[1].Value, NumberStyles.None, CultureInfo.InvariantCulture, out id)) { - IUser user = null; + TUser user = null; //Verify this user was actually mentioned foreach (var userMention in mentionedUsers) { if (userMention.Id == id) { - if (channel.IsAttached) //Waiting this sync is safe because it's using a cache - user = channel.GetUserAsync(id).GetAwaiter().GetResult() as IUser; + user = channel?.GetCachedUser(id) as TUser; if (user == null) //User not found, fallback to basic mention info user = userMention; break; @@ -134,32 +55,28 @@ namespace Discord { ulong id; if (ulong.TryParse(match.Groups[1].Value, NumberStyles.None, CultureInfo.InvariantCulture, out id)) - { - /*var channel = guild.GetChannelAsync(id).GetAwaiter().GetResult(); - if (channel != null) - builder.Add(channel);*/ builder.Add(id); - } } return builder.ToImmutable(); } - internal static ImmutableArray GetRoleMentions(string text, IGuild guild) + internal static ImmutableArray GetRoleMentions(string text, IGuild guild) + where TRole : class, IRole { var matches = _roleRegex.Matches(text); - var builder = ImmutableArray.CreateBuilder(matches.Count); + var builder = ImmutableArray.CreateBuilder(matches.Count); foreach (var match in matches.OfType()) { ulong id; if (ulong.TryParse(match.Groups[1].Value, NumberStyles.None, CultureInfo.InvariantCulture, out id)) { - var role = guild.GetRole(id); + var role = guild.GetRole(id) as TRole; if (role != null) builder.Add(role); } } return builder.ToImmutable(); } - + internal static string ResolveUserMentions(string text, IMessageChannel channel, IReadOnlyCollection mentions, UserMentionHandling mode) { if (mode == UserMentionHandling.Ignore) return text; @@ -218,16 +135,12 @@ namespace Discord case ChannelMentionHandling.Remove: return ""; case ChannelMentionHandling.Name: - if (guild != null && guild.IsAttached) //It's too expensive to do a channel lookup in REST mode - { - IGuildChannel channel = null; - channel = guild.GetChannel(id); - if (channel != null) - return $"#{channel.Name}"; - else - return $"#deleted-channel"; - } - break; + IGuildChannel channel = null; + channel = guild.GetCachedChannel(id); + if (channel != null) + return $"#{channel.Name}"; + else + return $"#deleted-channel"; } } return e.Value; @@ -236,7 +149,7 @@ namespace Discord internal static string ResolveRoleMentions(string text, IReadOnlyCollection mentions, RoleMentionHandling mode) { if (mode == RoleMentionHandling.Ignore) return text; - + return _roleRegex.Replace(text, new MatchEvaluator(e => { ulong id; @@ -268,7 +181,7 @@ namespace Discord internal static string ResolveEveryoneMentions(string text, EveryoneMentionHandling mode) { if (mode == EveryoneMentionHandling.Ignore) return text; - + switch (mode) { case EveryoneMentionHandling.Sanitize: diff --git a/src/Discord.Net.Utils/Paging/Page.cs b/src/Discord.Net.Utils/Paging/Page.cs new file mode 100644 index 000000000..996d0ac6a --- /dev/null +++ b/src/Discord.Net.Utils/Paging/Page.cs @@ -0,0 +1,22 @@ +using System.Collections; +using System.Collections.Generic; +using System.Collections.Immutable; + +namespace Discord +{ + internal class Page : IReadOnlyCollection + { + private readonly IReadOnlyCollection _items; + public int Index { get; } + + public Page(PageInfo info, IEnumerable source) + { + Index = info.Page; + _items = source.ToImmutableArray(); + } + + int IReadOnlyCollection.Count => _items.Count; + IEnumerator IEnumerable.GetEnumerator() => _items.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => _items.GetEnumerator(); + } +} diff --git a/src/Discord.Net.Utils/Paging/PageInfo.cs b/src/Discord.Net.Utils/Paging/PageInfo.cs new file mode 100644 index 000000000..cb573f3cf --- /dev/null +++ b/src/Discord.Net.Utils/Paging/PageInfo.cs @@ -0,0 +1,20 @@ +namespace Discord +{ + internal class PageInfo + { + public int Page { get; set; } + public ulong? Position { get; set; } + public uint? Count { get; set; } + public int PageSize { get; set; } + public uint? Remaining { get; set; } + + internal PageInfo(ulong? pos, uint? count, int pageSize) + { + Page = 1; + Position = pos; + Count = count; + Remaining = count; + PageSize = pageSize; + } + } +} diff --git a/src/Discord.Net.Utils/Paging/PagedEnumerator.cs b/src/Discord.Net.Utils/Paging/PagedEnumerator.cs new file mode 100644 index 000000000..3f347440b --- /dev/null +++ b/src/Discord.Net.Utils/Paging/PagedEnumerator.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace Discord +{ + internal class PagedAsyncEnumerable : IAsyncEnumerable> + { + public int PageSize { get; } + + private readonly ulong? _start; + private readonly uint? _count; + private readonly Func>> _getPage; + private readonly Action> _nextPage; + + public PagedAsyncEnumerable(int pageSize, Func>> getPage, Action> nextPage = null, + ulong? start = null, uint? count = null) + { + PageSize = pageSize; + _start = start; + _count = count; + + _getPage = getPage; + _nextPage = nextPage; + } + + public IAsyncEnumerator> GetEnumerator() => new Enumerator(this); + internal class Enumerator : IAsyncEnumerator> + { + private readonly PagedAsyncEnumerable _source; + private readonly PageInfo _info; + + public IReadOnlyCollection Current { get; private set; } + + public Enumerator(PagedAsyncEnumerable source) + { + _source = source; + _info = new PageInfo(source._start, source._count, source.PageSize); + } + + public async Task MoveNext(CancellationToken cancelToken) + { + var data = await _source._getPage(_info, cancelToken); + Current = new Page(_info, data); + + _info.Page++; + _info.Remaining -= (uint)Current.Count; + _info.PageSize = _info.Remaining != null ? (int)Math.Min(_info.Remaining.Value, (ulong)_source.PageSize) : _source.PageSize; + _source?._nextPage(_info, data); + + return _info.Remaining > 0; + } + + public void Dispose() { Current = null; } + } + } + + public static class PagedAsyncEnumerable + { + public static async Task> Flatten(this IAsyncEnumerable> source) + { + return (await source.ToArray().ConfigureAwait(false)).SelectMany(x => x); + } + } +} \ No newline at end of file diff --git a/src/Discord.Net/Entities/Permissions/Permissions.cs b/src/Discord.Net.Utils/Permissions.cs similarity index 91% rename from src/Discord.Net/Entities/Permissions/Permissions.cs rename to src/Discord.Net.Utils/Permissions.cs index d7cfe3191..390c142de 100644 --- a/src/Discord.Net/Entities/Permissions/Permissions.cs +++ b/src/Discord.Net.Utils/Permissions.cs @@ -5,7 +5,7 @@ namespace Discord internal static class Permissions { public const int MaxBits = 53; - + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static PermValue GetValue(ulong allow, ulong deny, ChannelPermission bit) => GetValue(allow, deny, (byte)bit); @@ -86,16 +86,16 @@ namespace Discord [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void UnsetBit(ref ulong value, byte bit) => value &= ~(1U << bit); - public static ulong ResolveGuild(IGuildUser user) + public static ulong ResolveGuild(IGuild guild, IGuildUser user) { ulong resolvedPermissions = 0; - - if (user.Id == user.Guild.OwnerId) + + if (user.Id == guild.OwnerId) resolvedPermissions = GuildPermissions.All.RawValue; //Owners always have all permissions else { - foreach (var role in user.Roles) - resolvedPermissions |= role.Permissions.RawValue; + foreach (var roleId in user.RoleIds) + resolvedPermissions |= guild.GetRole(roleId)?.Permissions.RawValue ?? 0; if (GetValue(resolvedPermissions, GuildPermission.Administrator)) resolvedPermissions = GuildPermissions.All.RawValue; //Administrators always have all permissions } @@ -106,7 +106,7 @@ namespace Discord { return ResolveChannel(user, channel, ResolveGuild(user)); }*/ - public static ulong ResolveChannel(IGuildUser user, IGuildChannel channel, ulong guildPermissions) + public static ulong ResolveChannel(IGuild guild, IGuildUser user, IGuildChannel channel, ulong guildPermissions) { ulong resolvedPermissions = 0; @@ -119,13 +119,13 @@ namespace Discord resolvedPermissions = guildPermissions; OverwritePermissions? perms; - var roles = user.Roles; - if (roles.Count > 0) + var roleIds = user.RoleIds; + if (roleIds.Count > 0) { ulong deniedPermissions = 0UL, allowedPermissions = 0UL; - foreach (var role in roles) + foreach (var roleId in roleIds) { - perms = channel.GetPermissionOverwrite(role); + perms = channel.GetPermissionOverwrite(guild.GetRole(roleId)); if (perms != null) { deniedPermissions |= perms.Value.DenyValue; @@ -137,7 +137,7 @@ namespace Discord perms = channel.GetPermissionOverwrite(user); if (perms != null) resolvedPermissions = (resolvedPermissions & ~perms.Value.DenyValue) | perms.Value.AllowValue; - + //TODO: C#7 Typeswitch candidate var textChannel = channel as ITextChannel; var voiceChannel = channel as IVoiceChannel; @@ -151,4 +151,4 @@ namespace Discord return resolvedPermissions; } } -} +} \ No newline at end of file diff --git a/src/Discord.Net/Utilities/Preconditions.cs b/src/Discord.Net.Utils/Preconditions.cs similarity index 99% rename from src/Discord.Net/Utilities/Preconditions.cs rename to src/Discord.Net.Utils/Preconditions.cs index 1bd8da7ac..14c9db24d 100644 --- a/src/Discord.Net/Utilities/Preconditions.cs +++ b/src/Discord.Net.Utils/Preconditions.cs @@ -1,5 +1,4 @@ -using Discord.API; -using System; +using System; namespace Discord { diff --git a/src/Discord.Net/API/DiscordSocketApiClient.cs b/src/Discord.Net.WebSocket/API/DiscordSocketApiClient.cs similarity index 99% rename from src/Discord.Net/API/DiscordSocketApiClient.cs rename to src/Discord.Net.WebSocket/API/DiscordSocketApiClient.cs index 2d2579273..38124de63 100644 --- a/src/Discord.Net/API/DiscordSocketApiClient.cs +++ b/src/Discord.Net.WebSocket/API/DiscordSocketApiClient.cs @@ -4,7 +4,6 @@ using Discord.API.Rest; using Discord.Net.Queue; using Discord.Net.Rest; using Discord.Net.WebSockets; -using Discord.WebSocket; using Newtonsoft.Json; using System; using System.Collections.Generic; diff --git a/src/Discord.Net/API/DiscordVoiceAPIClient.cs b/src/Discord.Net.WebSocket/API/DiscordVoiceApiClient.cs similarity index 100% rename from src/Discord.Net/API/DiscordVoiceAPIClient.cs rename to src/Discord.Net.WebSocket/API/DiscordVoiceApiClient.cs diff --git a/src/Discord.Net/API/Gateway/ExtendedGuild.cs b/src/Discord.Net.WebSocket/API/Gateway/ExtendedGuild.cs similarity index 100% rename from src/Discord.Net/API/Gateway/ExtendedGuild.cs rename to src/Discord.Net.WebSocket/API/Gateway/ExtendedGuild.cs diff --git a/src/Discord.Net/API/Gateway/GatewayOpCode.cs b/src/Discord.Net.WebSocket/API/Gateway/GatewayOpCode.cs similarity index 100% rename from src/Discord.Net/API/Gateway/GatewayOpCode.cs rename to src/Discord.Net.WebSocket/API/Gateway/GatewayOpCode.cs diff --git a/src/Discord.Net/API/Gateway/GuildBanEvent.cs b/src/Discord.Net.WebSocket/API/Gateway/GuildBanEvent.cs similarity index 100% rename from src/Discord.Net/API/Gateway/GuildBanEvent.cs rename to src/Discord.Net.WebSocket/API/Gateway/GuildBanEvent.cs diff --git a/src/Discord.Net/API/Gateway/GuildEmojiUpdateEvent.cs b/src/Discord.Net.WebSocket/API/Gateway/GuildEmojiUpdateEvent.cs similarity index 100% rename from src/Discord.Net/API/Gateway/GuildEmojiUpdateEvent.cs rename to src/Discord.Net.WebSocket/API/Gateway/GuildEmojiUpdateEvent.cs diff --git a/src/Discord.Net/API/Gateway/GuildMemberAddEvent.cs b/src/Discord.Net.WebSocket/API/Gateway/GuildMemberAddEvent.cs similarity index 100% rename from src/Discord.Net/API/Gateway/GuildMemberAddEvent.cs rename to src/Discord.Net.WebSocket/API/Gateway/GuildMemberAddEvent.cs diff --git a/src/Discord.Net/API/Gateway/GuildMemberRemoveEvent.cs b/src/Discord.Net.WebSocket/API/Gateway/GuildMemberRemoveEvent.cs similarity index 100% rename from src/Discord.Net/API/Gateway/GuildMemberRemoveEvent.cs rename to src/Discord.Net.WebSocket/API/Gateway/GuildMemberRemoveEvent.cs diff --git a/src/Discord.Net/API/Gateway/GuildMemberUpdateEvent.cs b/src/Discord.Net.WebSocket/API/Gateway/GuildMemberUpdateEvent.cs similarity index 100% rename from src/Discord.Net/API/Gateway/GuildMemberUpdateEvent.cs rename to src/Discord.Net.WebSocket/API/Gateway/GuildMemberUpdateEvent.cs diff --git a/src/Discord.Net/API/Gateway/GuildMembersChunkEvent.cs b/src/Discord.Net.WebSocket/API/Gateway/GuildMembersChunkEvent.cs similarity index 100% rename from src/Discord.Net/API/Gateway/GuildMembersChunkEvent.cs rename to src/Discord.Net.WebSocket/API/Gateway/GuildMembersChunkEvent.cs diff --git a/src/Discord.Net/API/Gateway/GuildRoleCreateEvent.cs b/src/Discord.Net.WebSocket/API/Gateway/GuildRoleCreateEvent.cs similarity index 100% rename from src/Discord.Net/API/Gateway/GuildRoleCreateEvent.cs rename to src/Discord.Net.WebSocket/API/Gateway/GuildRoleCreateEvent.cs diff --git a/src/Discord.Net/API/Gateway/GuildRoleDeleteEvent.cs b/src/Discord.Net.WebSocket/API/Gateway/GuildRoleDeleteEvent.cs similarity index 100% rename from src/Discord.Net/API/Gateway/GuildRoleDeleteEvent.cs rename to src/Discord.Net.WebSocket/API/Gateway/GuildRoleDeleteEvent.cs diff --git a/src/Discord.Net/API/Gateway/GuildRoleUpdateEvent.cs b/src/Discord.Net.WebSocket/API/Gateway/GuildRoleUpdateEvent.cs similarity index 100% rename from src/Discord.Net/API/Gateway/GuildRoleUpdateEvent.cs rename to src/Discord.Net.WebSocket/API/Gateway/GuildRoleUpdateEvent.cs diff --git a/src/Discord.Net/API/Gateway/GuildSyncEvent.cs b/src/Discord.Net.WebSocket/API/Gateway/GuildSyncEvent.cs similarity index 100% rename from src/Discord.Net/API/Gateway/GuildSyncEvent.cs rename to src/Discord.Net.WebSocket/API/Gateway/GuildSyncEvent.cs diff --git a/src/Discord.Net/API/Gateway/HelloEvent.cs b/src/Discord.Net.WebSocket/API/Gateway/HelloEvent.cs similarity index 100% rename from src/Discord.Net/API/Gateway/HelloEvent.cs rename to src/Discord.Net.WebSocket/API/Gateway/HelloEvent.cs diff --git a/src/Discord.Net/API/Gateway/IdentifyParams.cs b/src/Discord.Net.WebSocket/API/Gateway/IdentifyParams.cs similarity index 100% rename from src/Discord.Net/API/Gateway/IdentifyParams.cs rename to src/Discord.Net.WebSocket/API/Gateway/IdentifyParams.cs diff --git a/src/Discord.Net/API/Gateway/MessageDeleteBulkEvent.cs b/src/Discord.Net.WebSocket/API/Gateway/MessageDeleteBulkEvent.cs similarity index 100% rename from src/Discord.Net/API/Gateway/MessageDeleteBulkEvent.cs rename to src/Discord.Net.WebSocket/API/Gateway/MessageDeleteBulkEvent.cs diff --git a/src/Discord.Net/API/Gateway/ReadyEvent.cs b/src/Discord.Net.WebSocket/API/Gateway/ReadyEvent.cs similarity index 100% rename from src/Discord.Net/API/Gateway/ReadyEvent.cs rename to src/Discord.Net.WebSocket/API/Gateway/ReadyEvent.cs diff --git a/src/Discord.Net/API/Gateway/RecipientEvent.cs b/src/Discord.Net.WebSocket/API/Gateway/RecipientEvent.cs similarity index 100% rename from src/Discord.Net/API/Gateway/RecipientEvent.cs rename to src/Discord.Net.WebSocket/API/Gateway/RecipientEvent.cs diff --git a/src/Discord.Net/API/Gateway/RequestMembersParams.cs b/src/Discord.Net.WebSocket/API/Gateway/RequestMembersParams.cs similarity index 100% rename from src/Discord.Net/API/Gateway/RequestMembersParams.cs rename to src/Discord.Net.WebSocket/API/Gateway/RequestMembersParams.cs diff --git a/src/Discord.Net/API/Gateway/ResumeParams.cs b/src/Discord.Net.WebSocket/API/Gateway/ResumeParams.cs similarity index 100% rename from src/Discord.Net/API/Gateway/ResumeParams.cs rename to src/Discord.Net.WebSocket/API/Gateway/ResumeParams.cs diff --git a/src/Discord.Net/API/Gateway/ResumedEvent.cs b/src/Discord.Net.WebSocket/API/Gateway/ResumedEvent.cs similarity index 100% rename from src/Discord.Net/API/Gateway/ResumedEvent.cs rename to src/Discord.Net.WebSocket/API/Gateway/ResumedEvent.cs diff --git a/src/Discord.Net/API/Gateway/StatusUpdateParams.cs b/src/Discord.Net.WebSocket/API/Gateway/StatusUpdateParams.cs similarity index 100% rename from src/Discord.Net/API/Gateway/StatusUpdateParams.cs rename to src/Discord.Net.WebSocket/API/Gateway/StatusUpdateParams.cs diff --git a/src/Discord.Net/API/Gateway/TypingStartEvent.cs b/src/Discord.Net.WebSocket/API/Gateway/TypingStartEvent.cs similarity index 100% rename from src/Discord.Net/API/Gateway/TypingStartEvent.cs rename to src/Discord.Net.WebSocket/API/Gateway/TypingStartEvent.cs diff --git a/src/Discord.Net/API/Gateway/UpdateStatusParams.cs b/src/Discord.Net.WebSocket/API/Gateway/UpdateStatusParams.cs similarity index 100% rename from src/Discord.Net/API/Gateway/UpdateStatusParams.cs rename to src/Discord.Net.WebSocket/API/Gateway/UpdateStatusParams.cs diff --git a/src/Discord.Net/API/Gateway/VoiceServerUpdateEvent.cs b/src/Discord.Net.WebSocket/API/Gateway/VoiceServerUpdateEvent.cs similarity index 100% rename from src/Discord.Net/API/Gateway/VoiceServerUpdateEvent.cs rename to src/Discord.Net.WebSocket/API/Gateway/VoiceServerUpdateEvent.cs diff --git a/src/Discord.Net/API/Gateway/VoiceStateUpdateParams.cs b/src/Discord.Net.WebSocket/API/Gateway/VoiceStateUpdateParams.cs similarity index 100% rename from src/Discord.Net/API/Gateway/VoiceStateUpdateParams.cs rename to src/Discord.Net.WebSocket/API/Gateway/VoiceStateUpdateParams.cs diff --git a/src/Discord.Net/API/Voice/IdentifyParams.cs b/src/Discord.Net.WebSocket/API/Voice/IdentifyParams.cs similarity index 100% rename from src/Discord.Net/API/Voice/IdentifyParams.cs rename to src/Discord.Net.WebSocket/API/Voice/IdentifyParams.cs diff --git a/src/Discord.Net/API/Voice/ReadyEvent.cs b/src/Discord.Net.WebSocket/API/Voice/ReadyEvent.cs similarity index 100% rename from src/Discord.Net/API/Voice/ReadyEvent.cs rename to src/Discord.Net.WebSocket/API/Voice/ReadyEvent.cs diff --git a/src/Discord.Net/API/Voice/SelectProtocolParams.cs b/src/Discord.Net.WebSocket/API/Voice/SelectProtocolParams.cs similarity index 100% rename from src/Discord.Net/API/Voice/SelectProtocolParams.cs rename to src/Discord.Net.WebSocket/API/Voice/SelectProtocolParams.cs diff --git a/src/Discord.Net/API/Voice/SessionDescriptionEvent.cs b/src/Discord.Net.WebSocket/API/Voice/SessionDescriptionEvent.cs similarity index 100% rename from src/Discord.Net/API/Voice/SessionDescriptionEvent.cs rename to src/Discord.Net.WebSocket/API/Voice/SessionDescriptionEvent.cs diff --git a/src/Discord.Net/API/Voice/SpeakingParams.cs b/src/Discord.Net.WebSocket/API/Voice/SpeakingParams.cs similarity index 100% rename from src/Discord.Net/API/Voice/SpeakingParams.cs rename to src/Discord.Net.WebSocket/API/Voice/SpeakingParams.cs diff --git a/src/Discord.Net/API/Voice/UdpProtocolInfo.cs b/src/Discord.Net.WebSocket/API/Voice/UdpProtocolInfo.cs similarity index 100% rename from src/Discord.Net/API/Voice/UdpProtocolInfo.cs rename to src/Discord.Net.WebSocket/API/Voice/UdpProtocolInfo.cs diff --git a/src/Discord.Net/API/Voice/VoiceOpCode.cs b/src/Discord.Net.WebSocket/API/Voice/VoiceOpCode.cs similarity index 100% rename from src/Discord.Net/API/Voice/VoiceOpCode.cs rename to src/Discord.Net.WebSocket/API/Voice/VoiceOpCode.cs diff --git a/src/Discord.Net/Audio/AudioClient.cs b/src/Discord.Net.WebSocket/Audio/AudioClient.cs similarity index 100% rename from src/Discord.Net/Audio/AudioClient.cs rename to src/Discord.Net.WebSocket/Audio/AudioClient.cs diff --git a/src/Discord.Net/Audio/AudioMode.cs b/src/Discord.Net.WebSocket/Audio/AudioMode.cs similarity index 100% rename from src/Discord.Net/Audio/AudioMode.cs rename to src/Discord.Net.WebSocket/Audio/AudioMode.cs diff --git a/src/Discord.Net/Audio/Opus/OpusConverter.cs b/src/Discord.Net.WebSocket/Audio/Opus/OpusConverter.cs similarity index 100% rename from src/Discord.Net/Audio/Opus/OpusConverter.cs rename to src/Discord.Net.WebSocket/Audio/Opus/OpusConverter.cs diff --git a/src/Discord.Net/Audio/Opus/OpusCtl.cs b/src/Discord.Net.WebSocket/Audio/Opus/OpusCtl.cs similarity index 100% rename from src/Discord.Net/Audio/Opus/OpusCtl.cs rename to src/Discord.Net.WebSocket/Audio/Opus/OpusCtl.cs diff --git a/src/Discord.Net/Audio/Opus/OpusDecoder.cs b/src/Discord.Net.WebSocket/Audio/Opus/OpusDecoder.cs similarity index 100% rename from src/Discord.Net/Audio/Opus/OpusDecoder.cs rename to src/Discord.Net.WebSocket/Audio/Opus/OpusDecoder.cs diff --git a/src/Discord.Net/Audio/Opus/OpusEncoder.cs b/src/Discord.Net.WebSocket/Audio/Opus/OpusEncoder.cs similarity index 100% rename from src/Discord.Net/Audio/Opus/OpusEncoder.cs rename to src/Discord.Net.WebSocket/Audio/Opus/OpusEncoder.cs diff --git a/src/Discord.Net/Audio/Opus/OpusError.cs b/src/Discord.Net.WebSocket/Audio/Opus/OpusError.cs similarity index 100% rename from src/Discord.Net/Audio/Opus/OpusError.cs rename to src/Discord.Net.WebSocket/Audio/Opus/OpusError.cs diff --git a/src/Discord.Net/Audio/Sodium/SecretBox.cs b/src/Discord.Net.WebSocket/Audio/Sodium/SecretBox.cs similarity index 100% rename from src/Discord.Net/Audio/Sodium/SecretBox.cs rename to src/Discord.Net.WebSocket/Audio/Sodium/SecretBox.cs diff --git a/src/Discord.Net/Audio/Streams/OpusDecodeStream.cs b/src/Discord.Net.WebSocket/Audio/Streams/OpusDecodeStream.cs similarity index 100% rename from src/Discord.Net/Audio/Streams/OpusDecodeStream.cs rename to src/Discord.Net.WebSocket/Audio/Streams/OpusDecodeStream.cs diff --git a/src/Discord.Net/Audio/Streams/OpusEncodeStream.cs b/src/Discord.Net.WebSocket/Audio/Streams/OpusEncodeStream.cs similarity index 100% rename from src/Discord.Net/Audio/Streams/OpusEncodeStream.cs rename to src/Discord.Net.WebSocket/Audio/Streams/OpusEncodeStream.cs diff --git a/src/Discord.Net/Audio/Streams/RTPReadStream.cs b/src/Discord.Net.WebSocket/Audio/Streams/RTPReadStream.cs similarity index 100% rename from src/Discord.Net/Audio/Streams/RTPReadStream.cs rename to src/Discord.Net.WebSocket/Audio/Streams/RTPReadStream.cs diff --git a/src/Discord.Net/Audio/Streams/RTPWriteStream.cs b/src/Discord.Net.WebSocket/Audio/Streams/RTPWriteStream.cs similarity index 100% rename from src/Discord.Net/Audio/Streams/RTPWriteStream.cs rename to src/Discord.Net.WebSocket/Audio/Streams/RTPWriteStream.cs diff --git a/src/Discord.Net/WebSocket/DataStore.cs b/src/Discord.Net.WebSocket/DataStore.cs similarity index 100% rename from src/Discord.Net/WebSocket/DataStore.cs rename to src/Discord.Net.WebSocket/DataStore.cs diff --git a/src/Discord.Net.WebSocket/Discord.Net.WebSocket.xproj b/src/Discord.Net.WebSocket/Discord.Net.WebSocket.xproj new file mode 100644 index 000000000..da99d6e1b --- /dev/null +++ b/src/Discord.Net.WebSocket/Discord.Net.WebSocket.xproj @@ -0,0 +1,21 @@ + + + + 14.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + + 22ab6c66-536c-4ac2-bbdb-a8bc4eb6b14d + Discord.Net.WebSocket + .\obj + .\bin\ + v4.6.1 + + + + 2.0 + + + diff --git a/src/Discord.Net/WebSocket/DiscordSocketClient.Events.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.Events.cs similarity index 100% rename from src/Discord.Net/WebSocket/DiscordSocketClient.Events.cs rename to src/Discord.Net.WebSocket/DiscordSocketClient.Events.cs diff --git a/src/Discord.Net/WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs similarity index 99% rename from src/Discord.Net/WebSocket/DiscordSocketClient.cs rename to src/Discord.Net.WebSocket/DiscordSocketClient.cs index a77110c66..c90e9d7c6 100644 --- a/src/Discord.Net/WebSocket/DiscordSocketClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs @@ -393,7 +393,7 @@ namespace Discord.WebSocket case ChannelType.Group: { var channel = new SocketGroupChannel(this, model); - channel.UpdateUsers(model.Recipients.Value, UpdateSource.Creation, dataStore); + channel.UpdateUsers(model.Recipients.Value, dataStore); dataStore.AddChannel(channel); return channel; } diff --git a/src/Discord.Net/WebSocket/DiscordSocketConfig.cs b/src/Discord.Net.WebSocket/DiscordSocketConfig.cs similarity index 100% rename from src/Discord.Net/WebSocket/DiscordSocketConfig.cs rename to src/Discord.Net.WebSocket/DiscordSocketConfig.cs diff --git a/src/Discord.Net/WebSocket/Entities/Channels/SocketDMChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/SocketDMChannel.cs similarity index 93% rename from src/Discord.Net/WebSocket/Entities/Channels/SocketDMChannel.cs rename to src/Discord.Net.WebSocket/Entities/Channels/SocketDMChannel.cs index 958c71bba..c19508856 100644 --- a/src/Discord.Net/WebSocket/Entities/Channels/SocketDMChannel.cs +++ b/src/Discord.Net.WebSocket/Entities/Channels/SocketDMChannel.cs @@ -1,5 +1,4 @@ -using Discord.Rest; -using System.Collections.Generic; +using System.Collections.Generic; using System.Collections.Immutable; using System.Threading.Tasks; using MessageModel = Discord.API.Message; @@ -7,10 +6,8 @@ using Model = Discord.API.Channel; namespace Discord.WebSocket { - internal class SocketDMChannel : DMChannel, IDMChannel, ISocketChannel, ISocketMessageChannel, ISocketPrivateChannel + internal class DMChannel : IDMChannel, ISocketChannel, ISocketMessageChannel, ISocketPrivateChannel { - internal override bool IsAttached => true; - private readonly MessageManager _messages; public new DiscordSocketClient Discord => base.Discord as DiscordSocketClient; diff --git a/src/Discord.Net/WebSocket/Entities/Channels/SocketGroupChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/SocketGroupChannel.cs similarity index 93% rename from src/Discord.Net/WebSocket/Entities/Channels/SocketGroupChannel.cs rename to src/Discord.Net.WebSocket/Entities/Channels/SocketGroupChannel.cs index 8d456bb55..cbe6d9c8f 100644 --- a/src/Discord.Net/WebSocket/Entities/Channels/SocketGroupChannel.cs +++ b/src/Discord.Net.WebSocket/Entities/Channels/SocketGroupChannel.cs @@ -11,7 +11,7 @@ using VoiceStateModel = Discord.API.VoiceState; namespace Discord.WebSocket { - internal class SocketGroupChannel : GroupChannel, IGroupChannel, ISocketChannel, ISocketMessageChannel, ISocketPrivateChannel + internal class SocketGroupChannel : IGroupChannel, ISocketChannel, ISocketMessageChannel, ISocketPrivateChannel { internal override bool IsAttached => true; @@ -32,14 +32,14 @@ namespace Discord.WebSocket _messages = new MessageManager(Discord, this); _voiceStates = new ConcurrentDictionary(1, 5); } - public override void Update(Model model, UpdateSource source) + public override void Update(Model model) { if (source == UpdateSource.Rest && IsAttached) return; base.Update(model, source); } - internal void UpdateUsers(UserModel[] models, UpdateSource source, DataStore dataStore) + internal void UpdateUsers(UserModel[] models, DataStore dataStore) { var users = new ConcurrentDictionary(1, models.Length); for (int i = 0; i < models.Length; i++) @@ -49,7 +49,7 @@ namespace Discord.WebSocket } _users = users; } - internal override void UpdateUsers(UserModel[] models, UpdateSource source) + internal override void UpdateUsers(UserModel[] models) => UpdateUsers(models, source, Discord.DataStore); public SocketGroupUser AddUser(UserModel model, DataStore dataStore) diff --git a/src/Discord.Net/Rest/Entities/Channels/GuildChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/SocketGuildChannel.cs similarity index 96% rename from src/Discord.Net/Rest/Entities/Channels/GuildChannel.cs rename to src/Discord.Net.WebSocket/Entities/Channels/SocketGuildChannel.cs index 6b5777570..613a79064 100644 --- a/src/Discord.Net/Rest/Entities/Channels/GuildChannel.cs +++ b/src/Discord.Net.WebSocket/Entities/Channels/SocketGuildChannel.cs @@ -10,7 +10,7 @@ using Model = Discord.API.Channel; namespace Discord.Rest { [DebuggerDisplay(@"{DebuggerDisplay,nq}")] - internal abstract class GuildChannel : SnowflakeEntity, IGuildChannel + internal abstract class SocketGuildChannel : ISnowflakeEntity, IGuildChannel { private List _overwrites; //TODO: Is maintaining a list here too expensive? Is this threadsafe? @@ -26,9 +26,9 @@ namespace Discord.Rest { Guild = guild; - Update(model, UpdateSource.Creation); + Update(model); } - public virtual void Update(Model model, UpdateSource source) + public virtual void Update(Model model) { if (source == UpdateSource.Rest && IsAttached) return; diff --git a/src/Discord.Net/WebSocket/Entities/Channels/SocketTextChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/SocketTextChannel.cs similarity index 100% rename from src/Discord.Net/WebSocket/Entities/Channels/SocketTextChannel.cs rename to src/Discord.Net.WebSocket/Entities/Channels/SocketTextChannel.cs diff --git a/src/Discord.Net/WebSocket/Entities/Channels/SocketVoiceChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/SocketVoiceChannel.cs similarity index 100% rename from src/Discord.Net/WebSocket/Entities/Channels/SocketVoiceChannel.cs rename to src/Discord.Net.WebSocket/Entities/Channels/SocketVoiceChannel.cs diff --git a/src/Discord.Net/WebSocket/Entities/Guilds/SocketGuild.cs b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs similarity index 98% rename from src/Discord.Net/WebSocket/Entities/Guilds/SocketGuild.cs rename to src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs index ea0eb07a9..7ddab5ad6 100644 --- a/src/Discord.Net/WebSocket/Entities/Guilds/SocketGuild.cs +++ b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs @@ -60,10 +60,10 @@ namespace Discord.WebSocket _audioLock = new SemaphoreSlim(1, 1); _syncPromise = new TaskCompletionSource(); _downloaderPromise = new TaskCompletionSource(); - Update(model, UpdateSource.Creation, dataStore); + Update(model, dataStore); } - public void Update(ExtendedModel model, UpdateSource source, DataStore dataStore) + public void Update(ExtendedModel model, DataStore dataStore) { if (source == UpdateSource.Rest && IsAttached) return; @@ -117,7 +117,7 @@ namespace Discord.WebSocket } _voiceStates = voiceStates; } - public void Update(GuildSyncModel model, UpdateSource source, DataStore dataStore) + public void Update(GuildSyncModel model, DataStore dataStore) { if (source == UpdateSource.Rest && IsAttached) return; @@ -136,7 +136,7 @@ namespace Discord.WebSocket _members = members; } - public void Update(EmojiUpdateModel model, UpdateSource source) + public void Update(EmojiUpdateModel model) { if (source == UpdateSource.Rest && IsAttached) return; diff --git a/src/Discord.Net/Rest/Entities/Guilds/GuildIntegration.cs b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuildIntegration.cs similarity index 90% rename from src/Discord.Net/Rest/Entities/Guilds/GuildIntegration.cs rename to src/Discord.Net.WebSocket/Entities/Guilds/SocketGuildIntegration.cs index 135c0f3f3..bd65a3cc8 100644 --- a/src/Discord.Net/Rest/Entities/Guilds/GuildIntegration.cs +++ b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuildIntegration.cs @@ -1,5 +1,4 @@ using Discord.API.Rest; -using Discord.Rest; using System; using System.Diagnostics; using System.Threading.Tasks; @@ -8,7 +7,7 @@ using Model = Discord.API.Integration; namespace Discord.Rest { [DebuggerDisplay(@"{DebuggerDisplay,nq}")] - internal class GuildIntegration : Entity, IGuildIntegration + internal class GuildIntegration : IEntity, IGuildIntegration { private long _syncedAtTicks; @@ -31,13 +30,11 @@ namespace Discord.Rest : base(model.Id) { Guild = guild; - Update(model, UpdateSource.Creation); + Update(model); } - public void Update(Model model, UpdateSource source) + public void Update(Model model) { - if (source == UpdateSource.Rest && IsAttached) return; - Name = model.Name; Type = model.Type; IsEnabled = model.Enabled; @@ -49,7 +46,7 @@ namespace Discord.Rest Role = Guild.GetRole(model.RoleId); User = new User(model.User); } - + public async Task DeleteAsync() { await Discord.ApiClient.DeleteGuildIntegrationAsync(Guild.Id, Id).ConfigureAwait(false); diff --git a/src/Discord.Net/WebSocket/Entities/Messages/ISocketMessage.cs b/src/Discord.Net.WebSocket/Entities/Messages/ISocketMessage.cs similarity index 82% rename from src/Discord.Net/WebSocket/Entities/Messages/ISocketMessage.cs rename to src/Discord.Net.WebSocket/Entities/Messages/ISocketMessage.cs index 818d62ec0..c4f112b77 100644 --- a/src/Discord.Net/WebSocket/Entities/Messages/ISocketMessage.cs +++ b/src/Discord.Net.WebSocket/Entities/Messages/ISocketMessage.cs @@ -7,7 +7,7 @@ namespace Discord.WebSocket DiscordSocketClient Discord { get; } new ISocketMessageChannel Channel { get; } - void Update(Model model, UpdateSource source); + void Update(Model model); ISocketMessage Clone(); } } diff --git a/src/Discord.Net/WebSocket/Entities/Messages/SocketSystemMessage.cs b/src/Discord.Net.WebSocket/Entities/Messages/SocketSystemMessage.cs similarity index 100% rename from src/Discord.Net/WebSocket/Entities/Messages/SocketSystemMessage.cs rename to src/Discord.Net.WebSocket/Entities/Messages/SocketSystemMessage.cs diff --git a/src/Discord.Net/WebSocket/Entities/Messages/SocketUserMessage.cs b/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs similarity index 100% rename from src/Discord.Net/WebSocket/Entities/Messages/SocketUserMessage.cs rename to src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs diff --git a/src/Discord.Net/WebSocket/Entities/Users/ISocketUser.cs b/src/Discord.Net.WebSocket/Entities/Users/ISocketUser.cs similarity index 100% rename from src/Discord.Net/WebSocket/Entities/Users/ISocketUser.cs rename to src/Discord.Net.WebSocket/Entities/Users/ISocketUser.cs diff --git a/src/Discord.Net/WebSocket/Entities/Users/Presence.cs b/src/Discord.Net.WebSocket/Entities/Users/Presence.cs similarity index 100% rename from src/Discord.Net/WebSocket/Entities/Users/Presence.cs rename to src/Discord.Net.WebSocket/Entities/Users/Presence.cs diff --git a/src/Discord.Net/WebSocket/Entities/Users/SocketDMUser.cs b/src/Discord.Net.WebSocket/Entities/Users/SocketDMUser.cs similarity index 95% rename from src/Discord.Net/WebSocket/Entities/Users/SocketDMUser.cs rename to src/Discord.Net.WebSocket/Entities/Users/SocketDMUser.cs index b996ce94c..d68a395f6 100644 --- a/src/Discord.Net/WebSocket/Entities/Users/SocketDMUser.cs +++ b/src/Discord.Net.WebSocket/Entities/Users/SocketDMUser.cs @@ -32,7 +32,7 @@ namespace Discord.WebSocket User = user; } - public void Update(PresenceModel model, UpdateSource source) + public void Update(PresenceModel model) { User.Update(model, source); } diff --git a/src/Discord.Net/WebSocket/Entities/Users/SocketGlobalUser.cs b/src/Discord.Net.WebSocket/Entities/Users/SocketGlobalUser.cs similarity index 91% rename from src/Discord.Net/WebSocket/Entities/Users/SocketGlobalUser.cs rename to src/Discord.Net.WebSocket/Entities/Users/SocketGlobalUser.cs index 3835dcc46..53180ebfc 100644 --- a/src/Discord.Net/WebSocket/Entities/Users/SocketGlobalUser.cs +++ b/src/Discord.Net.WebSocket/Entities/Users/SocketGlobalUser.cs @@ -38,12 +38,12 @@ namespace Discord.WebSocket } } - public override void Update(Model model, UpdateSource source) + public override void Update(Model model) { lock (this) base.Update(model, source); } - public void Update(PresenceModel model, UpdateSource source) + public void Update(PresenceModel model) { //Race conditions are okay here. Multiple shards racing already cant guarantee presence in order. diff --git a/src/Discord.Net/WebSocket/Entities/Users/SocketGroupUser.cs b/src/Discord.Net.WebSocket/Entities/Users/SocketGroupUser.cs similarity index 100% rename from src/Discord.Net/WebSocket/Entities/Users/SocketGroupUser.cs rename to src/Discord.Net.WebSocket/Entities/Users/SocketGroupUser.cs diff --git a/src/Discord.Net/WebSocket/Entities/Users/SocketGuildUser.cs b/src/Discord.Net.WebSocket/Entities/Users/SocketGuildUser.cs similarity index 96% rename from src/Discord.Net/WebSocket/Entities/Users/SocketGuildUser.cs rename to src/Discord.Net.WebSocket/Entities/Users/SocketGuildUser.cs index ab01956cc..3b38eb213 100644 --- a/src/Discord.Net/WebSocket/Entities/Users/SocketGuildUser.cs +++ b/src/Discord.Net.WebSocket/Entities/Users/SocketGuildUser.cs @@ -35,7 +35,7 @@ namespace Discord.WebSocket { } - public override void Update(PresenceModel model, UpdateSource source) + public override void Update(PresenceModel model) { base.Update(model, source); diff --git a/src/Discord.Net/WebSocket/Entities/Users/SocketSelfUser.cs b/src/Discord.Net.WebSocket/Entities/Users/SocketSelfUser.cs similarity index 100% rename from src/Discord.Net/WebSocket/Entities/Users/SocketSelfUser.cs rename to src/Discord.Net.WebSocket/Entities/Users/SocketSelfUser.cs diff --git a/src/Discord.Net/WebSocket/Entities/Users/VoiceState.cs b/src/Discord.Net.WebSocket/Entities/Users/VoiceState.cs similarity index 100% rename from src/Discord.Net/WebSocket/Entities/Users/VoiceState.cs rename to src/Discord.Net.WebSocket/Entities/Users/VoiceState.cs diff --git a/src/Discord.Net/WebSocket/Entities/Channels/MessageCache.cs b/src/Discord.Net.WebSocket/Entities/Utilities/MessageCache.cs similarity index 100% rename from src/Discord.Net/WebSocket/Entities/Channels/MessageCache.cs rename to src/Discord.Net.WebSocket/Entities/Utilities/MessageCache.cs diff --git a/src/Discord.Net/WebSocket/Entities/Channels/MessageManager.cs b/src/Discord.Net.WebSocket/Entities/Utilities/MessageManager.cs similarity index 100% rename from src/Discord.Net/WebSocket/Entities/Channels/MessageManager.cs rename to src/Discord.Net.WebSocket/Entities/Utilities/MessageManager.cs diff --git a/src/Discord.Net.WebSocket/Properties/AssemblyInfo.cs b/src/Discord.Net.WebSocket/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..2aae053d2 --- /dev/null +++ b/src/Discord.Net.WebSocket/Properties/AssemblyInfo.cs @@ -0,0 +1,19 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Discord.Net.WebSocket")] +[assembly: AssemblyTrademark("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("22ab6c66-536c-4ac2-bbdb-a8bc4eb6b14d")] diff --git a/src/Discord.Net.WebSocket/project.json b/src/Discord.Net.WebSocket/project.json new file mode 100644 index 000000000..c52db6ba9 --- /dev/null +++ b/src/Discord.Net.WebSocket/project.json @@ -0,0 +1,20 @@ +{ + "version": "1.0.0-*", + + "buildOptions": { + "compile": { + "include": [ "../Discord.Net.Entities/**.cs", "../Discord.Net.Utils/**.cs" ] + }, + "define": [ "WEBSOCKET" ] + }, + + "dependencies": { + "NETStandard.Library": "1.6.0" + }, + + "frameworks": { + "netstandard1.6": { + "imports": "dnxcore50" + } + } +} diff --git a/src/Discord.Net/API/Rest/CreateChannelInviteParams.cs b/src/Discord.Net/API/Rest/CreateChannelInviteParams.cs deleted file mode 100644 index 0899a7e68..000000000 --- a/src/Discord.Net/API/Rest/CreateChannelInviteParams.cs +++ /dev/null @@ -1,21 +0,0 @@ -#pragma warning disable CS1591 -using Newtonsoft.Json; - -namespace Discord.API.Rest -{ - [JsonObject(MemberSerialization = MemberSerialization.OptIn)] - public class CreateChannelInviteParams - { - [JsonProperty("max_age")] - internal Optional _maxAge { get; set; } - public int MaxAge { set { _maxAge = value; } } - - [JsonProperty("max_uses")] - internal Optional _maxUses { get; set; } - public int MaxUses { set { _maxUses = value; } } - - [JsonProperty("temporary")] - internal Optional _temporary { get; set; } - public bool Temporary { set { _temporary = value; } } - } -} diff --git a/src/Discord.Net/API/Rest/CreateGuildChannelParams.cs b/src/Discord.Net/API/Rest/CreateGuildChannelParams.cs deleted file mode 100644 index fe1428cf8..000000000 --- a/src/Discord.Net/API/Rest/CreateGuildChannelParams.cs +++ /dev/null @@ -1,21 +0,0 @@ -#pragma warning disable CS1591 -using Newtonsoft.Json; - -namespace Discord.API.Rest -{ - [JsonObject(MemberSerialization = MemberSerialization.OptIn)] - public class CreateGuildChannelParams - { - [JsonProperty("name")] - internal string _name { get; set; } - public string Name { set { _name = value; } } - - [JsonProperty("type")] - internal ChannelType _type { get; set; } - public ChannelType Type { set { _type = value; } } - - [JsonProperty("bitrate")] - internal Optional _bitrate { get; set; } - public int Bitrate { set { _bitrate = value; } } - } -} diff --git a/src/Discord.Net/API/Rest/CreateMessageParams.cs b/src/Discord.Net/API/Rest/CreateMessageParams.cs deleted file mode 100644 index 1a586c4cf..000000000 --- a/src/Discord.Net/API/Rest/CreateMessageParams.cs +++ /dev/null @@ -1,21 +0,0 @@ -#pragma warning disable CS1591 -using Newtonsoft.Json; - -namespace Discord.API.Rest -{ - [JsonObject(MemberSerialization = MemberSerialization.OptIn)] - public class CreateMessageParams - { - [JsonProperty("content")] - internal string _content { get; set; } - public string Content { set { _content = value; } } - - [JsonProperty("nonce")] - internal Optional _nonce { get; set; } - public string Nonce { set { _nonce = value; } } - - [JsonProperty("tts")] - internal Optional _tts { get; set; } - public bool IsTTS { set { _tts = value; } } - } -} diff --git a/src/Discord.Net/API/Rest/DeleteMessagesParams.cs b/src/Discord.Net/API/Rest/DeleteMessagesParams.cs deleted file mode 100644 index 89e279f13..000000000 --- a/src/Discord.Net/API/Rest/DeleteMessagesParams.cs +++ /dev/null @@ -1,16 +0,0 @@ -#pragma warning disable CS1591 -using Newtonsoft.Json; -using System.Collections.Generic; -using System.Linq; - -namespace Discord.API.Rest -{ - [JsonObject(MemberSerialization = MemberSerialization.OptIn)] - public class DeleteMessagesParams - { - [JsonProperty("messages")] - internal ulong[] _messages { get; set; } - public IEnumerable MessageIds { set { _messages = value.ToArray(); } } - public IEnumerable Messages { set { _messages = value.Select(x => x.Id).ToArray(); } } - } -} diff --git a/src/Discord.Net/API/Rest/GetChannelMessagesParams.cs b/src/Discord.Net/API/Rest/GetChannelMessagesParams.cs deleted file mode 100644 index 68660ab01..000000000 --- a/src/Discord.Net/API/Rest/GetChannelMessagesParams.cs +++ /dev/null @@ -1,14 +0,0 @@ -#pragma warning disable CS1591 -namespace Discord.API.Rest -{ - public class GetChannelMessagesParams - { - public int Limit { internal get; set; } = DiscordConfig.MaxMessagesPerBatch; - - public Direction RelativeDirection { internal get; set; } = Direction.Before; - - internal Optional _relativeMessageId { get; set; } - public ulong RelativeMessageId { set { _relativeMessageId = value; } } - public IMessage RelativeMessage { set { _relativeMessageId = value.Id; } } - } -} diff --git a/src/Discord.Net/API/Rest/GetGuildMembersParams.cs b/src/Discord.Net/API/Rest/GetGuildMembersParams.cs deleted file mode 100644 index cefca801e..000000000 --- a/src/Discord.Net/API/Rest/GetGuildMembersParams.cs +++ /dev/null @@ -1,12 +0,0 @@ -#pragma warning disable CS1591 -namespace Discord.API.Rest -{ - public class GetGuildMembersParams - { - internal Optional _limit { get; set; } - public int Limit { set { _limit = value; } } - - internal Optional _afterUserId { get; set; } - public ulong AfterUserId { set { _afterUserId = value; } } - } -} diff --git a/src/Discord.Net/API/Rest/ModifyGuildEmbedParams.cs b/src/Discord.Net/API/Rest/ModifyGuildEmbedParams.cs deleted file mode 100644 index 84a7f28ca..000000000 --- a/src/Discord.Net/API/Rest/ModifyGuildEmbedParams.cs +++ /dev/null @@ -1,18 +0,0 @@ -#pragma warning disable CS1591 -using Newtonsoft.Json; - -namespace Discord.API.Rest -{ - [JsonObject(MemberSerialization = MemberSerialization.OptIn)] - public class ModifyGuildEmbedParams - { - [JsonProperty("enabled")] - internal Optional _enabled { get; set; } - public bool Enabled { set { _enabled = value; } } - - [JsonProperty("channel")] - internal Optional _channelId { get; set; } - public ulong? ChannelId { set { _channelId = value; } } - public IVoiceChannel Channel { set { _channelId = value != null ? value.Id : (ulong?)null; } } - } -} diff --git a/src/Discord.Net/API/Rest/ModifyGuildIntegrationParams.cs b/src/Discord.Net/API/Rest/ModifyGuildIntegrationParams.cs deleted file mode 100644 index 9b280f8ae..000000000 --- a/src/Discord.Net/API/Rest/ModifyGuildIntegrationParams.cs +++ /dev/null @@ -1,21 +0,0 @@ -#pragma warning disable CS1591 -using Newtonsoft.Json; - -namespace Discord.API.Rest -{ - [JsonObject(MemberSerialization = MemberSerialization.OptIn)] - public class ModifyGuildIntegrationParams - { - [JsonProperty("expire_behavior")] - internal Optional _expireBehavior { get; set; } - public int ExpireBehavior { set { _expireBehavior = value; } } - - [JsonProperty("expire_grace_period")] - internal Optional _expireGracePeriod { get; set; } - public int ExpireGracePeriod { set { _expireGracePeriod = value; } } - - [JsonProperty("enable_emoticons")] - internal Optional _enableEmoticons { get; set; } - public bool EnableEmoticons { set { _enableEmoticons = value; } } - } -} diff --git a/src/Discord.Net/API/Rest/ModifyGuildMemberParams.cs b/src/Discord.Net/API/Rest/ModifyGuildMemberParams.cs deleted file mode 100644 index 4377efca2..000000000 --- a/src/Discord.Net/API/Rest/ModifyGuildMemberParams.cs +++ /dev/null @@ -1,33 +0,0 @@ -#pragma warning disable CS1591 -using Newtonsoft.Json; -using System.Collections.Generic; -using System.Linq; - -namespace Discord.API.Rest -{ - [JsonObject(MemberSerialization = MemberSerialization.OptIn)] - public class ModifyGuildMemberParams - { - [JsonProperty("mute")] - internal Optional _mute { get; set; } - public bool Mute { set { _mute = value; } } - - [JsonProperty("deaf")] - internal Optional _deaf { get; set; } - public bool Deaf { set { _deaf = value; } } - - [JsonProperty("nick")] - internal Optional _nickname { get; set; } - public string Nickname { set { _nickname = value; } } - - [JsonProperty("roles")] - internal Optional _roleIds { get; set; } - public IEnumerable RoleIds { set { _roleIds = value.ToArray(); } } - public IEnumerable Roles { set { _roleIds = value.Select(x => x.Id).ToArray(); } } - - [JsonProperty("channel_id")] - internal Optional _channelId { get; set; } - public ulong VoiceChannelId { set { _channelId = value; } } - public IVoiceChannel VoiceChannel { set { _channelId = value.Id; } } - } -} diff --git a/src/Discord.Net/API/Rest/ModifyGuildParams.cs b/src/Discord.Net/API/Rest/ModifyGuildParams.cs deleted file mode 100644 index e7e8219bd..000000000 --- a/src/Discord.Net/API/Rest/ModifyGuildParams.cs +++ /dev/null @@ -1,52 +0,0 @@ -#pragma warning disable CS1591 -using Newtonsoft.Json; -using System.IO; - -namespace Discord.API.Rest -{ - [JsonObject(MemberSerialization = MemberSerialization.OptIn)] - public class ModifyGuildParams - { - [JsonProperty("username")] - internal Optional _username { get; set; } - public string Username { set { _username = value; } } - - [JsonProperty("name")] - internal Optional _name { get; set; } - public string Name { set { _name = value; } } - - [JsonProperty("region")] - internal Optional _region { get; set; } - public IVoiceRegion Region { set { _region = Optional.Create(value); } } - - [JsonProperty("verification_level")] - internal Optional _verificationLevel { get; set; } - public VerificationLevel VerificationLevel { set { _verificationLevel = value; } } - - [JsonProperty("default_message_notifications")] - internal Optional _defaultMessageNotifications { get; set; } - public DefaultMessageNotifications DefaultMessageNotifications { set { _defaultMessageNotifications = value; } } - - [JsonProperty("afk_timeout")] - internal Optional _afkTimeout { get; set; } - public int AFKTimeout { set { _afkTimeout = value; } } - - [JsonProperty("icon")] - internal Optional _icon { get; set; } - public Stream Icon { set { _icon = value != null ? new Image(value) : (Image?)null; } } - - [JsonProperty("splash")] - internal Optional _splash { get; set; } - public Stream Splash { set { _splash = value != null ? new Image(value) : (Image?)null; } } - - [JsonProperty("afk_channel_id")] - internal Optional _afkChannelId { get; set; } - public ulong? AFKChannelId { set { _afkChannelId = value; } } - public IVoiceChannel AFKChannel { set { _afkChannelId = value?.Id; } } - - [JsonProperty("owner_id")] - internal Optional _ownerId { get; set; } - public ulong OwnerId { set { _ownerId = value; } } - public IGuildUser Owner { set { _ownerId = value.Id; } } - } -} diff --git a/src/Discord.Net/API/Rest/ModifyGuildRoleParams.cs b/src/Discord.Net/API/Rest/ModifyGuildRoleParams.cs deleted file mode 100644 index a9e7eeb3d..000000000 --- a/src/Discord.Net/API/Rest/ModifyGuildRoleParams.cs +++ /dev/null @@ -1,29 +0,0 @@ -#pragma warning disable CS1591 -using Newtonsoft.Json; - -namespace Discord.API.Rest -{ - [JsonObject(MemberSerialization = MemberSerialization.OptIn)] - public class ModifyGuildRoleParams - { - [JsonProperty("name")] - internal Optional _name { get; set; } - public string Name { set { _name = value; } } - - [JsonProperty("permissions")] - internal Optional _permissions { get; set; } - public ulong Permissions { set { _permissions = value; } } - - [JsonProperty("position")] - internal Optional _position { get; set; } - public int Position { set { _position = value; } } - - [JsonProperty("color")] - internal Optional _color { get; set; } - public uint Color { set { _color = value; } } - - [JsonProperty("hoist")] - internal Optional _hoist { get; set; } - public bool Hoist { set { _hoist = value; } } - } -} diff --git a/src/Discord.Net/API/Rest/ModifyPresenceParams.cs b/src/Discord.Net/API/Rest/ModifyPresenceParams.cs deleted file mode 100644 index 00d64bfd6..000000000 --- a/src/Discord.Net/API/Rest/ModifyPresenceParams.cs +++ /dev/null @@ -1,12 +0,0 @@ -#pragma warning disable CS1591 -namespace Discord.API.Rest -{ - public class ModifyPresenceParams - { - internal Optional _status { get; set; } - public UserStatus Status { set { _status = value; } } - - internal Optional _game { get; set; } - public Discord.Game Game { set { _game = value; } } - } -} diff --git a/src/Discord.Net/API/Rest/UploadFileParams.cs b/src/Discord.Net/API/Rest/UploadFileParams.cs deleted file mode 100644 index b454068d7..000000000 --- a/src/Discord.Net/API/Rest/UploadFileParams.cs +++ /dev/null @@ -1,42 +0,0 @@ -#pragma warning disable CS1591 -using Discord.Net.Rest; -using System.Collections.Generic; -using System.IO; - -namespace Discord.API.Rest -{ - public class UploadFileParams - { - public Stream File { internal get; set; } - - internal Optional _filename { get; set; } - public string Filename { set { _filename = value; } } - - internal Optional _content { get; set; } - public string Content { set { _content = value; } } - - internal Optional _nonce { get; set; } - public string Nonce { set { _nonce = value; } } - - internal Optional _isTTS { get; set; } - public bool IsTTS { set { _isTTS = value; } } - - public UploadFileParams(Stream file) - { - File = file; - } - - internal IReadOnlyDictionary ToDictionary() - { - var d = new Dictionary(); - d["file"] = new MultipartFile(File, _filename.GetValueOrDefault("unknown.dat")); - if (_content.IsSpecified) - d["content"] = _content.Value; - if (_isTTS.IsSpecified) - d["tts"] = _isTTS.Value.ToString(); - if (_nonce.IsSpecified) - d["nonce"] = _nonce.Value; - return d; - } - } -} diff --git a/src/Discord.Net/Discord.Net.xproj b/src/Discord.Net/Discord.Net.xproj index 6759e09b4..079338b62 100644 --- a/src/Discord.Net/Discord.Net.xproj +++ b/src/Discord.Net/Discord.Net.xproj @@ -6,11 +6,11 @@ - 91e9e7bd-75c9-4e98-84aa-2c271922e5c2 - Discord + 496db20a-a455-4d01-b6bc-90fe6d7c6b81 + Discord.Net .\obj .\bin\ - v4.5.2 + v4.6.1 2.0 diff --git a/src/Discord.Net/Entities/Channels/IGroupChannel.cs b/src/Discord.Net/Entities/Channels/IGroupChannel.cs deleted file mode 100644 index 6b71f76b6..000000000 --- a/src/Discord.Net/Entities/Channels/IGroupChannel.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.Threading.Tasks; - -namespace Discord -{ - public interface IGroupChannel : IMessageChannel, IPrivateChannel - { - /// Adds a user to this group. - Task AddUserAsync(IUser user); - - /// Leaves this group. - Task LeaveAsync(); - } -} \ No newline at end of file diff --git a/src/Discord.Net/Entities/Guilds/Ban.cs b/src/Discord.Net/Entities/Guilds/Ban.cs deleted file mode 100644 index 5d9d3df07..000000000 --- a/src/Discord.Net/Entities/Guilds/Ban.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System.Diagnostics; - -namespace Discord -{ - [DebuggerDisplay(@"{DebuggerDisplay,nq}")] - public struct Ban - { - public IUser User { get; } - public string Reason { get; } - - public Ban(IUser user, string reason) - { - User = user; - Reason = reason; - } - - public override string ToString() => User.ToString(); - private string DebuggerDisplay => $"{User}: {Reason}"; - } -} diff --git a/src/Discord.Net/Entities/Guilds/Emoji.cs b/src/Discord.Net/Entities/Guilds/Emoji.cs deleted file mode 100644 index 19ee306b0..000000000 --- a/src/Discord.Net/Entities/Guilds/Emoji.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System.Collections.Immutable; -using System.Diagnostics; -using Model = Discord.API.Emoji; - -namespace Discord -{ - [DebuggerDisplay(@"{DebuggerDisplay,nq}")] - public struct Emoji - { - public ulong Id { get; } - public string Name { get; } - public bool IsManaged { get; } - public bool RequireColons { get; } - public IImmutableList RoleIds { get; } - - public Emoji(Model model) - { - Id = model.Id; - Name = model.Name; - IsManaged = model.Managed; - RequireColons = model.RequireColons; - RoleIds = ImmutableArray.Create(model.Roles); - } - - public override string ToString() => Name; - private string DebuggerDisplay => $"{Name} ({Id})"; - } -} diff --git a/src/Discord.Net/Entities/IEntity.cs b/src/Discord.Net/Entities/IEntity.cs deleted file mode 100644 index d6d97626d..000000000 --- a/src/Discord.Net/Entities/IEntity.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; - -namespace Discord -{ - public interface IEntity - where TId : IEquatable - { - /// Gets the unique identifier for this object. - TId Id { get; } - - //TODO: What do we do when an object is destroyed due to reconnect? This summary isn't correct. - /// Returns true if this object is getting live updates from the DiscordClient. - bool IsAttached { get;} - } -} diff --git a/src/Discord.Net/Entities/ISnowflakeEntity.cs b/src/Discord.Net/Entities/ISnowflakeEntity.cs deleted file mode 100644 index 60623425c..000000000 --- a/src/Discord.Net/Entities/ISnowflakeEntity.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System; - -namespace Discord -{ - public interface ISnowflakeEntity : IEntity - { - /// Gets when this object was created. - DateTimeOffset CreatedAt { get; } - } -} diff --git a/src/Discord.Net/Entities/Messages/ISystemMessage.cs b/src/Discord.Net/Entities/Messages/ISystemMessage.cs deleted file mode 100644 index d2e23d147..000000000 --- a/src/Discord.Net/Entities/Messages/ISystemMessage.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Discord -{ - public interface ISystemMessage : IMessage - { - /// Gets the type of this system message. - MessageType Type { get; } - } -} diff --git a/src/Discord.Net/Entities/UpdateSource.cs b/src/Discord.Net/Entities/UpdateSource.cs deleted file mode 100644 index 6c56416e7..000000000 --- a/src/Discord.Net/Entities/UpdateSource.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Discord -{ - internal enum UpdateSource - { - Creation, - Rest, - WebSocket - } -} diff --git a/src/Discord.Net/Entities/Users/IGroupUser.cs b/src/Discord.Net/Entities/Users/IGroupUser.cs deleted file mode 100644 index 8ed53616c..000000000 --- a/src/Discord.Net/Entities/Users/IGroupUser.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.Threading.Tasks; - -namespace Discord -{ - public interface IGroupUser : IUser - { - /// Kicks this user from this group. - Task KickAsync(); - - /// Returns a private message channel to this user, creating one if it does not already exist. - Task CreateDMChannelAsync(); - } -} diff --git a/src/Discord.Net/Logging/ILogManager.cs b/src/Discord.Net/Logging/ILogManager.cs deleted file mode 100644 index b244419b9..000000000 --- a/src/Discord.Net/Logging/ILogManager.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System; -using System.Threading.Tasks; - -namespace Discord.Logging -{ - public interface ILogManager - { - LogSeverity Level { get; } - - Task LogAsync(LogSeverity severity, string source, string message, Exception ex = null); - Task LogAsync(LogSeverity severity, string source, FormattableString message, Exception ex = null); - Task LogAsync(LogSeverity severity, string source, Exception ex); - - Task ErrorAsync(string source, string message, Exception ex = null); - Task ErrorAsync(string source, FormattableString message, Exception ex = null); - Task ErrorAsync(string source, Exception ex); - - Task WarningAsync(string source, string message, Exception ex = null); - Task WarningAsync(string source, FormattableString message, Exception ex = null); - Task WarningAsync(string source, Exception ex); - - Task InfoAsync(string source, string message, Exception ex = null); - Task InfoAsync(string source, FormattableString message, Exception ex = null); - Task InfoAsync(string source, Exception ex); - - Task VerboseAsync(string source, string message, Exception ex = null); - Task VerboseAsync(string source, FormattableString message, Exception ex = null); - Task VerboseAsync(string source, Exception ex); - - Task DebugAsync(string source, string message, Exception ex = null); - Task DebugAsync(string source, FormattableString message, Exception ex = null); - Task DebugAsync(string source, Exception ex); - - ILogger CreateLogger(string name); - } -} diff --git a/src/Discord.Net/Logging/ILogger.cs b/src/Discord.Net/Logging/ILogger.cs deleted file mode 100644 index 207c03dc7..000000000 --- a/src/Discord.Net/Logging/ILogger.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System; -using System.Threading.Tasks; - -namespace Discord.Logging -{ - public interface ILogger - { - LogSeverity Level { get; } - - Task LogAsync(LogSeverity severity, string message, Exception exception = null); - Task LogAsync(LogSeverity severity, FormattableString message, Exception exception = null); - Task LogAsync(LogSeverity severity, Exception exception); - - Task ErrorAsync(string message, Exception exception = null); - Task ErrorAsync(FormattableString message, Exception exception = null); - Task ErrorAsync(Exception exception); - - Task WarningAsync(string message, Exception exception = null); - Task WarningAsync(FormattableString message, Exception exception = null); - Task WarningAsync(Exception exception); - - Task InfoAsync(string message, Exception exception = null); - Task InfoAsync(FormattableString message, Exception exception = null); - Task InfoAsync(Exception exception); - - Task VerboseAsync(string message, Exception exception = null); - Task VerboseAsync(FormattableString message, Exception exception = null); - Task VerboseAsync(Exception exception); - - Task DebugAsync(string message, Exception exception = null); - Task DebugAsync(FormattableString message, Exception exception = null); - Task DebugAsync(Exception exception); - } -} diff --git a/src/Discord.Net/Properties/AssemblyInfo.cs b/src/Discord.Net/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..e23324a59 --- /dev/null +++ b/src/Discord.Net/Properties/AssemblyInfo.cs @@ -0,0 +1,19 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Discord.Net")] +[assembly: AssemblyTrademark("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("496db20a-a455-4d01-b6bc-90fe6d7c6b81")] diff --git a/src/Discord.Net/Rest/Entities/Channels/DMChannel.cs b/src/Discord.Net/Rest/Entities/Channels/DMChannel.cs deleted file mode 100644 index 6097a7f00..000000000 --- a/src/Discord.Net/Rest/Entities/Channels/DMChannel.cs +++ /dev/null @@ -1,139 +0,0 @@ -using Discord.API.Rest; -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Threading.Tasks; -using Model = Discord.API.Channel; -using MessageModel = Discord.API.Message; - -namespace Discord.Rest -{ - [DebuggerDisplay(@"{DebuggerDisplay,nq}")] - internal class DMChannel : SnowflakeEntity, IDMChannel - { - public override DiscordRestClient Discord { get; } - public IUser Recipient { get; private set; } - - public virtual IReadOnlyCollection CachedMessages => ImmutableArray.Create(); - IReadOnlyCollection IPrivateChannel.Recipients => ImmutableArray.Create(Recipient); - - public DMChannel(DiscordRestClient discord, IUser recipient, Model model) - : base(model.Id) - { - Discord = discord; - Recipient = recipient; - - Update(model, UpdateSource.Creation); - } - public void Update(Model model, UpdateSource source) - { - if (/*source == UpdateSource.Rest && */IsAttached) return; - - (Recipient as User).Update(model.Recipients.Value[0], source); - } - - public async Task UpdateAsync() - { - if (IsAttached) throw new NotSupportedException(); - - var model = await Discord.ApiClient.GetChannelAsync(Id).ConfigureAwait(false); - Update(model, UpdateSource.Rest); - } - public async Task CloseAsync() - { - await Discord.ApiClient.DeleteChannelAsync(Id).ConfigureAwait(false); - } - - public virtual async Task GetUserAsync(ulong id) - { - var currentUser = await Discord.GetCurrentUserAsync().ConfigureAwait(false); - if (id == Recipient.Id) - return Recipient; - else if (id == currentUser.Id) - return currentUser; - else - return null; - } - public virtual async Task> GetUsersAsync() - { - var currentUser = await Discord.GetCurrentUserAsync().ConfigureAwait(false); - return ImmutableArray.Create(currentUser, Recipient); - } - - public async Task SendMessageAsync(string text, bool isTTS) - { - var args = new CreateMessageParams { Content = text, IsTTS = isTTS }; - var model = await Discord.ApiClient.CreateDMMessageAsync(Id, args).ConfigureAwait(false); - return CreateOutgoingMessage(model); - } - public async Task SendFileAsync(string filePath, string text, bool isTTS) - { - string filename = Path.GetFileName(filePath); - using (var file = File.OpenRead(filePath)) - { - var args = new UploadFileParams(file) { Filename = filename, Content = text, IsTTS = isTTS }; - var model = await Discord.ApiClient.UploadDMFileAsync(Id, args).ConfigureAwait(false); - return CreateOutgoingMessage(model); - } - } - public async Task SendFileAsync(Stream stream, string filename, string text, bool isTTS) - { - var args = new UploadFileParams(stream) { Filename = filename, Content = text, IsTTS = isTTS }; - var model = await Discord.ApiClient.UploadDMFileAsync(Id, args).ConfigureAwait(false); - return CreateOutgoingMessage(model); - } - public virtual async Task GetMessageAsync(ulong id) - { - var model = await Discord.ApiClient.GetChannelMessageAsync(Id, id).ConfigureAwait(false); - if (model != null) - return CreateIncomingMessage(model); - return null; - } - public virtual async Task> GetMessagesAsync(int limit) - { - var args = new GetChannelMessagesParams { Limit = limit }; - var models = await Discord.ApiClient.GetChannelMessagesAsync(Id, args).ConfigureAwait(false); - return models.Select(x => CreateIncomingMessage(x)).ToImmutableArray(); - } - public virtual async Task> GetMessagesAsync(ulong fromMessageId, Direction dir, int limit) - { - var args = new GetChannelMessagesParams { Limit = limit, RelativeMessageId = fromMessageId, RelativeDirection = dir }; - var models = await Discord.ApiClient.GetChannelMessagesAsync(Id, args).ConfigureAwait(false); - return models.Select(x => CreateIncomingMessage(x)).ToImmutableArray(); - } - public async Task DeleteMessagesAsync(IEnumerable messages) - { - await Discord.ApiClient.DeleteDMMessagesAsync(Id, new DeleteMessagesParams { MessageIds = messages.Select(x => x.Id) }).ConfigureAwait(false); - } - public async Task> GetPinnedMessagesAsync() - { - var models = await Discord.ApiClient.GetPinsAsync(Id); - return models.Select(x => CreateIncomingMessage(x)).ToImmutableArray(); - } - - public async Task TriggerTypingAsync() - { - await Discord.ApiClient.TriggerTypingIndicatorAsync(Id).ConfigureAwait(false); - } - - private UserMessage CreateOutgoingMessage(MessageModel model) - { - return new UserMessage(this, new User(model.Author.Value), model); - } - private Message CreateIncomingMessage(MessageModel model) - { - if (model.Type == MessageType.Default) - return new UserMessage(this, new User(model.Author.Value), model); - else - return new SystemMessage(this, new User(model.Author.Value), model); - } - - public override string ToString() => '@' + Recipient.ToString(); - private string DebuggerDisplay => $"@{Recipient} ({Id}, DM)"; - - IMessage IMessageChannel.GetCachedMessage(ulong id) => null; - } -} diff --git a/src/Discord.Net/Rest/Entities/Channels/GroupChannel.cs b/src/Discord.Net/Rest/Entities/Channels/GroupChannel.cs deleted file mode 100644 index 3ed544087..000000000 --- a/src/Discord.Net/Rest/Entities/Channels/GroupChannel.cs +++ /dev/null @@ -1,164 +0,0 @@ -using Discord.API.Rest; -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Threading.Tasks; -using Model = Discord.API.Channel; -using MessageModel = Discord.API.Message; - -namespace Discord.Rest -{ - [DebuggerDisplay(@"{DebuggerDisplay,nq}")] - internal class GroupChannel : SnowflakeEntity, IGroupChannel - { - protected ConcurrentDictionary _users; - private string _iconId; - - public override DiscordRestClient Discord { get; } - public string Name { get; private set; } - - public IReadOnlyCollection Recipients => _users.ToReadOnlyCollection(); - public virtual IReadOnlyCollection CachedMessages => ImmutableArray.Create(); - public string IconUrl => API.CDN.GetChannelIconUrl(Id, _iconId); - - public GroupChannel(DiscordRestClient discord, Model model) - : base(model.Id) - { - Discord = discord; - - Update(model, UpdateSource.Creation); - } - public virtual void Update(Model model, UpdateSource source) - { - if (source == UpdateSource.Rest && IsAttached) return; - - if (model.Name.IsSpecified) - Name = model.Name.Value; - if (model.Icon.IsSpecified) - _iconId = model.Icon.Value; - - if (source != UpdateSource.Creation && model.Recipients.IsSpecified) - UpdateUsers(model.Recipients.Value, source); - } - - internal virtual void UpdateUsers(API.User[] models, UpdateSource source) - { - if (!IsAttached) - { - var users = new ConcurrentDictionary(1, (int)(models.Length * 1.05)); - for (int i = 0; i < models.Length; i++) - users[models[i].Id] = new GroupUser(this, new User(models[i])); - _users = users; - } - } - - public async Task UpdateAsync() - { - if (IsAttached) throw new NotSupportedException(); - - var model = await Discord.ApiClient.GetChannelAsync(Id).ConfigureAwait(false); - Update(model, UpdateSource.Rest); - } - public async Task LeaveAsync() - { - await Discord.ApiClient.DeleteChannelAsync(Id).ConfigureAwait(false); - } - - public async Task AddUserAsync(IUser user) - { - await Discord.ApiClient.AddGroupRecipientAsync(Id, user.Id).ConfigureAwait(false); - } - public async Task GetUserAsync(ulong id) - { - GroupUser user; - if (_users.TryGetValue(id, out user)) - return user; - var currentUser = await Discord.GetCurrentUserAsync().ConfigureAwait(false); - if (id == currentUser.Id) - return currentUser; - return null; - } - public async Task> GetUsersAsync() - { - var currentUser = await Discord.GetCurrentUserAsync().ConfigureAwait(false); - return _users.Select(x => x.Value).Concat(ImmutableArray.Create(currentUser)).ToReadOnlyCollection(_users); - } - - public async Task SendMessageAsync(string text, bool isTTS) - { - var args = new CreateMessageParams { Content = text, IsTTS = isTTS }; - var model = await Discord.ApiClient.CreateDMMessageAsync(Id, args).ConfigureAwait(false); - return CreateOutgoingMessage(model); - } - public async Task SendFileAsync(string filePath, string text, bool isTTS) - { - string filename = Path.GetFileName(filePath); - using (var file = File.OpenRead(filePath)) - { - var args = new UploadFileParams(file) { Filename = filename, Content = text, IsTTS = isTTS }; - var model = await Discord.ApiClient.UploadDMFileAsync(Id, args).ConfigureAwait(false); - return CreateOutgoingMessage(model); - } - } - public async Task SendFileAsync(Stream stream, string filename, string text, bool isTTS) - { - var args = new UploadFileParams(stream) { Filename = filename, Content = text, IsTTS = isTTS }; - var model = await Discord.ApiClient.UploadDMFileAsync(Id, args).ConfigureAwait(false); - return CreateOutgoingMessage(model); - } - public virtual async Task GetMessageAsync(ulong id) - { - var model = await Discord.ApiClient.GetChannelMessageAsync(Id, id).ConfigureAwait(false); - if (model != null) - return CreateIncomingMessage(model); - return null; - } - public virtual async Task> GetMessagesAsync(int limit) - { - var args = new GetChannelMessagesParams { Limit = limit }; - var models = await Discord.ApiClient.GetChannelMessagesAsync(Id, args).ConfigureAwait(false); - return models.Select(x => CreateIncomingMessage(x)).ToImmutableArray(); - } - public virtual async Task> GetMessagesAsync(ulong fromMessageId, Direction dir, int limit) - { - var args = new GetChannelMessagesParams { Limit = limit, RelativeMessageId = fromMessageId, RelativeDirection = dir }; - var models = await Discord.ApiClient.GetChannelMessagesAsync(Id, args).ConfigureAwait(false); - return models.Select(x => CreateIncomingMessage(x)).ToImmutableArray(); - } - public async Task DeleteMessagesAsync(IEnumerable messages) - { - await Discord.ApiClient.DeleteDMMessagesAsync(Id, new DeleteMessagesParams { MessageIds = messages.Select(x => x.Id) }).ConfigureAwait(false); - } - public async Task> GetPinnedMessagesAsync() - { - var models = await Discord.ApiClient.GetPinsAsync(Id); - return models.Select(x => CreateIncomingMessage(x)).ToImmutableArray(); - } - - public async Task TriggerTypingAsync() - { - await Discord.ApiClient.TriggerTypingIndicatorAsync(Id).ConfigureAwait(false); - } - - private UserMessage CreateOutgoingMessage(MessageModel model) - { - return new UserMessage(this, new User(model.Author.Value), model); - } - private Message CreateIncomingMessage(MessageModel model) - { - if (model.Type == MessageType.Default) - return new UserMessage(this, new User(model.Author.Value), model); - else - return new SystemMessage(this, new User(model.Author.Value), model); - } - - public override string ToString() => Name; - private string DebuggerDisplay => $"@{Name} ({Id}, Group)"; - - IMessage IMessageChannel.GetCachedMessage(ulong id) => null; - } -} diff --git a/src/Discord.Net/Rest/Entities/Channels/TextChannel.cs b/src/Discord.Net/Rest/Entities/Channels/TextChannel.cs deleted file mode 100644 index 5d97bca7c..000000000 --- a/src/Discord.Net/Rest/Entities/Channels/TextChannel.cs +++ /dev/null @@ -1,133 +0,0 @@ -using Discord.API.Rest; -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Threading.Tasks; -using Model = Discord.API.Channel; -using MessageModel = Discord.API.Message; - -namespace Discord.Rest -{ - [DebuggerDisplay(@"{DebuggerDisplay,nq}")] - internal class TextChannel : GuildChannel, ITextChannel - { - public string Topic { get; private set; } - - public string Mention => MentionUtils.Mention(this); - public virtual IReadOnlyCollection CachedMessages => ImmutableArray.Create(); - - public TextChannel(Guild guild, Model model) - : base(guild, model) - { - } - public override void Update(Model model, UpdateSource source) - { - if (source == UpdateSource.Rest && IsAttached) return; - - Topic = model.Topic.Value; - base.Update(model, source); - } - - public async Task ModifyAsync(Action func) - { - if (func == null) throw new NullReferenceException(nameof(func)); - - var args = new ModifyTextChannelParams(); - func(args); - - if (!args._name.IsSpecified) - args._name = Name; - - var model = await Discord.ApiClient.ModifyGuildChannelAsync(Id, args).ConfigureAwait(false); - Update(model, UpdateSource.Rest); - } - - public override async Task GetUserAsync(ulong id) - { - var user = await Guild.GetUserAsync(id).ConfigureAwait(false); - if (user != null && Permissions.GetValue(Permissions.ResolveChannel(user, this, user.GuildPermissions.RawValue), ChannelPermission.ReadMessages)) - return user; - return null; - } - public override async Task> GetUsersAsync() - { - var users = await Guild.GetUsersAsync().ConfigureAwait(false); - return users.Where(x => Permissions.GetValue(Permissions.ResolveChannel(x, this, x.GuildPermissions.RawValue), ChannelPermission.ReadMessages)).ToImmutableArray(); - } - - public async Task SendMessageAsync(string text, bool isTTS) - { - var args = new CreateMessageParams { Content = text, IsTTS = isTTS }; - var model = await Discord.ApiClient.CreateMessageAsync(Guild.Id, Id, args).ConfigureAwait(false); - return CreateOutgoingMessage(model); - } - public async Task SendFileAsync(string filePath, string text, bool isTTS) - { - string filename = Path.GetFileName(filePath); - using (var file = File.OpenRead(filePath)) - { - var args = new UploadFileParams(file) { Filename = filename, Content = text, IsTTS = isTTS }; - var model = await Discord.ApiClient.UploadFileAsync(Guild.Id, Id, args).ConfigureAwait(false); - return CreateOutgoingMessage(model); - } - } - public async Task SendFileAsync(Stream stream, string filename, string text, bool isTTS) - { - var args = new UploadFileParams(stream) { Filename = filename, Content = text, IsTTS = isTTS }; - var model = await Discord.ApiClient.UploadFileAsync(Guild.Id, Id, args).ConfigureAwait(false); - return CreateOutgoingMessage(model); - } - public virtual async Task GetMessageAsync(ulong id) - { - var model = await Discord.ApiClient.GetChannelMessageAsync(Id, id).ConfigureAwait(false); - if (model != null) - return CreateIncomingMessage(model); - return null; - } - public virtual async Task> GetMessagesAsync(int limit) - { - var args = new GetChannelMessagesParams { Limit = limit }; - var models = await Discord.ApiClient.GetChannelMessagesAsync(Id, args).ConfigureAwait(false); - return models.Select(x => CreateIncomingMessage(x)).ToImmutableArray(); - } - public virtual async Task> GetMessagesAsync(ulong fromMessageId, Direction dir, int limit) - { - var args = new GetChannelMessagesParams { Limit = limit, RelativeMessageId = fromMessageId, RelativeDirection = dir }; - var models = await Discord.ApiClient.GetChannelMessagesAsync(Id, args).ConfigureAwait(false); - return models.Select(x => CreateIncomingMessage(x)).ToImmutableArray(); - } - public async Task DeleteMessagesAsync(IEnumerable messages) - { - await Discord.ApiClient.DeleteMessagesAsync(Guild.Id, Id, new DeleteMessagesParams { MessageIds = messages.Select(x => x.Id) }).ConfigureAwait(false); - } - public async Task> GetPinnedMessagesAsync() - { - var models = await Discord.ApiClient.GetPinsAsync(Id); - return models.Select(x => CreateIncomingMessage(x)).ToImmutableArray(); - } - - public async Task TriggerTypingAsync() - { - await Discord.ApiClient.TriggerTypingIndicatorAsync(Id).ConfigureAwait(false); - } - - private UserMessage CreateOutgoingMessage(MessageModel model) - { - return new UserMessage(this, new User(model.Author.Value), model); - } - private Message CreateIncomingMessage(MessageModel model) - { - if (model.Type == MessageType.Default) - return new UserMessage(this, new User(model.Author.Value), model); - else - return new SystemMessage(this, new User(model.Author.Value), model); - } - - private string DebuggerDisplay => $"{Name} ({Id}, Text)"; - - IMessage IMessageChannel.GetCachedMessage(ulong id) => null; - } -} diff --git a/src/Discord.Net/Rest/Entities/Channels/VoiceChannel.cs b/src/Discord.Net/Rest/Entities/Channels/VoiceChannel.cs deleted file mode 100644 index abe4fa56c..000000000 --- a/src/Discord.Net/Rest/Entities/Channels/VoiceChannel.cs +++ /dev/null @@ -1,57 +0,0 @@ -using Discord.API.Rest; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Threading.Tasks; -using Model = Discord.API.Channel; -using Discord.Audio; - -namespace Discord.Rest -{ - [DebuggerDisplay(@"{DebuggerDisplay,nq}")] - internal class VoiceChannel : GuildChannel, IVoiceChannel - { - public int Bitrate { get; private set; } - public int UserLimit { get; private set; } - - public VoiceChannel(Guild guild, Model model) - : base(guild, model) - { - } - public override void Update(Model model, UpdateSource source) - { - if (source == UpdateSource.Rest && IsAttached) return; - - base.Update(model, source); - Bitrate = model.Bitrate.Value; - UserLimit = model.UserLimit.Value; - } - - public async Task ModifyAsync(Action func) - { - if (func == null) throw new NullReferenceException(nameof(func)); - - var args = new ModifyVoiceChannelParams(); - func(args); - - if (!args._name.IsSpecified) - args._name = Name; - - var model = await Discord.ApiClient.ModifyGuildChannelAsync(Id, args).ConfigureAwait(false); - Update(model, UpdateSource.Rest); - } - - public override Task GetUserAsync(ulong id) - { - throw new NotSupportedException(); - } - public override Task> GetUsersAsync() - { - throw new NotSupportedException(); - } - - public virtual Task ConnectAsync() { throw new NotSupportedException(); } - - private string DebuggerDisplay => $"{Name} ({Id}, Voice)"; - } -} diff --git a/src/Discord.Net/Rest/Entities/Entity.cs b/src/Discord.Net/Rest/Entities/Entity.cs deleted file mode 100644 index 6023626f1..000000000 --- a/src/Discord.Net/Rest/Entities/Entity.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; -using Discord.Rest; - -namespace Discord.Rest -{ - internal abstract class Entity : IEntity - where T : IEquatable - { - public T Id { get; } - - public abstract DiscordRestClient Discord { get; } - - internal virtual bool IsAttached => false; - bool IEntity.IsAttached => IsAttached; - - public Entity(T id) - { - Id = id; - } - } -} diff --git a/src/Discord.Net/Rest/Entities/Guilds/Guild.cs b/src/Discord.Net/Rest/Entities/Guilds/Guild.cs deleted file mode 100644 index 395504943..000000000 --- a/src/Discord.Net/Rest/Entities/Guilds/Guild.cs +++ /dev/null @@ -1,307 +0,0 @@ -using Discord.API.Rest; -using Discord.Audio; -using Discord.Rest; -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; -using System.Threading.Tasks; -using EmbedModel = Discord.API.GuildEmbed; -using Model = Discord.API.Guild; -using RoleModel = Discord.API.Role; - -namespace Discord.Rest -{ - [DebuggerDisplay(@"{DebuggerDisplay,nq}")] - internal class Guild : SnowflakeEntity, IGuild - { - protected ConcurrentDictionary _roles; - protected string _iconId, _splashId; - - public string Name { get; private set; } - public int AFKTimeout { get; private set; } - public bool IsEmbeddable { get; private set; } - public VerificationLevel VerificationLevel { get; private set; } - public MfaLevel MfaLevel { get; private set; } - public DefaultMessageNotifications DefaultMessageNotifications { get; private set; } - - public override DiscordRestClient Discord { get; } - public ulong? AFKChannelId { get; private set; } - public ulong? EmbedChannelId { get; private set; } - public ulong OwnerId { get; private set; } - public string VoiceRegionId { get; private set; } - public ImmutableArray Emojis { get; protected set; } - public ImmutableArray Features { get; protected set; } - - public ulong DefaultChannelId => Id; - public string IconUrl => API.CDN.GetGuildIconUrl(Id, _iconId); - public string SplashUrl => API.CDN.GetGuildSplashUrl(Id, _splashId); - - public Role EveryoneRole => GetRole(Id); - public IReadOnlyCollection Roles => _roles.ToReadOnlyCollection(); - - public Guild(DiscordRestClient discord, Model model) - : base(model.Id) - { - Discord = discord; - - Update(model, UpdateSource.Creation); - } - public void Update(Model model, UpdateSource source) - { - if (source == UpdateSource.Rest && IsAttached) return; - - AFKChannelId = model.AFKChannelId; - EmbedChannelId = model.EmbedChannelId; - AFKTimeout = model.AFKTimeout; - IsEmbeddable = model.EmbedEnabled; - _iconId = model.Icon; - Name = model.Name; - OwnerId = model.OwnerId; - VoiceRegionId = model.Region; - _splashId = model.Splash; - VerificationLevel = model.VerificationLevel; - MfaLevel = model.MfaLevel; - DefaultMessageNotifications = model.DefaultMessageNotifications; - - if (model.Emojis != null) - { - var emojis = ImmutableArray.CreateBuilder(model.Emojis.Length); - for (int i = 0; i < model.Emojis.Length; i++) - emojis.Add(new Emoji(model.Emojis[i])); - Emojis = emojis.ToImmutableArray(); - } - else - Emojis = ImmutableArray.Create(); - - if (model.Features != null) - Features = model.Features.ToImmutableArray(); - else - Features = ImmutableArray.Create(); - - var roles = new ConcurrentDictionary(1, model.Roles?.Length ?? 0); - if (model.Roles != null) - { - for (int i = 0; i < model.Roles.Length; i++) - roles[model.Roles[i].Id] = new Role(this, model.Roles[i]); - } - _roles = roles; - } - public void Update(EmbedModel model, UpdateSource source) - { - if (source == UpdateSource.Rest && IsAttached) return; - - IsEmbeddable = model.Enabled; - EmbedChannelId = model.ChannelId; - } - public void Update(IEnumerable models, UpdateSource source) - { - if (source == UpdateSource.Rest && IsAttached) return; - - Role role; - foreach (var model in models) - { - if (_roles.TryGetValue(model.Id, out role)) - role.Update(model, UpdateSource.Rest); - } - } - - public async Task UpdateAsync() - { - if (IsAttached) throw new NotSupportedException(); - - var response = await Discord.ApiClient.GetGuildAsync(Id).ConfigureAwait(false); - Update(response, UpdateSource.Rest); - } - public async Task ModifyAsync(Action func) - { - if (func == null) throw new NullReferenceException(nameof(func)); - - var args = new ModifyGuildParams(); - func(args); - - if (args._splash.IsSpecified && _splashId != null) - args._splash = new API.Image(_splashId); - if (args._icon.IsSpecified && _iconId != null) - args._icon = new API.Image(_iconId); - - var model = await Discord.ApiClient.ModifyGuildAsync(Id, args).ConfigureAwait(false); - Update(model, UpdateSource.Rest); - } - public async Task ModifyEmbedAsync(Action func) - { - if (func == null) throw new NullReferenceException(nameof(func)); - - var args = new ModifyGuildEmbedParams(); - func(args); - var model = await Discord.ApiClient.ModifyGuildEmbedAsync(Id, args).ConfigureAwait(false); - Update(model, UpdateSource.Rest); - } - public async Task ModifyChannelsAsync(IEnumerable args) - { - await Discord.ApiClient.ModifyGuildChannelsAsync(Id, args).ConfigureAwait(false); - } - public async Task ModifyRolesAsync(IEnumerable args) - { - var models = await Discord.ApiClient.ModifyGuildRolesAsync(Id, args).ConfigureAwait(false); - Update(models, UpdateSource.Rest); - } - public async Task LeaveAsync() - { - await Discord.ApiClient.LeaveGuildAsync(Id).ConfigureAwait(false); - } - public async Task DeleteAsync() - { - await Discord.ApiClient.DeleteGuildAsync(Id).ConfigureAwait(false); - } - - public async Task> GetBansAsync() - { - var models = await Discord.ApiClient.GetGuildBansAsync(Id).ConfigureAwait(false); - return models.Select(x => new Ban(new User(x.User), x.Reason)).ToImmutableArray(); - } - public Task AddBanAsync(IUser user, int pruneDays = 0) => AddBanAsync(user.Id, pruneDays); - public async Task AddBanAsync(ulong userId, int pruneDays = 0) - { - var args = new CreateGuildBanParams() { DeleteMessageDays = pruneDays }; - await Discord.ApiClient.CreateGuildBanAsync(Id, userId, args).ConfigureAwait(false); - } - public Task RemoveBanAsync(IUser user) => RemoveBanAsync(user.Id); - public async Task RemoveBanAsync(ulong userId) - { - await Discord.ApiClient.RemoveGuildBanAsync(Id, userId).ConfigureAwait(false); - } - - public virtual async Task GetChannelAsync(ulong id) - { - var model = await Discord.ApiClient.GetChannelAsync(Id, id).ConfigureAwait(false); - if (model != null) - return ToChannel(model); - return null; - } - public virtual async Task> GetChannelsAsync() - { - var models = await Discord.ApiClient.GetGuildChannelsAsync(Id).ConfigureAwait(false); - return models.Select(x => ToChannel(x)).ToImmutableArray(); - } - public async Task CreateTextChannelAsync(string name) - { - if (name == null) throw new ArgumentNullException(nameof(name)); - - var args = new CreateGuildChannelParams() { Name = name, Type = ChannelType.Text }; - var model = await Discord.ApiClient.CreateGuildChannelAsync(Id, args).ConfigureAwait(false); - return new TextChannel(this, model); - } - public async Task CreateVoiceChannelAsync(string name) - { - if (name == null) throw new ArgumentNullException(nameof(name)); - - var args = new CreateGuildChannelParams { Name = name, Type = ChannelType.Voice }; - var model = await Discord.ApiClient.CreateGuildChannelAsync(Id, args).ConfigureAwait(false); - return new VoiceChannel(this, model); - } - - public async Task> GetIntegrationsAsync() - { - var models = await Discord.ApiClient.GetGuildIntegrationsAsync(Id).ConfigureAwait(false); - return models.Select(x => new GuildIntegration(this, x)).ToImmutableArray(); - } - public async Task CreateIntegrationAsync(ulong id, string type) - { - var args = new CreateGuildIntegrationParams { Id = id, Type = type }; - var model = await Discord.ApiClient.CreateGuildIntegrationAsync(Id, args).ConfigureAwait(false); - return new GuildIntegration(this, model); - } - - public async Task> GetInvitesAsync() - { - var models = await Discord.ApiClient.GetGuildInvitesAsync(Id).ConfigureAwait(false); - return models.Select(x => new InviteMetadata(Discord, x)).ToImmutableArray(); - } - - public Role GetRole(ulong id) - { - Role result = null; - if (_roles?.TryGetValue(id, out result) == true) - return result; - return null; - } - public async Task CreateRoleAsync(string name, GuildPermissions? permissions = null, Color? color = null, bool isHoisted = false) - { - if (name == null) throw new ArgumentNullException(nameof(name)); - - var model = await Discord.ApiClient.CreateGuildRoleAsync(Id).ConfigureAwait(false); - var role = new Role(this, model); - - await role.ModifyAsync(x => - { - x.Name = name; - x.Permissions = (permissions ?? role.Permissions).RawValue; - x.Color = (color ?? Color.Default).RawValue; - x.Hoist = isHoisted; - }).ConfigureAwait(false); - - return role; - } - - public virtual async Task GetUserAsync(ulong id) - { - var model = await Discord.ApiClient.GetGuildMemberAsync(Id, id).ConfigureAwait(false); - if (model != null) - return new GuildUser(this, new User(model.User), model); - return null; - } - public virtual async Task GetCurrentUserAsync() - { - var currentUser = await Discord.GetCurrentUserAsync().ConfigureAwait(false); - return await GetUserAsync(currentUser.Id).ConfigureAwait(false); - } - public virtual async Task> GetUsersAsync() - { - var args = new GetGuildMembersParams(); - var models = await Discord.ApiClient.GetGuildMembersAsync(Id, args).ConfigureAwait(false); - return models.Select(x => new GuildUser(this, new User(x.User), x)).ToImmutableArray(); - } - public async Task PruneUsersAsync(int days = 30, bool simulate = false) - { - var args = new GuildPruneParams() { Days = days }; - GetGuildPruneCountResponse model; - if (simulate) - model = await Discord.ApiClient.GetGuildPruneCountAsync(Id, args).ConfigureAwait(false); - else - model = await Discord.ApiClient.BeginGuildPruneAsync(Id, args).ConfigureAwait(false); - return model.Pruned; - } - public virtual Task DownloadUsersAsync() - { - throw new NotSupportedException(); - } - - internal GuildChannel ToChannel(API.Channel model) - { - switch (model.Type) - { - case ChannelType.Text: - return new TextChannel(this, model); - case ChannelType.Voice: - return new VoiceChannel(this, model); - default: - throw new InvalidOperationException($"Unexpected channel type: {model.Type}"); - } - } - - public override string ToString() => Name; - - private string DebuggerDisplay => $"{Name} ({Id})"; - - bool IGuild.Available => false; - IRole IGuild.EveryoneRole => EveryoneRole; - IReadOnlyCollection IGuild.Emojis => Emojis; - IReadOnlyCollection IGuild.Features => Features; - IAudioClient IGuild.AudioClient => null; - - IRole IGuild.GetRole(ulong id) => GetRole(id); - } -} diff --git a/src/Discord.Net/Rest/Entities/Guilds/VoiceRegion.cs b/src/Discord.Net/Rest/Entities/Guilds/VoiceRegion.cs deleted file mode 100644 index 8f8bbfc53..000000000 --- a/src/Discord.Net/Rest/Entities/Guilds/VoiceRegion.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System.Diagnostics; -using Model = Discord.API.VoiceRegion; - -namespace Discord.Rest -{ - [DebuggerDisplay("{DebuggerDisplay,nq}")] - internal class VoiceRegion : IVoiceRegion - { - public string Id { get; } - public string Name { get; } - public bool IsVip { get; } - public bool IsOptimal { get; } - public string SampleHostname { get; } - public int SamplePort { get; } - - public VoiceRegion(Model model) - { - Id = model.Id; - Name = model.Name; - IsVip = model.IsVip; - IsOptimal = model.IsOptimal; - SampleHostname = model.SampleHostname; - SamplePort = model.SamplePort; - } - - public override string ToString() => Name; - private string DebuggerDisplay => $"{Name} ({Id}{(IsVip ? ", VIP" : "")}{(IsOptimal ? ", Optimal" : "")})"; - } -} diff --git a/src/Discord.Net/Rest/Entities/Messages/Attachment.cs b/src/Discord.Net/Rest/Entities/Messages/Attachment.cs deleted file mode 100644 index b5a94689e..000000000 --- a/src/Discord.Net/Rest/Entities/Messages/Attachment.cs +++ /dev/null @@ -1,26 +0,0 @@ -using Model = Discord.API.Attachment; - -namespace Discord.Rest -{ - internal class Attachment : IAttachment - { - public ulong Id { get; } - public string Filename { get; } - public string Url { get; } - public string ProxyUrl { get; } - public int Size { get; } - public int? Height { get; } - public int? Width { get; } - - public Attachment(Model model) - { - Id = model.Id; - Filename = model.Filename; - Size = model.Size; - Url = model.Url; - ProxyUrl = model.ProxyUrl; - Height = model.Height.IsSpecified ? model.Height.Value : (int?)null; - Width = model.Width.IsSpecified ? model.Width.Value : (int?)null; - } - } -} diff --git a/src/Discord.Net/Rest/Entities/Messages/Embed.cs b/src/Discord.Net/Rest/Entities/Messages/Embed.cs deleted file mode 100644 index 77c8e28b7..000000000 --- a/src/Discord.Net/Rest/Entities/Messages/Embed.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Model = Discord.API.Embed; - -namespace Discord.Rest -{ - internal class Embed : IEmbed - { - public string Description { get; } - public string Url { get; } - public string Title { get; } - public string Type { get; } - public EmbedProvider Provider { get; } - public EmbedThumbnail Thumbnail { get; } - - public Embed(Model model) - { - Url = model.Url; - Type = model.Type; - Title = model.Title; - Description = model.Description; - - if (model.Provider.IsSpecified) - Provider = new EmbedProvider(model.Provider.Value); - if (model.Thumbnail.IsSpecified) - Thumbnail = new EmbedThumbnail(model.Thumbnail.Value); - } - } -} diff --git a/src/Discord.Net/Rest/Entities/Messages/Message.cs b/src/Discord.Net/Rest/Entities/Messages/Message.cs deleted file mode 100644 index 1b5c025e9..000000000 --- a/src/Discord.Net/Rest/Entities/Messages/Message.cs +++ /dev/null @@ -1,100 +0,0 @@ -using Discord.API.Rest; -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Threading.Tasks; -using Model = Discord.API.Message; - -namespace Discord.Rest -{ - [DebuggerDisplay(@"{DebuggerDisplay,nq}")] - internal abstract class Message : SnowflakeEntity, IMessage - { - private long _timestampTicks; - - public IMessageChannel Channel { get; } - public IUser Author { get; } - - public string Content { get; private set; } - - public override DiscordRestClient Discord => (Channel as Entity).Discord; - - public virtual bool IsTTS => false; - public virtual bool IsPinned => false; - public virtual DateTimeOffset? EditedTimestamp => null; - - public virtual IReadOnlyCollection Attachments => ImmutableArray.Create(); - public virtual IReadOnlyCollection Embeds => ImmutableArray.Create(); - public virtual IReadOnlyCollection MentionedChannelIds => ImmutableArray.Create(); - public virtual IReadOnlyCollection MentionedRoles => ImmutableArray.Create(); - public virtual IReadOnlyCollection MentionedUsers => ImmutableArray.Create(); - - public DateTimeOffset Timestamp => DateTimeUtils.FromTicks(_timestampTicks); - - public Message(IMessageChannel channel, IUser author, Model model) - : base(model.Id) - { - Channel = channel; - Author = author; - - Update(model, UpdateSource.Creation); - } - public virtual void Update(Model model, UpdateSource source) - { - if (source == UpdateSource.Rest && IsAttached) return; - - var guildChannel = Channel as GuildChannel; - var guild = guildChannel?.Guild; - - if (model.Timestamp.IsSpecified) - _timestampTicks = model.Timestamp.Value.UtcTicks; - - if (model.Content.IsSpecified) - Content = model.Content.Value; - } - - public async Task UpdateAsync() - { - if (IsAttached) throw new NotSupportedException(); - - var model = await Discord.ApiClient.GetChannelMessageAsync(Channel.Id, Id).ConfigureAwait(false); - Update(model, UpdateSource.Rest); - } - public async Task ModifyAsync(Action func) - { - if (func == null) throw new NullReferenceException(nameof(func)); - - var args = new ModifyMessageParams(); - func(args); - var guildChannel = Channel as GuildChannel; - - Model model; - if (guildChannel != null) - model = await Discord.ApiClient.ModifyMessageAsync(guildChannel.Guild.Id, Channel.Id, Id, args).ConfigureAwait(false); - else - model = await Discord.ApiClient.ModifyDMMessageAsync(Channel.Id, Id, args).ConfigureAwait(false); - - Update(model, UpdateSource.Rest); - } - public async Task DeleteAsync() - { - var guildChannel = Channel as GuildChannel; - if (guildChannel != null) - await Discord.ApiClient.DeleteMessageAsync(guildChannel.Id, Channel.Id, Id).ConfigureAwait(false); - else - await Discord.ApiClient.DeleteDMMessageAsync(Channel.Id, Id).ConfigureAwait(false); - } - public async Task PinAsync() - { - await Discord.ApiClient.AddPinAsync(Channel.Id, Id).ConfigureAwait(false); - } - public async Task UnpinAsync() - { - await Discord.ApiClient.RemovePinAsync(Channel.Id, Id).ConfigureAwait(false); - } - - public override string ToString() => Content; - private string DebuggerDisplay => $"{Author}: {Content}{(Attachments.Count > 0 ? $" [{Attachments.Count} Attachments]" : "")}"; - } -} diff --git a/src/Discord.Net/Rest/Entities/Messages/SystemMessage.cs b/src/Discord.Net/Rest/Entities/Messages/SystemMessage.cs deleted file mode 100644 index a715c6493..000000000 --- a/src/Discord.Net/Rest/Entities/Messages/SystemMessage.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Diagnostics; -using Model = Discord.API.Message; - -namespace Discord.Rest -{ - [DebuggerDisplay(@"{DebuggerDisplay,nq}")] - internal class SystemMessage : Message, ISystemMessage - { - public MessageType Type { get; } - - public override DiscordRestClient Discord => (Channel as Entity).Discord; - - public SystemMessage(IMessageChannel channel, IUser author, Model model) - : base(channel, author, model) - { - Type = model.Type; - } - - public override string ToString() => Content; - private string DebuggerDisplay => $"[{Type}] {Author}{(!string.IsNullOrEmpty(Content) ? $": ({Content})" : "")}"; - } -} diff --git a/src/Discord.Net/Rest/Entities/Messages/UserMessage.cs b/src/Discord.Net/Rest/Entities/Messages/UserMessage.cs deleted file mode 100644 index 06c904232..000000000 --- a/src/Discord.Net/Rest/Entities/Messages/UserMessage.cs +++ /dev/null @@ -1,175 +0,0 @@ -using Discord.API.Rest; -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Threading.Tasks; -using Model = Discord.API.Message; - -namespace Discord.Rest -{ - [DebuggerDisplay(@"{DebuggerDisplay,nq}")] - internal class UserMessage : Message, IUserMessage - { - private bool _isMentioningEveryone, _isTTS, _isPinned; - private long? _editedTimestampTicks; - private IReadOnlyCollection _attachments; - private IReadOnlyCollection _embeds; - private IReadOnlyCollection _mentionedChannelIds; - private IReadOnlyCollection _mentionedRoles; - private IReadOnlyCollection _mentionedUsers; - - public override DiscordRestClient Discord => (Channel as Entity).Discord; - public override bool IsTTS => _isTTS; - public override bool IsPinned => _isPinned; - public override DateTimeOffset? EditedTimestamp => DateTimeUtils.FromTicks(_editedTimestampTicks); - - public override IReadOnlyCollection Attachments => _attachments; - public override IReadOnlyCollection Embeds => _embeds; - public override IReadOnlyCollection MentionedChannelIds => _mentionedChannelIds; - public override IReadOnlyCollection MentionedRoles => _mentionedRoles; - public override IReadOnlyCollection MentionedUsers => _mentionedUsers; - - public UserMessage(IMessageChannel channel, IUser author, Model model) - : base(channel, author, model) - { - _mentionedChannelIds = ImmutableArray.Create(); - _mentionedRoles = ImmutableArray.Create(); - _mentionedUsers = ImmutableArray.Create(); - - Update(model, UpdateSource.Creation); - } - public override void Update(Model model, UpdateSource source) - { - if (source == UpdateSource.Rest && IsAttached) return; - - var guildChannel = Channel as GuildChannel; - var guild = guildChannel?.Guild; - - if (model.IsTextToSpeech.IsSpecified) - _isTTS = model.IsTextToSpeech.Value; - if (model.Pinned.IsSpecified) - _isPinned = model.Pinned.Value; - if (model.EditedTimestamp.IsSpecified) - _editedTimestampTicks = model.EditedTimestamp.Value?.UtcTicks; - if (model.MentionEveryone.IsSpecified) - _isMentioningEveryone = model.MentionEveryone.Value; - - if (model.Attachments.IsSpecified) - { - var value = model.Attachments.Value; - if (value.Length > 0) - { - var attachments = new Attachment[value.Length]; - for (int i = 0; i < attachments.Length; i++) - attachments[i] = new Attachment(value[i]); - _attachments = ImmutableArray.Create(attachments); - } - else - _attachments = ImmutableArray.Create(); - } - - if (model.Embeds.IsSpecified) - { - var value = model.Embeds.Value; - if (value.Length > 0) - { - var embeds = new Embed[value.Length]; - for (int i = 0; i < embeds.Length; i++) - embeds[i] = new Embed(value[i]); - _embeds = ImmutableArray.Create(embeds); - } - else - _embeds = ImmutableArray.Create(); - } - - ImmutableArray mentions = ImmutableArray.Create(); - if (model.Mentions.IsSpecified) - { - var value = model.Mentions.Value; - if (value.Length > 0) - { - var newMentions = new IUser[value.Length]; - for (int i = 0; i < value.Length; i++) - newMentions[i] = new User(value[i]); - mentions = ImmutableArray.Create(newMentions); - } - } - - if (model.Content.IsSpecified) - { - var text = model.Content.Value; - - if (guildChannel != null) - { - _mentionedUsers = MentionUtils.GetUserMentions(text, Channel, mentions); - _mentionedChannelIds = MentionUtils.GetChannelMentions(text, guildChannel.Guild); - _mentionedRoles = MentionUtils.GetRoleMentions(text, guildChannel.Guild); - } - model.Content = text; - } - - base.Update(model, source); - } - - public async Task UpdateAsync() - { - if (IsAttached) throw new NotSupportedException(); - - var model = await Discord.ApiClient.GetChannelMessageAsync(Channel.Id, Id).ConfigureAwait(false); - Update(model, UpdateSource.Rest); - } - public async Task ModifyAsync(Action func) - { - if (func == null) throw new NullReferenceException(nameof(func)); - - var args = new ModifyMessageParams(); - func(args); - var guildChannel = Channel as GuildChannel; - - Model model; - if (guildChannel != null) - model = await Discord.ApiClient.ModifyMessageAsync(guildChannel.Guild.Id, Channel.Id, Id, args).ConfigureAwait(false); - else - model = await Discord.ApiClient.ModifyDMMessageAsync(Channel.Id, Id, args).ConfigureAwait(false); - - Update(model, UpdateSource.Rest); - } - public async Task DeleteAsync() - { - var guildChannel = Channel as GuildChannel; - if (guildChannel != null) - await Discord.ApiClient.DeleteMessageAsync(guildChannel.Id, Channel.Id, Id).ConfigureAwait(false); - else - await Discord.ApiClient.DeleteDMMessageAsync(Channel.Id, Id).ConfigureAwait(false); - } - public async Task PinAsync() - { - await Discord.ApiClient.AddPinAsync(Channel.Id, Id).ConfigureAwait(false); - } - public async Task UnpinAsync() - { - await Discord.ApiClient.RemovePinAsync(Channel.Id, Id).ConfigureAwait(false); - } - - public string Resolve(int startIndex, int length, UserMentionHandling userHandling, ChannelMentionHandling channelHandling, - RoleMentionHandling roleHandling, EveryoneMentionHandling everyoneHandling) - => Resolve(Content.Substring(startIndex, length), userHandling, channelHandling, roleHandling, everyoneHandling); - public string Resolve(UserMentionHandling userHandling, ChannelMentionHandling channelHandling, - RoleMentionHandling roleHandling, EveryoneMentionHandling everyoneHandling) - => Resolve(Content, userHandling, channelHandling, roleHandling, everyoneHandling); - - private string Resolve(string text, UserMentionHandling userHandling, ChannelMentionHandling channelHandling, - RoleMentionHandling roleHandling, EveryoneMentionHandling everyoneHandling) - { - text = MentionUtils.ResolveUserMentions(text, Channel, MentionedUsers, userHandling); - text = MentionUtils.ResolveChannelMentions(text, (Channel as IGuildChannel)?.Guild, channelHandling); - text = MentionUtils.ResolveRoleMentions(text, MentionedRoles, roleHandling); - text = MentionUtils.ResolveEveryoneMentions(text, everyoneHandling); - return text; - } - - public override string ToString() => Content; - private string DebuggerDisplay => $"{Author}: {Content}{(Attachments.Count > 0 ? $" [{Attachments.Count} Attachments]" : "")}"; - } -} diff --git a/src/Discord.Net/Rest/Entities/Roles/Role.cs b/src/Discord.Net/Rest/Entities/Roles/Role.cs deleted file mode 100644 index dcb198cd4..000000000 --- a/src/Discord.Net/Rest/Entities/Roles/Role.cs +++ /dev/null @@ -1,67 +0,0 @@ -using Discord.API.Rest; -using Discord.Rest; -using System; -using System.Diagnostics; -using System.Threading.Tasks; -using Model = Discord.API.Role; - -namespace Discord.Rest -{ - [DebuggerDisplay(@"{DebuggerDisplay,nq}")] - internal class Role : SnowflakeEntity, IRole, IMentionable - { - public Guild Guild { get; } - - public Color Color { get; private set; } - public bool IsHoisted { get; private set; } - public bool IsManaged { get; private set; } - public string Name { get; private set; } - public GuildPermissions Permissions { get; private set; } - public int Position { get; private set; } - - public bool IsEveryone => Id == Guild.Id; - public string Mention => MentionUtils.Mention(this); - public override DiscordRestClient Discord => Guild.Discord; - - public Role(Guild guild, Model model) - : base(model.Id) - { - Guild = guild; - - Update(model, UpdateSource.Creation); - } - public void Update(Model model, UpdateSource source) - { - if (source == UpdateSource.Rest && IsAttached) return; - - Name = model.Name; - IsHoisted = model.Hoist; - IsManaged = model.Managed; - Position = model.Position; - Color = new Color(model.Color); - Permissions = new GuildPermissions(model.Permissions); - } - - public async Task ModifyAsync(Action func) - { - if (func == null) throw new NullReferenceException(nameof(func)); - - var args = new ModifyGuildRoleParams(); - func(args); - var response = await Discord.ApiClient.ModifyGuildRoleAsync(Guild.Id, Id, args).ConfigureAwait(false); - - Update(response, UpdateSource.Rest); - } - public async Task DeleteAsync() - { - await Discord.ApiClient.DeleteGuildRoleAsync(Guild.Id, Id).ConfigureAwait(false); - } - - public Role Clone() => MemberwiseClone() as Role; - - public override string ToString() => Name; - private string DebuggerDisplay => $"{Name} ({Id})"; - - ulong IRole.GuildId => Guild.Id; - } -} diff --git a/src/Discord.Net/Rest/Entities/SnowflakeEntity.cs b/src/Discord.Net/Rest/Entities/SnowflakeEntity.cs deleted file mode 100644 index f126c8ff5..000000000 --- a/src/Discord.Net/Rest/Entities/SnowflakeEntity.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; - -namespace Discord.Rest -{ - internal abstract class SnowflakeEntity : Entity, ISnowflakeEntity - { - //TODO: C#7 Candidate for Extension Property. Lets us remove this class. - public DateTimeOffset CreatedAt => DateTimeUtils.FromSnowflake(Id); - - public SnowflakeEntity(ulong id) - : base(id) - { - } - } -} diff --git a/src/Discord.Net/Rest/Entities/Users/Connection.cs b/src/Discord.Net/Rest/Entities/Users/Connection.cs deleted file mode 100644 index 622bc8730..000000000 --- a/src/Discord.Net/Rest/Entities/Users/Connection.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System.Collections.Generic; -using System.Diagnostics; -using Model = Discord.API.Connection; - -namespace Discord.Rest -{ - [DebuggerDisplay(@"{DebuggerDisplay,nq}")] - internal class Connection : IConnection - { - public string Id { get; } - public string Type { get; } - public string Name { get; } - public bool IsRevoked { get; } - - public IReadOnlyCollection IntegrationIds { get; } - - public Connection(Model model) - { - Id = model.Id; - Type = model.Type; - Name = model.Name; - IsRevoked = model.Revoked; - - IntegrationIds = model.Integrations; - } - - public override string ToString() => Name; - private string DebuggerDisplay => $"{Name} ({Id}, Type = {Type}{(IsRevoked ? ", Revoked" : "")})"; - } -} diff --git a/src/Discord.Net/Rest/Entities/Users/GroupUser.cs b/src/Discord.Net/Rest/Entities/Users/GroupUser.cs deleted file mode 100644 index 5a0df5cc2..000000000 --- a/src/Discord.Net/Rest/Entities/Users/GroupUser.cs +++ /dev/null @@ -1,49 +0,0 @@ -using Discord.API.Rest; -using Discord.Rest; -using System; -using System.Threading.Tasks; - -namespace Discord.Rest -{ - internal class GroupUser : IGroupUser - { - internal virtual bool IsAttached => false; - bool IEntity.IsAttached => IsAttached; - - public GroupChannel Channel { get; private set; } - public User User { get; private set; } - - public ulong Id => User.Id; - public string AvatarUrl => User.AvatarUrl; - public DateTimeOffset CreatedAt => User.CreatedAt; - public string Discriminator => User.Discriminator; - public ushort DiscriminatorValue => User.DiscriminatorValue; - public bool IsBot => User.IsBot; - public string Username => User.Username; - public string Mention => MentionUtils.Mention(this, false); - - public virtual UserStatus Status => UserStatus.Unknown; - public virtual Game Game => null; - - public DiscordRestClient Discord => Channel.Discord; - - public GroupUser(GroupChannel channel, User user) - { - Channel = channel; - User = user; - } - - public async Task KickAsync() - { - await Discord.ApiClient.RemoveGroupRecipientAsync(Channel.Id, Id).ConfigureAwait(false); - } - - public async Task CreateDMChannelAsync() - { - var args = new CreateDMChannelParams { Recipient = this }; - var model = await Discord.ApiClient.CreateDMChannelAsync(args).ConfigureAwait(false); - - return new DMChannel(Discord, new User(model.Recipients.Value[0]), model); - } - } -} diff --git a/src/Discord.Net/Rest/Entities/Users/GuildUser.cs b/src/Discord.Net/Rest/Entities/Users/GuildUser.cs deleted file mode 100644 index 1c8b2cfc1..000000000 --- a/src/Discord.Net/Rest/Entities/Users/GuildUser.cs +++ /dev/null @@ -1,164 +0,0 @@ -using Discord.API.Rest; -using Discord.Rest; -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; -using System.Threading.Tasks; -using Model = Discord.API.GuildMember; -using PresenceModel = Discord.API.Presence; - -namespace Discord.Rest -{ - [DebuggerDisplay("{DebuggerDisplay,nq}")] - internal class GuildUser : IGuildUser, ISnowflakeEntity - { - internal virtual bool IsAttached => false; - bool IEntity.IsAttached => IsAttached; - - private long? _joinedAtTicks; - - public string Nickname { get; private set; } - public GuildPermissions GuildPermissions { get; private set; } - - public Guild Guild { get; private set; } - public User User { get; private set; } - public ImmutableArray Roles { get; private set; } - - public ulong Id => User.Id; - public string AvatarUrl => User.AvatarUrl; - public DateTimeOffset CreatedAt => User.CreatedAt; - public string Discriminator => User.Discriminator; - public ushort DiscriminatorValue => User.DiscriminatorValue; - public bool IsBot => User.IsBot; - public string Mention => MentionUtils.Mention(this, Nickname != null); - public string Username => User.Username; - - public virtual UserStatus Status => UserStatus.Unknown; - public virtual Game Game => null; - - public DiscordRestClient Discord => Guild.Discord; - public DateTimeOffset? JoinedAt => DateTimeUtils.FromTicks(_joinedAtTicks); - - public GuildUser(Guild guild, User user) - { - Guild = guild; - User = user; - Roles = ImmutableArray.Create(); - } - public GuildUser(Guild guild, User user, Model model) - : this(guild, user) - { - Update(model, UpdateSource.Creation); - } - public GuildUser(Guild guild, User user, PresenceModel model) - : this(guild, user) - { - Update(model, UpdateSource.Creation); - } - public void Update(Model model, UpdateSource source) - { - if (source == UpdateSource.Rest && IsAttached) return; - - //if (model.JoinedAt.IsSpecified) - _joinedAtTicks = model.JoinedAt.UtcTicks; - if (model.Nick.IsSpecified) - Nickname = model.Nick.Value; - - //if (model.Roles.IsSpecified) - UpdateRoles(model.Roles); - } - public virtual void Update(PresenceModel model, UpdateSource source) - { - if (source == UpdateSource.Rest && IsAttached) return; - - if (model.Roles.IsSpecified) - UpdateRoles(model.Roles.Value); - if (model.Nick.IsSpecified) - Nickname = model.Nick.Value; - } - private void Update(ModifyGuildMemberParams args, UpdateSource source) - { - if (source == UpdateSource.Rest && IsAttached) return; - - if (args._roleIds.IsSpecified) - Roles = args._roleIds.Value.Select(x => Guild.GetRole(x)).Where(x => x != null).ToImmutableArray(); - if (args._nickname.IsSpecified) - Nickname = args._nickname.Value ?? ""; - } - private void UpdateRoles(ulong[] roleIds) - { - var roles = ImmutableArray.CreateBuilder(roleIds.Length + 1); - roles.Add(Guild.EveryoneRole); - for (int i = 0; i < roleIds.Length; i++) - { - var role = Guild.GetRole(roleIds[i]); - if (role != null) - roles.Add(role); - } - Roles = roles.ToImmutable(); - GuildPermissions = new GuildPermissions(Permissions.ResolveGuild(this)); - } - - public async Task UpdateAsync() - { - if (IsAttached) throw new NotSupportedException(); - - var model = await Discord.ApiClient.GetGuildMemberAsync(Guild.Id, Id).ConfigureAwait(false); - Update(model, UpdateSource.Rest); - } - public async Task ModifyAsync(Action func) - { - if (func == null) throw new NullReferenceException(nameof(func)); - - var args = new ModifyGuildMemberParams(); - func(args); - - bool isCurrentUser = (await Discord.GetCurrentUserAsync().ConfigureAwait(false)).Id == Id; - if (isCurrentUser && args._nickname.IsSpecified) - { - var nickArgs = new ModifyCurrentUserNickParams { Nickname = args._nickname.Value ?? "" }; - await Discord.ApiClient.ModifyMyNickAsync(Guild.Id, nickArgs).ConfigureAwait(false); - args._nickname = Optional.Create(); //Remove - } - - if (!isCurrentUser || args._deaf.IsSpecified || args._mute.IsSpecified || args._roleIds.IsSpecified) - { - await Discord.ApiClient.ModifyGuildMemberAsync(Guild.Id, Id, args).ConfigureAwait(false); - Update(args, UpdateSource.Rest); - } - } - public async Task KickAsync() - { - await Discord.ApiClient.RemoveGuildMemberAsync(Guild.Id, Id).ConfigureAwait(false); - } - - public override string ToString() => $"{Username}#{Discriminator}"; - private string DebuggerDisplay => $"{Username}#{Discriminator} ({Id})"; - - public ChannelPermissions GetPermissions(IGuildChannel channel) - { - if (channel == null) throw new ArgumentNullException(nameof(channel)); - return new ChannelPermissions(Permissions.ResolveChannel(this, channel, GuildPermissions.RawValue)); - } - - public async Task CreateDMChannelAsync() - { - var args = new CreateDMChannelParams { Recipient = this }; - var model = await Discord.ApiClient.CreateDMChannelAsync(args).ConfigureAwait(false); - - return new DMChannel(Discord, new User(model.Recipients.Value[0]), model); - } - - IGuild IGuildUser.Guild => Guild; - IReadOnlyCollection IGuildUser.Roles => Roles; - bool IVoiceState.IsDeafened => false; - bool IVoiceState.IsMuted => false; - bool IVoiceState.IsSelfDeafened => false; - bool IVoiceState.IsSelfMuted => false; - bool IVoiceState.IsSuppressed => false; - IVoiceChannel IVoiceState.VoiceChannel => null; - string IVoiceState.VoiceSessionId => null; - } -} diff --git a/src/Discord.Net/Rest/Entities/Users/SelfUser.cs b/src/Discord.Net/Rest/Entities/Users/SelfUser.cs deleted file mode 100644 index ee32fa243..000000000 --- a/src/Discord.Net/Rest/Entities/Users/SelfUser.cs +++ /dev/null @@ -1,68 +0,0 @@ -using Discord.API.Rest; -using Discord.Rest; -using System; -using System.Threading.Tasks; -using Model = Discord.API.User; - -namespace Discord.Rest -{ - internal class SelfUser : User, ISelfUser - { - protected long _idleSince; - protected UserStatus _status; - protected Game _game; - - public string Email { get; private set; } - public bool IsVerified { get; private set; } - public bool IsMfaEnabled { get; private set; } - - public override UserStatus Status => _status; - public override Game Game => _game; - - public override DiscordRestClient Discord { get; } - - public SelfUser(DiscordRestClient discord, Model model) - : base(model) - { - Discord = discord; - } - public override void Update(Model model, UpdateSource source) - { - if (source == UpdateSource.Rest && IsAttached) return; - - base.Update(model, source); - - if (model.Email.IsSpecified) - Email = model.Email.Value; - if (model.Verified.IsSpecified) - IsVerified = model.Verified.Value; - if (model.MfaEnabled.IsSpecified) - IsMfaEnabled = model.MfaEnabled.Value; - } - - public async Task UpdateAsync() - { - if (IsAttached) throw new NotSupportedException(); - - var model = await Discord.ApiClient.GetMyUserAsync().ConfigureAwait(false); - Update(model, UpdateSource.Rest); - } - public async Task ModifyAsync(Action func) - { - if (func == null) throw new NullReferenceException(nameof(func)); - - var args = new ModifyCurrentUserParams(); - func(args); - - if (!args._username.IsSpecified) - args._username = Username; - if (!args._avatar.IsSpecified && _avatarId != null) - args._avatar = new API.Image(_avatarId); - - var model = await Discord.ApiClient.ModifySelfAsync(args).ConfigureAwait(false); - Update(model, UpdateSource.Rest); - } - - Task ISelfUser.ModifyStatusAsync(Action func) { throw new NotSupportedException(); } - } -} diff --git a/src/Discord.Net/Rest/Entities/Users/User.cs b/src/Discord.Net/Rest/Entities/Users/User.cs deleted file mode 100644 index 8f0877b59..000000000 --- a/src/Discord.Net/Rest/Entities/Users/User.cs +++ /dev/null @@ -1,47 +0,0 @@ -using Discord.Rest; -using System; -using System.Diagnostics; -using Model = Discord.API.User; - -namespace Discord.Rest -{ - [DebuggerDisplay("{DebuggerDisplay,nq}")] - internal class User : SnowflakeEntity, IUser - { - protected string _avatarId; - - public bool IsBot { get; private set; } - public string Username { get; private set; } - public ushort DiscriminatorValue { get; private set; } - - public override DiscordRestClient Discord { get { throw new NotSupportedException(); } } - - public string AvatarUrl => API.CDN.GetUserAvatarUrl(Id, _avatarId); - public string Discriminator => DiscriminatorValue.ToString("D4"); - public string Mention => MentionUtils.Mention(this); - public virtual Game Game => null; - public virtual UserStatus Status => UserStatus.Unknown; - - public User(Model model) - : base(model.Id) - { - Update(model, UpdateSource.Creation); - } - public virtual void Update(Model model, UpdateSource source) - { - if (source == UpdateSource.Rest && IsAttached) return; - - if (model.Avatar.IsSpecified) - _avatarId = model.Avatar.Value; - if (model.Discriminator.IsSpecified) - DiscriminatorValue = ushort.Parse(model.Discriminator.Value); - if (model.Bot.IsSpecified) - IsBot = model.Bot.Value; - if (model.Username.IsSpecified) - Username = model.Username.Value; - } - - public override string ToString() => $"{Username}#{Discriminator}"; - private string DebuggerDisplay => $"{Username}#{Discriminator} ({Id})"; - } -} diff --git a/src/Discord.Net/WebSocket/Entities/Channels/ISocketChannel.cs b/src/Discord.Net/WebSocket/Entities/Channels/ISocketChannel.cs deleted file mode 100644 index d74f35faa..000000000 --- a/src/Discord.Net/WebSocket/Entities/Channels/ISocketChannel.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Model = Discord.API.Channel; - -namespace Discord.WebSocket -{ - internal interface ISocketChannel : IChannel - { - void Update(Model model, UpdateSource source); - - ISocketChannel Clone(); - } -} diff --git a/src/Discord.Net/WebSocket/Entities/Channels/ISocketGuildChannel.cs b/src/Discord.Net/WebSocket/Entities/Channels/ISocketGuildChannel.cs deleted file mode 100644 index f70a56a53..000000000 --- a/src/Discord.Net/WebSocket/Entities/Channels/ISocketGuildChannel.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Discord.WebSocket -{ - internal interface ISocketGuildChannel : ISocketChannel, IGuildChannel - { - new SocketGuild Guild { get; } - } -} diff --git a/src/Discord.Net/WebSocket/Entities/Channels/ISocketMessageChannel.cs b/src/Discord.Net/WebSocket/Entities/Channels/ISocketMessageChannel.cs deleted file mode 100644 index 80706970f..000000000 --- a/src/Discord.Net/WebSocket/Entities/Channels/ISocketMessageChannel.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Collections.Generic; -using MessageModel = Discord.API.Message; - -namespace Discord.WebSocket -{ - internal interface ISocketMessageChannel : ISocketChannel, IMessageChannel - { - IReadOnlyCollection Users { get; } - - ISocketMessage CreateMessage(ISocketUser author, MessageModel model); - ISocketMessage AddMessage(ISocketUser author, MessageModel model); - ISocketMessage GetMessage(ulong id); - ISocketMessage RemoveMessage(ulong id); - - ISocketUser GetUser(ulong id, bool skipCheck = false); - } -} diff --git a/src/Discord.Net/WebSocket/Entities/Channels/ISocketPrivateChannel.cs b/src/Discord.Net/WebSocket/Entities/Channels/ISocketPrivateChannel.cs deleted file mode 100644 index e38107da6..000000000 --- a/src/Discord.Net/WebSocket/Entities/Channels/ISocketPrivateChannel.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.Collections.Generic; - -namespace Discord.WebSocket -{ - internal interface ISocketPrivateChannel : ISocketChannel, IPrivateChannel - { - new IReadOnlyCollection Recipients { get; } - } -} diff --git a/src/Discord.Net/WebSocket/Extensions/ChannelExtensions.cs b/src/Discord.Net/WebSocket/Extensions/ChannelExtensions.cs deleted file mode 100644 index ab9911cac..000000000 --- a/src/Discord.Net/WebSocket/Extensions/ChannelExtensions.cs +++ /dev/null @@ -1,65 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace Discord.WebSocket -{ - public static class ChannelExtensions - { - public static IUser GetUser(this IDMChannel channel, ulong id) - => GetSocketDMChannel(channel).GetUser(id); - - public static IReadOnlyCollection GetUsers(this IDMChannel channel) - => GetSocketDMChannel(channel).Users; - - public static IUser GetUser(this IGroupChannel channel, ulong id) - => GetSocketGroupChannel(channel).GetUser(id); - - public static IReadOnlyCollection GetUsers(this IGroupChannel channel) - => GetSocketGroupChannel(channel).Users; - - public static IGuildUser GetUser(this ITextChannel channel, ulong id) - => GetSocketTextChannel(channel).GetUser(id); - - public static IReadOnlyCollection GetUsers(this ITextChannel channel) - => GetSocketTextChannel(channel).Members; - - public static IGuildUser GetUser(this IVoiceChannel channel, ulong id) - => GetSocketVoiceChannel(channel).GetUser(id); - - public static IReadOnlyCollection GetUsers(this IVoiceChannel channel) - => GetSocketVoiceChannel(channel).Members; - - internal static SocketDMChannel GetSocketDMChannel(IDMChannel channel) - { - Preconditions.NotNull(channel, nameof(channel)); - var socketChannel = channel as SocketDMChannel; - if (socketChannel == null) - throw new InvalidOperationException("This extension method is only valid on WebSocket Entities"); - return socketChannel; - } - internal static SocketGroupChannel GetSocketGroupChannel(IGroupChannel channel) - { - Preconditions.NotNull(channel, nameof(channel)); - var socketChannel = channel as SocketGroupChannel; - if (socketChannel == null) - throw new InvalidOperationException("This extension method is only valid on WebSocket Entities"); - return socketChannel; - } - internal static SocketTextChannel GetSocketTextChannel(ITextChannel channel) - { - Preconditions.NotNull(channel, nameof(channel)); - var socketChannel = channel as SocketTextChannel; - if (socketChannel == null) - throw new InvalidOperationException("This extension method is only valid on WebSocket Entities"); - return socketChannel; - } - internal static SocketVoiceChannel GetSocketVoiceChannel(IVoiceChannel channel) - { - Preconditions.NotNull(channel, nameof(channel)); - var socketChannel = channel as SocketVoiceChannel; - if (socketChannel == null) - throw new InvalidOperationException("This extension method is only valid on WebSocket Entities"); - return socketChannel; - } - } -} diff --git a/src/Discord.Net/WebSocket/Extensions/DiscordClientExtensions.cs b/src/Discord.Net/WebSocket/Extensions/DiscordClientExtensions.cs deleted file mode 100644 index 8a5cfc9bd..000000000 --- a/src/Discord.Net/WebSocket/Extensions/DiscordClientExtensions.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System.Collections.Generic; -using System.Linq; - -namespace Discord.WebSocket -{ - public static class DiscordClientExtensions - { - public static IPrivateChannel GetPrivateChannel(this DiscordSocketClient client, ulong id) - => client.GetChannel(id) as IPrivateChannel; - - public static IDMChannel GetDMChannel(this DiscordSocketClient client, ulong id) - => client.GetPrivateChannelAsync(id) as IDMChannel; - public static IEnumerable GetDMChannels(this DiscordSocketClient client) - => client.GetPrivateChannels().Select(x => x as IDMChannel).Where(x => x != null); - - public static IGroupChannel GetGroupChannel(this DiscordSocketClient client, ulong id) - => client.GetPrivateChannel(id) as IGroupChannel; - public static IEnumerable GetGroupChannels(this DiscordSocketClient client) - => client.GetPrivateChannels().Select(x => x as IGroupChannel).Where(x => x != null); - - public static IVoiceRegion GetVoiceRegion(this DiscordSocketClient client, string id) - => client.VoiceRegions.FirstOrDefault(r => r.Id == id); - public static IReadOnlyCollection GetVoiceRegions(this DiscordSocketClient client) => - client.VoiceRegions; - public static IVoiceRegion GetOptimalVoiceRegion(this DiscordSocketClient client) - => client.VoiceRegions.FirstOrDefault(x => x.IsOptimal); - - public static IGuild GetGuild(this DiscordSocketClient client, ulong id) => - client.DataStore.GetGuild(id); - public static GuildEmbed? GetGuildEmbed(this DiscordSocketClient client, ulong id) - { - var guild = client.DataStore.GetGuild(id); - if (guild != null) - return new GuildEmbed(guild.IsEmbeddable, guild.EmbedChannelId); - return null; - } - public static IReadOnlyCollection GetGuilds(this DiscordSocketClient client) => - client.Guilds; - - public static IChannel GetChannel(this DiscordSocketClient client, ulong id) => - client.DataStore.GetChannel(id); - public static IReadOnlyCollection GetPrivateChannels(this DiscordSocketClient client) => - client.DataStore.PrivateChannels; - - public static IUser GetUser(this DiscordSocketClient client, ulong id) => - client.DataStore.GetUser(id); - public static IUser GetUser(this DiscordSocketClient client, string username, string discriminator) => - client.DataStore.Users.Where(x => x.Discriminator == discriminator && x.Username == username).FirstOrDefault(); - public static ISelfUser GetCurrentUser(this DiscordSocketClient client) => - client.CurrentUser; - - } -} diff --git a/src/Discord.Net/WebSocket/Extensions/GuildExtensions.cs b/src/Discord.Net/WebSocket/Extensions/GuildExtensions.cs deleted file mode 100644 index a2c5c7882..000000000 --- a/src/Discord.Net/WebSocket/Extensions/GuildExtensions.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; - -namespace Discord.WebSocket -{ - // Todo: Docstrings - public static class GuildExtensions - { - // Channels - public static IGuildChannel GetChannel(this IGuild guild, ulong id) => - GetSocketGuild(guild).GetChannel(id); - public static IReadOnlyCollection GetChannels(this IGuild guild) => - GetSocketGuild(guild).Channels; - - public static ITextChannel GetTextChannel(this IGuild guild, ulong id) => - GetSocketGuild(guild).GetChannel(id) as ITextChannel; - public static IEnumerable GetTextChannels(this IGuild guild) => - GetSocketGuild(guild).Channels.Select(c => c as ITextChannel).Where(c => c != null); - - - public static IVoiceChannel GetVoiceChannel(this IGuild guild, ulong id) => - GetSocketGuild(guild).GetChannel(id) as IVoiceChannel; - public static IEnumerable GetVoiceChannels(this IGuild guild) => - GetSocketGuild(guild).Channels.Select(c => c as IVoiceChannel).Where(c => c != null); - - // Users - public static IGuildUser GetCurrentUser(this IGuild guild) => - GetSocketGuild(guild).CurrentUser; - public static IGuildUser GetUser(this IGuild guild, ulong id) => - GetSocketGuild(guild).GetUser(id); - - public static IReadOnlyCollection GetUsers(this IGuild guild) => - GetSocketGuild(guild).Members; - - public static int GetUserCount(this IGuild guild) => - GetSocketGuild(guild).MemberCount; - public static int GetCachedUserCount(this IGuild guild) => - GetSocketGuild(guild).DownloadedMemberCount; - - //Helpers - internal static SocketGuild GetSocketGuild(IGuild guild) - { - Preconditions.NotNull(guild, nameof(guild)); - var socketGuild = guild as SocketGuild; - if (socketGuild == null) - throw new InvalidOperationException("This extension method is only valid on WebSocket Entities"); - return socketGuild; - } - } -} diff --git a/src/Discord.Net/project.json b/src/Discord.Net/project.json index 52fbc34c9..8185a792d 100644 --- a/src/Discord.Net/project.json +++ b/src/Discord.Net/project.json @@ -1,6 +1,6 @@ -{ - "version": "1.0.0-beta-*", - "description": "An unofficial .Net API wrapper for the Discord service.", +{ + "version": "1.0.0-beta2-*", + "description": "An aynchronous API wrapper for Discord using .NET. This package includes all of the optional Discord.Net components", "authors": [ "RogueException" ], "packOptions": { @@ -13,37 +13,19 @@ } }, - "buildOptions": { - "allowUnsafe": true, - "warningsAsErrors": false, - "xmlDoc": true - }, - - "configurations": { - "Release": { - "buildOptions": { - "define": [ "RELEASE" ], - "nowarn": [ "CS1573", "CS1591" ] - } - } - }, - "dependencies": { - "Microsoft.Win32.Primitives": "4.0.1", - "Newtonsoft.Json": "8.0.3", - "System.Collections.Concurrent": "4.0.12", - "System.Collections.Immutable": "1.2.0", - "System.IO.Compression": "4.1.0", - "System.IO.FileSystem": "4.0.1", - "System.Net.Http": "4.1.0", - "System.Net.NameResolution": "4.0.0", - "System.Net.Sockets": "4.1.0", - "System.Net.WebSockets.Client": "4.0.0", - "System.Reflection.Extensions": "4.0.1", - "System.Runtime.InteropServices": "4.1.0", - "System.Runtime.InteropServices.RuntimeInformation": "4.0.0", - "System.Runtime.Serialization.Primitives": "4.1.1", - "System.Text.RegularExpressions": "4.1.0" + "Discord.Net.Rest": { + "target": "project" + } + //"Discord.Net.WebSocket": { + // "target": "project" + //}, + //"Discord.Net.Rpc": { + // "target": "project" + //}, + //"Discord.Net.Commands": { + // "target": "project" + //} }, "frameworks": {