@@ -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 | |||
/// </param> | |||
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<Type, TypeReader>()); | |||
@@ -455,6 +455,15 @@ namespace Discord | |||
/// An awaitable <see cref="Task"/> containing a collection of invites found within this guild. | |||
/// </returns> | |||
Task<IReadOnlyCollection<IInviteMetadata>> GetInvitesAsync(RequestOptions options = null); | |||
/// <summary> | |||
/// Gets the vanity invite URL of this guild. | |||
/// </summary> | |||
/// <param name="options">The options to be used when sending the request.</param> | |||
/// <returns> | |||
/// An awaitable <see cref="Task"/> containing the partial metadata of the vanity invite found within | |||
/// this guild. | |||
/// </returns> | |||
Task<IInviteMetadata> GetVanityInviteAsync(RequestOptions options = null); | |||
/// <summary> | |||
/// Gets a role in this guild. | |||
@@ -27,6 +27,8 @@ namespace Discord | |||
/// A generic channel that the invite points to. | |||
/// </returns> | |||
IChannel Channel { get; } | |||
/// <summary> Gets the type of the channel this invite is linked to. </summary> | |||
ChannelType ChannelType { get; } | |||
/// <summary> | |||
/// Gets the ID of the channel this invite is linked to. | |||
/// </summary> | |||
@@ -55,7 +57,7 @@ namespace Discord | |||
/// <returns> | |||
/// An <see cref="ulong"/> representing the guild snowflake identifier that the invite points to. | |||
/// </returns> | |||
ulong GuildId { get; } | |||
ulong? GuildId { get; } | |||
/// <summary> | |||
/// Gets the name of the guild this invite is linked to. | |||
/// </summary> | |||
@@ -52,13 +52,13 @@ namespace Discord | |||
/// <returns> | |||
/// An <see cref="int"/> representing the number of times this invite has been used. | |||
/// </returns> | |||
int Uses { get; } | |||
int? Uses { get; } | |||
/// <summary> | |||
/// Gets when this invite was created. | |||
/// </summary> | |||
/// <returns> | |||
/// A <see cref="DateTimeOffset"/> representing the time of which the invite was first created. | |||
/// </returns> | |||
DateTimeOffset CreatedAt { get; } | |||
DateTimeOffset? CreatedAt { get; } | |||
} | |||
} |
@@ -8,7 +8,7 @@ namespace Discord.API | |||
[JsonProperty("code")] | |||
public string Code { get; set; } | |||
[JsonProperty("guild")] | |||
public InviteGuild Guild { get; set; } | |||
public Optional<InviteGuild> Guild { get; set; } | |||
[JsonProperty("channel")] | |||
public InviteChannel Channel { get; set; } | |||
[JsonProperty("approximate_presence_count")] | |||
@@ -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; } | |||
} | |||
} |
@@ -9,15 +9,15 @@ namespace Discord.API | |||
[JsonProperty("inviter")] | |||
public User Inviter { get; set; } | |||
[JsonProperty("uses")] | |||
public int Uses { get; set; } | |||
public Optional<int> Uses { get; set; } | |||
[JsonProperty("max_uses")] | |||
public int MaxUses { get; set; } | |||
public Optional<int> MaxUses { get; set; } | |||
[JsonProperty("max_age")] | |||
public int MaxAge { get; set; } | |||
public Optional<int> MaxAge { get; set; } | |||
[JsonProperty("temporary")] | |||
public bool Temporary { get; set; } | |||
[JsonProperty("created_at")] | |||
public DateTimeOffset CreatedAt { get; set; } | |||
public Optional<DateTimeOffset> CreatedAt { get; set; } | |||
[JsonProperty("revoked")] | |||
public bool Revoked { get; set; } | |||
} | |||
@@ -1,7 +0,0 @@ | |||
namespace Discord.API.Rest | |||
{ | |||
internal class GetInviteParams | |||
{ | |||
public Optional<bool?> WithCounts { get; set; } | |||
} | |||
} |
@@ -53,13 +53,9 @@ namespace Discord.Rest | |||
} | |||
public static async Task<RestInviteMetadata> 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; | |||
@@ -918,7 +918,7 @@ namespace Discord.API | |||
} | |||
//Guild Invites | |||
public async Task<InviteMetadata> GetInviteAsync(string inviteId, GetInviteParams args, RequestOptions options = null) | |||
public async Task<InviteMetadata> 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<InviteMetadata>("GET", () => $"invites/{inviteId}?with_counts={withCounts}", new BucketIds(), options: options).ConfigureAwait(false); | |||
return await SendAsync<InviteMetadata>("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<InviteMetadata> 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<InviteMetadata>("GET", () => $"guilds/{guildId}/vanity-url", ids, options: options).ConfigureAwait(false); | |||
} | |||
public async Task<IReadOnlyCollection<InviteMetadata>> GetGuildInvitesAsync(ulong guildId, RequestOptions options = null) | |||
{ | |||
Preconditions.NotEqual(guildId, 0, nameof(guildId)); | |||
@@ -65,7 +65,7 @@ namespace Discord.Rest | |||
=> ClientHelper.GetConnectionsAsync(this, options); | |||
public Task<RestInviteMetadata> GetInviteAsync(string inviteId, bool withCount = false, RequestOptions options = null) | |||
=> ClientHelper.GetInviteAsync(this, inviteId, withCount, options); | |||
=> ClientHelper.GetInviteAsync(this, inviteId, options); | |||
public Task<RestGuild> GetGuildAsync(ulong id, RequestOptions options = null) | |||
=> ClientHelper.GetGuildAsync(this, id, options); | |||
@@ -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<RestInviteMetadata> 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 | |||
/// <exception cref="ArgumentNullException"><paramref name="name"/> is <c>null</c>.</exception> | |||
@@ -303,6 +303,15 @@ namespace Discord.Rest | |||
//Invites | |||
public Task<IReadOnlyCollection<RestInviteMetadata>> GetInvitesAsync(RequestOptions options = null) | |||
=> GuildHelper.GetInvitesAsync(this, Discord, options); | |||
/// <summary> | |||
/// Gets the vanity invite URL of this guild. | |||
/// </summary> | |||
/// <param name="options">The options to be used when sending the request.</param> | |||
/// <returns> | |||
/// A partial metadata of the vanity invite found within this guild. | |||
/// </returns> | |||
public Task<RestInviteMetadata> GetVanityInviteAsync(RequestOptions options = null) | |||
=> GuildHelper.GetVanityInviteAsync(this, Discord, options); | |||
//Roles | |||
public RestRole GetRole(ulong id) | |||
@@ -496,6 +505,9 @@ namespace Discord.Rest | |||
/// <inheritdoc /> | |||
async Task<IReadOnlyCollection<IInviteMetadata>> IGuild.GetInvitesAsync(RequestOptions options) | |||
=> await GetInvitesAsync(options).ConfigureAwait(false); | |||
/// <inheritdoc /> | |||
async Task<IInviteMetadata> IGuild.GetVanityInviteAsync(RequestOptions options) | |||
=> await GetVanityInviteAsync(options).ConfigureAwait(false); | |||
/// <inheritdoc /> | |||
IRole IGuild.GetRole(ulong id) | |||
@@ -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<string>, IInvite, IUpdateable | |||
{ | |||
public ChannelType ChannelType { get; private set; } | |||
/// <inheritdoc /> | |||
public string ChannelName { get; private set; } | |||
/// <inheritdoc /> | |||
@@ -20,7 +20,7 @@ namespace Discord.Rest | |||
/// <inheritdoc /> | |||
public ulong ChannelId { get; private set; } | |||
/// <inheritdoc /> | |||
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; | |||
} | |||
/// <inheritdoc /> | |||
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); | |||
} | |||
/// <inheritdoc /> | |||
@@ -6,7 +6,7 @@ namespace Discord.Rest | |||
/// <summary> Represents additional information regarding the REST-based invite object. </summary> | |||
public class RestInviteMetadata : RestInvite, IInviteMetadata | |||
{ | |||
private long _createdAtTicks; | |||
private long? _createdAtTicks; | |||
/// <inheritdoc /> | |||
public bool IsRevoked { get; private set; } | |||
@@ -17,14 +17,14 @@ namespace Discord.Rest | |||
/// <inheritdoc /> | |||
public int? MaxUses { get; private set; } | |||
/// <inheritdoc /> | |||
public int Uses { get; private set; } | |||
public int? Uses { get; private set; } | |||
/// <summary> | |||
/// Gets the user that created this invite. | |||
/// </summary> | |||
public RestUser Inviter { get; private set; } | |||
/// <inheritdoc /> | |||
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; | |||
} | |||
/// <inheritdoc /> | |||
@@ -215,7 +215,7 @@ namespace Discord.WebSocket | |||
/// An awaitable <see cref="Task"/> containing the invite information. | |||
/// </returns> | |||
public Task<RestInviteMetadata> 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 | |||
/// <inheritdoc /> | |||
@@ -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; | |||
@@ -534,6 +534,15 @@ namespace Discord.WebSocket | |||
/// </returns> | |||
public Task<IReadOnlyCollection<RestInviteMetadata>> GetInvitesAsync(RequestOptions options = null) | |||
=> GuildHelper.GetInvitesAsync(this, Discord, options); | |||
/// <summary> | |||
/// Gets the vanity invite URL of this guild. | |||
/// </summary> | |||
/// <param name="options">The options to be used when sending the request.</param> | |||
/// <returns> | |||
/// A partial metadata of the vanity invite found within this guild. | |||
/// </returns> | |||
public Task<RestInviteMetadata> GetVanityInviteAsync(RequestOptions options = null) | |||
=> GuildHelper.GetVanityInviteAsync(this, Discord, options); | |||
//Roles | |||
/// <summary> | |||
@@ -971,6 +980,9 @@ namespace Discord.WebSocket | |||
/// <inheritdoc /> | |||
async Task<IReadOnlyCollection<IInviteMetadata>> IGuild.GetInvitesAsync(RequestOptions options) | |||
=> await GetInvitesAsync(options).ConfigureAwait(false); | |||
/// <inheritdoc /> | |||
async Task<IInviteMetadata> IGuild.GetVanityInviteAsync(RequestOptions options) | |||
=> await GetVanityInviteAsync(options).ConfigureAwait(false); | |||
/// <inheritdoc /> | |||
IRole IGuild.GetRole(ulong id) | |||