diff --git a/src/Discord.Net.Commands/CommandService.cs b/src/Discord.Net.Commands/CommandService.cs index 291fba246..096fc9e11 100644 --- a/src/Discord.Net.Commands/CommandService.cs +++ b/src/Discord.Net.Commands/CommandService.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.Immutable; @@ -352,7 +352,7 @@ namespace Discord.Commands /// public void AddTypeReader(Type type, TypeReader reader, bool replaceDefault) { - if (replaceDefault && _defaultTypeReaders.ContainsKey(type)) + if (replaceDefault && HasDefaultTypeReader(type)) { _defaultTypeReaders.AddOrUpdate(type, reader, (k, v) => reader); if (type.GetTypeInfo().IsValueType) @@ -371,6 +371,16 @@ namespace Discord.Commands AddNullableTypeReader(type, reader); } } + internal bool HasDefaultTypeReader(Type type) + { + if (_defaultTypeReaders.ContainsKey(type)) + return true; + + var typeInfo = type.GetTypeInfo(); + if (typeInfo.IsEnum) + return true; + return _entityTypeReaders.Any(x => type == x.Item1 || typeInfo.ImplementedInterfaces.Contains(x.Item2)); + } internal void AddNullableTypeReader(Type valueType, TypeReader valueTypeReader) { var readers = _typeReaders.GetOrAdd(typeof(Nullable<>).MakeGenericType(valueType), x => new ConcurrentDictionary()); diff --git a/src/Discord.Net.Rest/Entities/Channels/ChannelType.cs b/src/Discord.Net.Core/Entities/Channels/ChannelType.cs similarity index 100% rename from src/Discord.Net.Rest/Entities/Channels/ChannelType.cs rename to src/Discord.Net.Core/Entities/Channels/ChannelType.cs diff --git a/src/Discord.Net.Core/Entities/Guilds/IGuild.cs b/src/Discord.Net.Core/Entities/Guilds/IGuild.cs index 8d6c090b5..2f7aeca52 100644 --- a/src/Discord.Net.Core/Entities/Guilds/IGuild.cs +++ b/src/Discord.Net.Core/Entities/Guilds/IGuild.cs @@ -455,6 +455,15 @@ namespace Discord /// An awaitable containing a collection of invites found within this guild. /// Task> GetInvitesAsync(RequestOptions options = null); + /// + /// Gets the vanity invite URL of this guild. + /// + /// The options to be used when sending the request. + /// + /// An awaitable containing the partial metadata of the vanity invite found within + /// this guild. + /// + Task GetVanityInviteAsync(RequestOptions options = null); /// /// Gets a role in this guild. diff --git a/src/Discord.Net.Core/Entities/Invites/IInvite.cs b/src/Discord.Net.Core/Entities/Invites/IInvite.cs index 90dde7474..6455b9f5c 100644 --- a/src/Discord.Net.Core/Entities/Invites/IInvite.cs +++ b/src/Discord.Net.Core/Entities/Invites/IInvite.cs @@ -27,6 +27,8 @@ namespace Discord /// A generic channel that the invite points to. /// IChannel Channel { get; } + /// Gets the type of the channel this invite is linked to. + ChannelType ChannelType { get; } /// /// Gets the ID of the channel this invite is linked to. /// @@ -55,7 +57,7 @@ namespace Discord /// /// An representing the guild snowflake identifier that the invite points to. /// - ulong GuildId { get; } + ulong? GuildId { get; } /// /// Gets the name of the guild this invite is linked to. /// diff --git a/src/Discord.Net.Core/Entities/Invites/IInviteMetadata.cs b/src/Discord.Net.Core/Entities/Invites/IInviteMetadata.cs index 20f13a74c..2279b59fc 100644 --- a/src/Discord.Net.Core/Entities/Invites/IInviteMetadata.cs +++ b/src/Discord.Net.Core/Entities/Invites/IInviteMetadata.cs @@ -52,13 +52,13 @@ namespace Discord /// /// An representing the number of times this invite has been used. /// - int Uses { get; } + int? Uses { get; } /// /// Gets when this invite was created. /// /// /// A representing the time of which the invite was first created. /// - DateTimeOffset CreatedAt { get; } + DateTimeOffset? CreatedAt { get; } } } diff --git a/src/Discord.Net.Rest/API/Common/Invite.cs b/src/Discord.Net.Rest/API/Common/Invite.cs index 1b35da870..649bc37ec 100644 --- a/src/Discord.Net.Rest/API/Common/Invite.cs +++ b/src/Discord.Net.Rest/API/Common/Invite.cs @@ -8,7 +8,7 @@ namespace Discord.API [JsonProperty("code")] public string Code { get; set; } [JsonProperty("guild")] - public InviteGuild Guild { get; set; } + public Optional Guild { get; set; } [JsonProperty("channel")] public InviteChannel Channel { get; set; } [JsonProperty("approximate_presence_count")] diff --git a/src/Discord.Net.Rest/API/Common/InviteChannel.cs b/src/Discord.Net.Rest/API/Common/InviteChannel.cs index ca9699067..f8f2a34f2 100644 --- a/src/Discord.Net.Rest/API/Common/InviteChannel.cs +++ b/src/Discord.Net.Rest/API/Common/InviteChannel.cs @@ -1,4 +1,4 @@ -#pragma warning disable CS1591 +#pragma warning disable CS1591 using Newtonsoft.Json; namespace Discord.API @@ -10,6 +10,6 @@ namespace Discord.API [JsonProperty("name")] public string Name { get; set; } [JsonProperty("type")] - public string Type { get; set; } + public int Type { get; set; } } } diff --git a/src/Discord.Net.Rest/API/Common/InviteMetadata.cs b/src/Discord.Net.Rest/API/Common/InviteMetadata.cs index a78017ffb..ca019b79b 100644 --- a/src/Discord.Net.Rest/API/Common/InviteMetadata.cs +++ b/src/Discord.Net.Rest/API/Common/InviteMetadata.cs @@ -9,15 +9,15 @@ namespace Discord.API [JsonProperty("inviter")] public User Inviter { get; set; } [JsonProperty("uses")] - public int Uses { get; set; } + public Optional Uses { get; set; } [JsonProperty("max_uses")] - public int MaxUses { get; set; } + public Optional MaxUses { get; set; } [JsonProperty("max_age")] - public int MaxAge { get; set; } + public Optional MaxAge { get; set; } [JsonProperty("temporary")] public bool Temporary { get; set; } [JsonProperty("created_at")] - public DateTimeOffset CreatedAt { get; set; } + public Optional CreatedAt { get; set; } [JsonProperty("revoked")] public bool Revoked { get; set; } } diff --git a/src/Discord.Net.Rest/API/Rest/GetInviteParams.cs b/src/Discord.Net.Rest/API/Rest/GetInviteParams.cs deleted file mode 100644 index cb8d8f7fe..000000000 --- a/src/Discord.Net.Rest/API/Rest/GetInviteParams.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Discord.API.Rest -{ - internal class GetInviteParams - { - public Optional WithCounts { get; set; } - } -} diff --git a/src/Discord.Net.Rest/ClientHelper.cs b/src/Discord.Net.Rest/ClientHelper.cs index 2e7f11801..a8f6b58ef 100644 --- a/src/Discord.Net.Rest/ClientHelper.cs +++ b/src/Discord.Net.Rest/ClientHelper.cs @@ -53,13 +53,9 @@ namespace Discord.Rest } public static async Task GetInviteAsync(BaseDiscordClient client, - string inviteId, bool withCount, RequestOptions options) + string inviteId, RequestOptions options) { - var args = new GetInviteParams - { - WithCounts = withCount - }; - var model = await client.ApiClient.GetInviteAsync(inviteId, args, options).ConfigureAwait(false); + var model = await client.ApiClient.GetInviteAsync(inviteId, options).ConfigureAwait(false); if (model != null) return RestInviteMetadata.Create(client, null, null, model); return null; diff --git a/src/Discord.Net.Rest/DiscordRestApiClient.cs b/src/Discord.Net.Rest/DiscordRestApiClient.cs index 194046863..93047cbc1 100644 --- a/src/Discord.Net.Rest/DiscordRestApiClient.cs +++ b/src/Discord.Net.Rest/DiscordRestApiClient.cs @@ -918,7 +918,7 @@ namespace Discord.API } //Guild Invites - public async Task GetInviteAsync(string inviteId, GetInviteParams args, RequestOptions options = null) + public async Task GetInviteAsync(string inviteId, RequestOptions options = null) { Preconditions.NotNullOrEmpty(inviteId, nameof(inviteId)); options = RequestOptions.CreateOrClone(options); @@ -931,14 +931,20 @@ namespace Discord.API if (index >= 0) inviteId = inviteId.Substring(index + 1); - var withCounts = args.WithCounts.GetValueOrDefault(false); - try { - return await SendAsync("GET", () => $"invites/{inviteId}?with_counts={withCounts}", new BucketIds(), options: options).ConfigureAwait(false); + return await SendAsync("GET", () => $"invites/{inviteId}?with_counts=true", new BucketIds(), options: options).ConfigureAwait(false); } catch (HttpException ex) when (ex.HttpCode == HttpStatusCode.NotFound) { return null; } } + public async Task GetVanityInviteAsync(ulong guildId, RequestOptions options = null) + { + Preconditions.NotEqual(guildId, 0, nameof(guildId)); + options = RequestOptions.CreateOrClone(options); + + var ids = new BucketIds(guildId: guildId); + return await SendAsync("GET", () => $"guilds/{guildId}/vanity-url", ids, options: options).ConfigureAwait(false); + } public async Task> GetGuildInvitesAsync(ulong guildId, RequestOptions options = null) { Preconditions.NotEqual(guildId, 0, nameof(guildId)); diff --git a/src/Discord.Net.Rest/DiscordRestClient.cs b/src/Discord.Net.Rest/DiscordRestClient.cs index 2737e1ac1..c1ed9cd15 100644 --- a/src/Discord.Net.Rest/DiscordRestClient.cs +++ b/src/Discord.Net.Rest/DiscordRestClient.cs @@ -65,7 +65,7 @@ namespace Discord.Rest => ClientHelper.GetConnectionsAsync(this, options); public Task GetInviteAsync(string inviteId, bool withCount = false, RequestOptions options = null) - => ClientHelper.GetInviteAsync(this, inviteId, withCount, options); + => ClientHelper.GetInviteAsync(this, inviteId, options); public Task GetGuildAsync(ulong id, RequestOptions options = null) => ClientHelper.GetGuildAsync(this, id, options); diff --git a/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs b/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs index 195e6f3c0..f5903c0be 100644 --- a/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs +++ b/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs @@ -214,6 +214,12 @@ namespace Discord.Rest var models = await client.ApiClient.GetGuildInvitesAsync(guild.Id, options).ConfigureAwait(false); return models.Select(x => RestInviteMetadata.Create(client, guild, null, x)).ToImmutableArray(); } + public static async Task GetVanityInviteAsync(IGuild guild, BaseDiscordClient client, + RequestOptions options) + { + var model = await client.ApiClient.GetVanityInviteAsync(guild.Id, options).ConfigureAwait(false); + return RestInviteMetadata.Create(client, guild, null, model); + } //Roles /// is null. diff --git a/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs b/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs index 560f7b629..606d24450 100644 --- a/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs +++ b/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs @@ -303,6 +303,15 @@ namespace Discord.Rest //Invites public Task> GetInvitesAsync(RequestOptions options = null) => GuildHelper.GetInvitesAsync(this, Discord, options); + /// + /// Gets the vanity invite URL of this guild. + /// + /// The options to be used when sending the request. + /// + /// A partial metadata of the vanity invite found within this guild. + /// + public Task GetVanityInviteAsync(RequestOptions options = null) + => GuildHelper.GetVanityInviteAsync(this, Discord, options); //Roles public RestRole GetRole(ulong id) @@ -496,6 +505,9 @@ namespace Discord.Rest /// async Task> IGuild.GetInvitesAsync(RequestOptions options) => await GetInvitesAsync(options).ConfigureAwait(false); + /// + async Task IGuild.GetVanityInviteAsync(RequestOptions options) + => await GetVanityInviteAsync(options).ConfigureAwait(false); /// IRole IGuild.GetRole(ulong id) diff --git a/src/Discord.Net.Rest/Entities/Invites/RestInvite.cs b/src/Discord.Net.Rest/Entities/Invites/RestInvite.cs index 4ada0ffe2..e498295cc 100644 --- a/src/Discord.Net.Rest/Entities/Invites/RestInvite.cs +++ b/src/Discord.Net.Rest/Entities/Invites/RestInvite.cs @@ -1,7 +1,6 @@ using System; using System.Diagnostics; using System.Threading.Tasks; -using Discord.API.Rest; using Model = Discord.API.Invite; namespace Discord.Rest @@ -9,6 +8,7 @@ namespace Discord.Rest [DebuggerDisplay(@"{DebuggerDisplay,nq}")] public class RestInvite : RestEntity, IInvite, IUpdateable { + public ChannelType ChannelType { get; private set; } /// public string ChannelName { get; private set; } /// @@ -20,7 +20,7 @@ namespace Discord.Rest /// public ulong ChannelId { get; private set; } /// - public ulong GuildId { get; private set; } + public ulong? GuildId { get; private set; } internal IChannel Channel { get; } internal IGuild Guild { get; } @@ -43,21 +43,19 @@ namespace Discord.Rest } internal void Update(Model model) { - GuildId = model.Guild.Id; + GuildId = model.Guild.IsSpecified ? model.Guild.Value.Id : default(ulong?); ChannelId = model.Channel.Id; - GuildName = model.Guild.Name; + GuildName = model.Guild.IsSpecified ? model.Guild.Value.Name : null; ChannelName = model.Channel.Name; MemberCount = model.MemberCount.IsSpecified ? model.MemberCount.Value : null; PresenceCount = model.PresenceCount.IsSpecified ? model.PresenceCount.Value : null; + ChannelType = (ChannelType)model.Channel.Type; } /// public async Task UpdateAsync(RequestOptions options = null) { - var args = new GetInviteParams(); - if (MemberCount != null || PresenceCount != null) - args.WithCounts = true; - var model = await Discord.ApiClient.GetInviteAsync(Code, args, options).ConfigureAwait(false); + var model = await Discord.ApiClient.GetInviteAsync(Code, options).ConfigureAwait(false); Update(model); } /// diff --git a/src/Discord.Net.Rest/Entities/Invites/RestInviteMetadata.cs b/src/Discord.Net.Rest/Entities/Invites/RestInviteMetadata.cs index d253d562e..55acd5f45 100644 --- a/src/Discord.Net.Rest/Entities/Invites/RestInviteMetadata.cs +++ b/src/Discord.Net.Rest/Entities/Invites/RestInviteMetadata.cs @@ -6,7 +6,7 @@ namespace Discord.Rest /// Represents additional information regarding the REST-based invite object. public class RestInviteMetadata : RestInvite, IInviteMetadata { - private long _createdAtTicks; + private long? _createdAtTicks; /// public bool IsRevoked { get; private set; } @@ -17,14 +17,14 @@ namespace Discord.Rest /// public int? MaxUses { get; private set; } /// - public int Uses { get; private set; } + public int? Uses { get; private set; } /// /// Gets the user that created this invite. /// public RestUser Inviter { get; private set; } /// - public DateTimeOffset CreatedAt => DateTimeUtils.FromTicks(_createdAtTicks); + public DateTimeOffset? CreatedAt => DateTimeUtils.FromTicks(_createdAtTicks); internal RestInviteMetadata(BaseDiscordClient discord, IGuild guild, IChannel channel, string id) : base(discord, guild, channel, id) @@ -42,10 +42,10 @@ namespace Discord.Rest Inviter = model.Inviter != null ? RestUser.Create(Discord, model.Inviter) : null; IsRevoked = model.Revoked; IsTemporary = model.Temporary; - MaxAge = model.MaxAge != 0 ? model.MaxAge : (int?)null; - MaxUses = model.MaxUses; - Uses = model.Uses; - _createdAtTicks = model.CreatedAt.UtcTicks; + MaxAge = model.MaxAge.IsSpecified ? model.MaxAge.Value : (int?)null; + MaxUses = model.MaxUses.IsSpecified ? model.MaxUses.Value : (int?)null; + Uses = model.Uses.IsSpecified ? model.Uses.Value : (int?)null; + _createdAtTicks = model.CreatedAt.IsSpecified ? model.CreatedAt.Value.UtcTicks : (long?)null; } /// diff --git a/src/Discord.Net.WebSocket/BaseSocketClient.cs b/src/Discord.Net.WebSocket/BaseSocketClient.cs index f176b6260..905a44fb6 100644 --- a/src/Discord.Net.WebSocket/BaseSocketClient.cs +++ b/src/Discord.Net.WebSocket/BaseSocketClient.cs @@ -215,7 +215,7 @@ namespace Discord.WebSocket /// An awaitable containing the invite information. /// public Task GetInviteAsync(string inviteId, bool withCount = false, RequestOptions options = null) - => ClientHelper.GetInviteAsync(this, inviteId, withCount, options ?? RequestOptions.Default); + => ClientHelper.GetInviteAsync(this, inviteId, options ?? RequestOptions.Default); // IDiscordClient /// diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs index f3d2d5b84..f3f400661 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs @@ -1559,6 +1559,9 @@ namespace Discord.WebSocket case "MESSAGE_ACK": await _gatewayLogger.DebugAsync("Ignored Dispatch (MESSAGE_ACK)").ConfigureAwait(false); break; + case "PRESENCES_REPLACE": + await _gatewayLogger.DebugAsync("Ignored Dispatch (PRESENCES_REPLACE)").ConfigureAwait(false); + break; case "USER_SETTINGS_UPDATE": await _gatewayLogger.DebugAsync("Ignored Dispatch (USER_SETTINGS_UPDATE)").ConfigureAwait(false); break; diff --git a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs index c1fd15255..fef0c2f90 100644 --- a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs +++ b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs @@ -534,6 +534,15 @@ namespace Discord.WebSocket /// public Task> GetInvitesAsync(RequestOptions options = null) => GuildHelper.GetInvitesAsync(this, Discord, options); + /// + /// Gets the vanity invite URL of this guild. + /// + /// The options to be used when sending the request. + /// + /// A partial metadata of the vanity invite found within this guild. + /// + public Task GetVanityInviteAsync(RequestOptions options = null) + => GuildHelper.GetVanityInviteAsync(this, Discord, options); //Roles /// @@ -971,6 +980,9 @@ namespace Discord.WebSocket /// async Task> IGuild.GetInvitesAsync(RequestOptions options) => await GetInvitesAsync(options).ConfigureAwait(false); + /// + async Task IGuild.GetVanityInviteAsync(RequestOptions options) + => await GetVanityInviteAsync(options).ConfigureAwait(false); /// IRole IGuild.GetRole(ulong id)