diff --git a/src/Discord.Net/API/Common/Embed.cs b/src/Discord.Net/API/Common/Embed.cs index 5c732a9d1..394b460dd 100644 --- a/src/Discord.Net/API/Common/Embed.cs +++ b/src/Discord.Net/API/Common/Embed.cs @@ -13,8 +13,8 @@ namespace Discord.API [JsonProperty("url")] public string Url { get; set; } [JsonProperty("thumbnail")] - public EmbedThumbnail Thumbnail { get; set; } + public Optional Thumbnail { get; set; } [JsonProperty("provider")] - public EmbedProvider Provider { get; set; } + public Optional Provider { get; set; } } } diff --git a/src/Discord.Net/API/Common/Message.cs b/src/Discord.Net/API/Common/Message.cs index f2ef47be3..52950caf2 100644 --- a/src/Discord.Net/API/Common/Message.cs +++ b/src/Discord.Net/API/Common/Message.cs @@ -10,24 +10,22 @@ namespace Discord.API [JsonProperty("channel_id")] public ulong ChannelId { get; set; } [JsonProperty("author")] - public User Author { get; set; } + public Optional Author { get; set; } [JsonProperty("content")] - public string Content { get; set; } + public Optional Content { get; set; } [JsonProperty("timestamp")] - public DateTime Timestamp { get; set; } + public Optional Timestamp { get; set; } [JsonProperty("edited_timestamp")] - public DateTime? EditedTimestamp { get; set; } + public Optional EditedTimestamp { get; set; } [JsonProperty("tts")] - public bool IsTextToSpeech { get; set; } + public Optional IsTextToSpeech { get; set; } [JsonProperty("mention_everyone")] - public bool IsMentioningEveryone { get; set; } + public Optional IsMentioningEveryone { get; set; } [JsonProperty("mentions")] - public User[] Mentions { get; set; } + public Optional Mentions { get; set; } [JsonProperty("attachments")] - public Attachment[] Attachments { get; set; } + public Optional Attachments { get; set; } [JsonProperty("embeds")] - public Embed[] Embeds { get; set; } - /*[JsonProperty("nonce")] - public object Nonce { get; set; }*/ + public Optional Embeds { get; set; } } } diff --git a/src/Discord.Net/DiscordSocketClient.cs b/src/Discord.Net/DiscordSocketClient.cs index cc0630922..9e2e33146 100644 --- a/src/Discord.Net/DiscordSocketClient.cs +++ b/src/Discord.Net/DiscordSocketClient.cs @@ -729,7 +729,7 @@ namespace Discord var channel = DataStore.GetChannel(data.ChannelId) as ICachedMessageChannel; if (channel != null) { - var author = channel.GetUser(data.Author.Id); + var author = channel.GetUser(data.Author.Value.Id); if (author != null) { diff --git a/src/Discord.Net/Entities/Channels/DMChannel.cs b/src/Discord.Net/Entities/Channels/DMChannel.cs index b1df139f0..ca9d1c1f3 100644 --- a/src/Discord.Net/Entities/Channels/DMChannel.cs +++ b/src/Discord.Net/Entities/Channels/DMChannel.cs @@ -70,7 +70,7 @@ namespace Discord { var args = new CreateMessageParams { Content = text, IsTTS = isTTS }; var model = await Discord.ApiClient.CreateDMMessageAsync(Id, args).ConfigureAwait(false); - return new Message(this, new User(Discord, model.Author), model); + return new Message(this, new User(Discord, model.Author.Value), model); } public async Task SendFileAsync(string filePath, string text, bool isTTS) { @@ -79,33 +79,33 @@ namespace Discord { var args = new UploadFileParams { Filename = filename, Content = text, IsTTS = isTTS }; var model = await Discord.ApiClient.UploadDMFileAsync(Id, file, args).ConfigureAwait(false); - return new Message(this, new User(Discord, model.Author), model); + return new Message(this, new User(Discord, model.Author.Value), model); } } public async Task SendFileAsync(Stream stream, string filename, string text, bool isTTS) { var args = new UploadFileParams { Filename = filename, Content = text, IsTTS = isTTS }; var model = await Discord.ApiClient.UploadDMFileAsync(Id, stream, args).ConfigureAwait(false); - return new Message(this, new User(Discord, model.Author), model); + return new Message(this, new User(Discord, model.Author.Value), model); } public virtual async Task GetMessageAsync(ulong id) { var model = await Discord.ApiClient.GetChannelMessageAsync(Id, id).ConfigureAwait(false); if (model != null) - return new Message(this, new User(Discord, model.Author), model); + return new Message(this, new User(Discord, model.Author.Value), 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 => new Message(this, new User(Discord, x.Author), x)).ToImmutableArray(); + return models.Select(x => new Message(this, new User(Discord, x.Author.Value), x)).ToImmutableArray(); } public virtual async Task> GetMessagesAsync(ulong fromMessageId, Direction dir, int limit) { var args = new GetChannelMessagesParams { Limit = limit }; var models = await Discord.ApiClient.GetChannelMessagesAsync(Id, args).ConfigureAwait(false); - return models.Select(x => new Message(this, new User(Discord, x.Author), x)).ToImmutableArray(); + return models.Select(x => new Message(this, new User(Discord, x.Author.Value), x)).ToImmutableArray(); } public async Task DeleteMessagesAsync(IEnumerable messages) { diff --git a/src/Discord.Net/Entities/Channels/TextChannel.cs b/src/Discord.Net/Entities/Channels/TextChannel.cs index 9c55d77bd..78ec865fe 100644 --- a/src/Discord.Net/Entities/Channels/TextChannel.cs +++ b/src/Discord.Net/Entities/Channels/TextChannel.cs @@ -62,7 +62,7 @@ namespace Discord { var args = new CreateMessageParams { Content = text, IsTTS = isTTS }; var model = await Discord.ApiClient.CreateMessageAsync(Guild.Id, Id, args).ConfigureAwait(false); - return new Message(this, new User(Discord, model.Author), model); + return new Message(this, new User(Discord, model.Author.Value), model); } public async Task SendFileAsync(string filePath, string text, bool isTTS) { @@ -71,33 +71,33 @@ namespace Discord { var args = new UploadFileParams { Filename = filename, Content = text, IsTTS = isTTS }; var model = await Discord.ApiClient.UploadFileAsync(Guild.Id, Id, file, args).ConfigureAwait(false); - return new Message(this, new User(Discord, model.Author), model); + return new Message(this, new User(Discord, model.Author.Value), model); } } public async Task SendFileAsync(Stream stream, string filename, string text, bool isTTS) { var args = new UploadFileParams { Filename = filename, Content = text, IsTTS = isTTS }; var model = await Discord.ApiClient.UploadFileAsync(Guild.Id, Id, stream, args).ConfigureAwait(false); - return new Message(this, new User(Discord, model.Author), model); + return new Message(this, new User(Discord, model.Author.Value), model); } public virtual async Task GetMessageAsync(ulong id) { var model = await Discord.ApiClient.GetChannelMessageAsync(Id, id).ConfigureAwait(false); if (model != null) - return new Message(this, new User(Discord, model.Author), model); + return new Message(this, new User(Discord, model.Author.Value), 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 => new Message(this, new User(Discord, x.Author), x)).ToImmutableArray(); + return models.Select(x => new Message(this, new User(Discord, x.Author.Value), x)).ToImmutableArray(); } public virtual async Task> GetMessagesAsync(ulong fromMessageId, Direction dir, int limit) { var args = new GetChannelMessagesParams { Limit = limit }; var models = await Discord.ApiClient.GetChannelMessagesAsync(Id, args).ConfigureAwait(false); - return models.Select(x => new Message(this, new User(Discord, x.Author), x)).ToImmutableArray(); + return models.Select(x => new Message(this, new User(Discord, x.Author.Value), x)).ToImmutableArray(); } public async Task DeleteMessagesAsync(IEnumerable messages) { diff --git a/src/Discord.Net/Entities/Messages/Embed.cs b/src/Discord.Net/Entities/Messages/Embed.cs index 4e3d065a1..b27caeac2 100644 --- a/src/Discord.Net/Entities/Messages/Embed.cs +++ b/src/Discord.Net/Entities/Messages/Embed.cs @@ -18,8 +18,10 @@ namespace Discord Title = model.Title; Description = model.Description; - Provider = new EmbedProvider(model.Provider); - Thumbnail = new EmbedThumbnail(model.Thumbnail); + 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/Entities/Messages/Message.cs b/src/Discord.Net/Entities/Messages/Message.cs index e72f89a57..c9f66bfe0 100644 --- a/src/Discord.Net/Entities/Messages/Message.cs +++ b/src/Discord.Net/Entities/Messages/Message.cs @@ -10,7 +10,9 @@ namespace Discord { [DebuggerDisplay(@"{DebuggerDisplay,nq}")] internal class Message : SnowflakeEntity, IMessage - { + { + private bool _isMentioningEveryone; + public DateTime? EditedTimestamp { get; private set; } public bool IsTTS { get; private set; } public string RawText { get; private set; } @@ -34,6 +36,13 @@ namespace Discord Channel = channel; Author = author; + if (channel is IGuildChannel) + { + MentionedUsers = ImmutableArray.Create(); + MentionedChannelIds = ImmutableArray.Create(); + MentionedRoleIds = ImmutableArray.Create(); + } + Update(model, UpdateSource.Creation); } public void Update(Model model, UpdateSource source) @@ -44,57 +53,73 @@ namespace Discord var guild = guildChannel?.Guild; var discord = Discord; - IsTTS = model.IsTextToSpeech; - Timestamp = model.Timestamp; - EditedTimestamp = model.EditedTimestamp; - RawText = model.Content; - - if (model.Attachments.Length > 0) + if (model.IsTextToSpeech.IsSpecified) + IsTTS = model.IsTextToSpeech.Value; + if (model.Timestamp.IsSpecified) + Timestamp = model.Timestamp.Value; + if (model.EditedTimestamp.IsSpecified) + EditedTimestamp = model.EditedTimestamp.Value; + if (model.IsMentioningEveryone.IsSpecified) + _isMentioningEveryone = model.IsMentioningEveryone.Value; + + if (model.Attachments.IsSpecified) { - var attachments = new Attachment[model.Attachments.Length]; - for (int i = 0; i < attachments.Length; i++) - attachments[i] = new Attachment(model.Attachments[i]); - Attachments = ImmutableArray.Create(attachments); + 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(); } - else - Attachments = ImmutableArray.Create(); - if (model.Embeds.Length > 0) + if (model.Embeds.IsSpecified) { - var embeds = new Embed[model.Attachments.Length]; - for (int i = 0; i < embeds.Length; i++) - embeds[i] = new Embed(model.Embeds[i]); - Embeds = ImmutableArray.Create(embeds); + 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(); } - else - Embeds = ImmutableArray.Create(); - if (guildChannel != null && model.Mentions.Length > 0) + if (model.Mentions.IsSpecified) { - var mentions = new User[model.Mentions.Length]; - for (int i = 0; i < model.Mentions.Length; i++) - mentions[i] = new User(discord, model.Mentions[i]); - MentionedUsers = ImmutableArray.Create(mentions); + var value = model.Mentions.Value; + if (value.Length > 0) + { + var mentions = new User[value.Length]; + for (int i = 0; i < value.Length; i++) + mentions[i] = new User(discord, value[i]); + MentionedUsers = ImmutableArray.Create(mentions); + } + else + MentionedUsers = ImmutableArray.Create(); } - else - MentionedUsers = ImmutableArray.Create(); - - if (guildChannel != null) - { - MentionedChannelIds = MentionUtils.GetChannelMentions(model.Content); - var mentionedRoleIds = MentionUtils.GetRoleMentions(model.Content); - if (model.IsMentioningEveryone) - mentionedRoleIds = mentionedRoleIds.Add(guildChannel.Guild.EveryoneRole.Id); - MentionedRoleIds = mentionedRoleIds; - } - else + if (model.Content.IsSpecified) { - MentionedChannelIds = ImmutableArray.Create(); - MentionedRoleIds = ImmutableArray.Create(); + RawText = model.Content.Value; + + if (Channel is IGuildChannel) + { + Text = MentionUtils.CleanUserMentions(RawText, MentionedUsers); + MentionedChannelIds = MentionUtils.GetChannelMentions(RawText); + var mentionedRoleIds = MentionUtils.GetRoleMentions(RawText); + if (_isMentioningEveryone) + mentionedRoleIds = mentionedRoleIds.Add(guildChannel.Guild.EveryoneRole.Id); + MentionedRoleIds = mentionedRoleIds; + } + else + Text = RawText; } - - Text = MentionUtils.CleanUserMentions(model.Content, model.Mentions); } public async Task UpdateAsync() diff --git a/src/Discord.Net/Net/Converters/DiscordContractResolver.cs b/src/Discord.Net/Net/Converters/DiscordContractResolver.cs index 0149d130e..e240c2017 100644 --- a/src/Discord.Net/Net/Converters/DiscordContractResolver.cs +++ b/src/Discord.Net/Net/Converters/DiscordContractResolver.cs @@ -55,6 +55,7 @@ namespace Discord.Net.Converters converter = ImageConverter.Instance; else if (type.IsConstructedGenericType && type.GetGenericTypeDefinition() == typeof(Optional<>)) { + var innerType = type.GenericTypeArguments[0]; var typeInput = propInfo.DeclaringType; var typeOutput = propInfo.PropertyType; @@ -62,9 +63,10 @@ namespace Discord.Net.Converters var getterDelegate = propInfo.GetMethod.CreateDelegate(getter); var shouldSerialize = _shouldSerialize.MakeGenericMethod(typeInput, typeOutput); var shouldSerializeDelegate = (Func)shouldSerialize.CreateDelegate(typeof(Func)); - property.ShouldSerialize = x => shouldSerializeDelegate(x, getterDelegate); - converter = OptionalConverter.Instance; + + var converterType = typeof(OptionalConverter<>).MakeGenericType(innerType); + converter = converterType.GetTypeInfo().GetDeclaredField("Instance").GetValue(null) as JsonConverter; } } diff --git a/src/Discord.Net/Net/Converters/OptionalConverter.cs b/src/Discord.Net/Net/Converters/OptionalConverter.cs index acc583c5f..a1e7c7543 100644 --- a/src/Discord.Net/Net/Converters/OptionalConverter.cs +++ b/src/Discord.Net/Net/Converters/OptionalConverter.cs @@ -3,22 +3,22 @@ using System; namespace Discord.Net.Converters { - public class OptionalConverter : JsonConverter + public class OptionalConverter : JsonConverter { - public static readonly OptionalConverter Instance = new OptionalConverter(); + public static readonly OptionalConverter Instance = new OptionalConverter(); public override bool CanConvert(Type objectType) => true; - public override bool CanRead => false; + public override bool CanRead => true; public override bool CanWrite => true; public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { - throw new InvalidOperationException(); + return new Optional(serializer.Deserialize(reader)); } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { - serializer.Serialize(writer, (value as IOptional).Value); + serializer.Serialize(writer, ((Optional)value).Value); } } } diff --git a/src/Discord.Net/Utilities/MentionUtils.cs b/src/Discord.Net/Utilities/MentionUtils.cs index 636711552..0053792c2 100644 --- a/src/Discord.Net/Utilities/MentionUtils.cs +++ b/src/Discord.Net/Utilities/MentionUtils.cs @@ -82,7 +82,7 @@ namespace Discord return builder; } - internal static string CleanUserMentions(string text, API.User[] mentions) + internal static string CleanUserMentions(string text, ImmutableArray mentions) { return _userRegex.Replace(text, new MatchEvaluator(e => { diff --git a/src/Discord.Net/Utilities/MessageCache.cs b/src/Discord.Net/Utilities/MessageCache.cs index 97bc6e813..c0ddf5afd 100644 --- a/src/Discord.Net/Utilities/MessageCache.cs +++ b/src/Discord.Net/Utilities/MessageCache.cs @@ -88,7 +88,7 @@ namespace Discord return msg; var model = await _discord.ApiClient.GetChannelMessageAsync(_channel.Id, id).ConfigureAwait(false); if (model != null) - return new CachedMessage(_channel, new User(_discord, model.Author), model); + return new CachedMessage(_channel, new User(_discord, model.Author.Value), model); return null; } public async Task> DownloadAsync(ulong? fromId, Direction dir, int limit)