Fixed several message parsing issues, added optional deserialization

This commit is contained in:
RogueException
2016-06-11 19:00:33 -03:00
parent 06fd96a72c
commit cc5e0bbe13
11 changed files with 115 additions and 88 deletions

View File

@@ -13,8 +13,8 @@ namespace Discord.API
[JsonProperty("url")] [JsonProperty("url")]
public string Url { get; set; } public string Url { get; set; }
[JsonProperty("thumbnail")] [JsonProperty("thumbnail")]
public EmbedThumbnail Thumbnail { get; set; } public Optional<EmbedThumbnail> Thumbnail { get; set; }
[JsonProperty("provider")] [JsonProperty("provider")]
public EmbedProvider Provider { get; set; } public Optional<EmbedProvider> Provider { get; set; }
} }
} }

View File

@@ -10,24 +10,22 @@ namespace Discord.API
[JsonProperty("channel_id")] [JsonProperty("channel_id")]
public ulong ChannelId { get; set; } public ulong ChannelId { get; set; }
[JsonProperty("author")] [JsonProperty("author")]
public User Author { get; set; } public Optional<User> Author { get; set; }
[JsonProperty("content")] [JsonProperty("content")]
public string Content { get; set; } public Optional<string> Content { get; set; }
[JsonProperty("timestamp")] [JsonProperty("timestamp")]
public DateTime Timestamp { get; set; } public Optional<DateTime> Timestamp { get; set; }
[JsonProperty("edited_timestamp")] [JsonProperty("edited_timestamp")]
public DateTime? EditedTimestamp { get; set; } public Optional<DateTime?> EditedTimestamp { get; set; }
[JsonProperty("tts")] [JsonProperty("tts")]
public bool IsTextToSpeech { get; set; } public Optional<bool> IsTextToSpeech { get; set; }
[JsonProperty("mention_everyone")] [JsonProperty("mention_everyone")]
public bool IsMentioningEveryone { get; set; } public Optional<bool> IsMentioningEveryone { get; set; }
[JsonProperty("mentions")] [JsonProperty("mentions")]
public User[] Mentions { get; set; } public Optional<User[]> Mentions { get; set; }
[JsonProperty("attachments")] [JsonProperty("attachments")]
public Attachment[] Attachments { get; set; } public Optional<Attachment[]> Attachments { get; set; }
[JsonProperty("embeds")] [JsonProperty("embeds")]
public Embed[] Embeds { get; set; } public Optional<Embed[]> Embeds { get; set; }
/*[JsonProperty("nonce")]
public object Nonce { get; set; }*/
} }
} }

View File

@@ -729,7 +729,7 @@ namespace Discord
var channel = DataStore.GetChannel(data.ChannelId) as ICachedMessageChannel; var channel = DataStore.GetChannel(data.ChannelId) as ICachedMessageChannel;
if (channel != null) if (channel != null)
{ {
var author = channel.GetUser(data.Author.Id); var author = channel.GetUser(data.Author.Value.Id);
if (author != null) if (author != null)
{ {

View File

@@ -70,7 +70,7 @@ namespace Discord
{ {
var args = new CreateMessageParams { Content = text, IsTTS = isTTS }; var args = new CreateMessageParams { Content = text, IsTTS = isTTS };
var model = await Discord.ApiClient.CreateDMMessageAsync(Id, args).ConfigureAwait(false); 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<IMessage> SendFileAsync(string filePath, string text, bool isTTS) public async Task<IMessage> SendFileAsync(string filePath, string text, bool isTTS)
{ {
@@ -79,33 +79,33 @@ namespace Discord
{ {
var args = new UploadFileParams { Filename = filename, Content = text, IsTTS = isTTS }; var args = new UploadFileParams { Filename = filename, Content = text, IsTTS = isTTS };
var model = await Discord.ApiClient.UploadDMFileAsync(Id, file, args).ConfigureAwait(false); 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<IMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS) public async Task<IMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS)
{ {
var args = new UploadFileParams { Filename = filename, Content = text, IsTTS = isTTS }; var args = new UploadFileParams { Filename = filename, Content = text, IsTTS = isTTS };
var model = await Discord.ApiClient.UploadDMFileAsync(Id, stream, args).ConfigureAwait(false); 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<IMessage> GetMessageAsync(ulong id) public virtual async Task<IMessage> GetMessageAsync(ulong id)
{ {
var model = await Discord.ApiClient.GetChannelMessageAsync(Id, id).ConfigureAwait(false); var model = await Discord.ApiClient.GetChannelMessageAsync(Id, id).ConfigureAwait(false);
if (model != null) 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; return null;
} }
public virtual async Task<IReadOnlyCollection<IMessage>> GetMessagesAsync(int limit) public virtual async Task<IReadOnlyCollection<IMessage>> GetMessagesAsync(int limit)
{ {
var args = new GetChannelMessagesParams { Limit = limit }; var args = new GetChannelMessagesParams { Limit = limit };
var models = await Discord.ApiClient.GetChannelMessagesAsync(Id, args).ConfigureAwait(false); 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<IReadOnlyCollection<IMessage>> GetMessagesAsync(ulong fromMessageId, Direction dir, int limit) public virtual async Task<IReadOnlyCollection<IMessage>> GetMessagesAsync(ulong fromMessageId, Direction dir, int limit)
{ {
var args = new GetChannelMessagesParams { Limit = limit }; var args = new GetChannelMessagesParams { Limit = limit };
var models = await Discord.ApiClient.GetChannelMessagesAsync(Id, args).ConfigureAwait(false); 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<IMessage> messages) public async Task DeleteMessagesAsync(IEnumerable<IMessage> messages)
{ {

View File

@@ -62,7 +62,7 @@ namespace Discord
{ {
var args = new CreateMessageParams { Content = text, IsTTS = isTTS }; var args = new CreateMessageParams { Content = text, IsTTS = isTTS };
var model = await Discord.ApiClient.CreateMessageAsync(Guild.Id, Id, args).ConfigureAwait(false); 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<IMessage> SendFileAsync(string filePath, string text, bool isTTS) public async Task<IMessage> SendFileAsync(string filePath, string text, bool isTTS)
{ {
@@ -71,33 +71,33 @@ namespace Discord
{ {
var args = new UploadFileParams { Filename = filename, Content = text, IsTTS = isTTS }; var args = new UploadFileParams { Filename = filename, Content = text, IsTTS = isTTS };
var model = await Discord.ApiClient.UploadFileAsync(Guild.Id, Id, file, args).ConfigureAwait(false); 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<IMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS) public async Task<IMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS)
{ {
var args = new UploadFileParams { Filename = filename, Content = text, IsTTS = isTTS }; var args = new UploadFileParams { Filename = filename, Content = text, IsTTS = isTTS };
var model = await Discord.ApiClient.UploadFileAsync(Guild.Id, Id, stream, args).ConfigureAwait(false); 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<IMessage> GetMessageAsync(ulong id) public virtual async Task<IMessage> GetMessageAsync(ulong id)
{ {
var model = await Discord.ApiClient.GetChannelMessageAsync(Id, id).ConfigureAwait(false); var model = await Discord.ApiClient.GetChannelMessageAsync(Id, id).ConfigureAwait(false);
if (model != null) 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; return null;
} }
public virtual async Task<IReadOnlyCollection<IMessage>> GetMessagesAsync(int limit) public virtual async Task<IReadOnlyCollection<IMessage>> GetMessagesAsync(int limit)
{ {
var args = new GetChannelMessagesParams { Limit = limit }; var args = new GetChannelMessagesParams { Limit = limit };
var models = await Discord.ApiClient.GetChannelMessagesAsync(Id, args).ConfigureAwait(false); 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<IReadOnlyCollection<IMessage>> GetMessagesAsync(ulong fromMessageId, Direction dir, int limit) public virtual async Task<IReadOnlyCollection<IMessage>> GetMessagesAsync(ulong fromMessageId, Direction dir, int limit)
{ {
var args = new GetChannelMessagesParams { Limit = limit }; var args = new GetChannelMessagesParams { Limit = limit };
var models = await Discord.ApiClient.GetChannelMessagesAsync(Id, args).ConfigureAwait(false); 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<IMessage> messages) public async Task DeleteMessagesAsync(IEnumerable<IMessage> messages)
{ {

View File

@@ -18,8 +18,10 @@ namespace Discord
Title = model.Title; Title = model.Title;
Description = model.Description; Description = model.Description;
Provider = new EmbedProvider(model.Provider); if (model.Provider.IsSpecified)
Thumbnail = new EmbedThumbnail(model.Thumbnail); Provider = new EmbedProvider(model.Provider.Value);
if (model.Thumbnail.IsSpecified)
Thumbnail = new EmbedThumbnail(model.Thumbnail.Value);
} }
} }
} }

View File

@@ -11,6 +11,8 @@ namespace Discord
[DebuggerDisplay(@"{DebuggerDisplay,nq}")] [DebuggerDisplay(@"{DebuggerDisplay,nq}")]
internal class Message : SnowflakeEntity, IMessage internal class Message : SnowflakeEntity, IMessage
{ {
private bool _isMentioningEveryone;
public DateTime? EditedTimestamp { get; private set; } public DateTime? EditedTimestamp { get; private set; }
public bool IsTTS { get; private set; } public bool IsTTS { get; private set; }
public string RawText { get; private set; } public string RawText { get; private set; }
@@ -34,6 +36,13 @@ namespace Discord
Channel = channel; Channel = channel;
Author = author; Author = author;
if (channel is IGuildChannel)
{
MentionedUsers = ImmutableArray.Create<User>();
MentionedChannelIds = ImmutableArray.Create<ulong>();
MentionedRoleIds = ImmutableArray.Create<ulong>();
}
Update(model, UpdateSource.Creation); Update(model, UpdateSource.Creation);
} }
public void Update(Model model, UpdateSource source) public void Update(Model model, UpdateSource source)
@@ -44,57 +53,73 @@ namespace Discord
var guild = guildChannel?.Guild; var guild = guildChannel?.Guild;
var discord = Discord; var discord = Discord;
IsTTS = model.IsTextToSpeech; if (model.IsTextToSpeech.IsSpecified)
Timestamp = model.Timestamp; IsTTS = model.IsTextToSpeech.Value;
EditedTimestamp = model.EditedTimestamp; if (model.Timestamp.IsSpecified)
RawText = model.Content; Timestamp = model.Timestamp.Value;
if (model.EditedTimestamp.IsSpecified)
EditedTimestamp = model.EditedTimestamp.Value;
if (model.IsMentioningEveryone.IsSpecified)
_isMentioningEveryone = model.IsMentioningEveryone.Value;
if (model.Attachments.Length > 0) if (model.Attachments.IsSpecified)
{ {
var attachments = new Attachment[model.Attachments.Length]; var value = model.Attachments.Value;
for (int i = 0; i < attachments.Length; i++) if (value.Length > 0)
attachments[i] = new Attachment(model.Attachments[i]); {
Attachments = ImmutableArray.Create(attachments); var attachments = new Attachment[value.Length];
} for (int i = 0; i < attachments.Length; i++)
else attachments[i] = new Attachment(value[i]);
Attachments = ImmutableArray.Create<Attachment>(); Attachments = ImmutableArray.Create(attachments);
}
if (model.Embeds.Length > 0) else
{ Attachments = ImmutableArray.Create<Attachment>();
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);
}
else
Embeds = ImmutableArray.Create<Embed>();
if (guildChannel != null && model.Mentions.Length > 0)
{
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);
}
else
MentionedUsers = ImmutableArray.Create<User>();
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
{
MentionedChannelIds = ImmutableArray.Create<ulong>();
MentionedRoleIds = ImmutableArray.Create<ulong>();
} }
Text = MentionUtils.CleanUserMentions(model.Content, model.Mentions); 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<Embed>();
}
if (model.Mentions.IsSpecified)
{
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<User>();
}
if (model.Content.IsSpecified)
{
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;
}
} }
public async Task UpdateAsync() public async Task UpdateAsync()

View File

@@ -55,6 +55,7 @@ namespace Discord.Net.Converters
converter = ImageConverter.Instance; converter = ImageConverter.Instance;
else if (type.IsConstructedGenericType && type.GetGenericTypeDefinition() == typeof(Optional<>)) else if (type.IsConstructedGenericType && type.GetGenericTypeDefinition() == typeof(Optional<>))
{ {
var innerType = type.GenericTypeArguments[0];
var typeInput = propInfo.DeclaringType; var typeInput = propInfo.DeclaringType;
var typeOutput = propInfo.PropertyType; var typeOutput = propInfo.PropertyType;
@@ -62,9 +63,10 @@ namespace Discord.Net.Converters
var getterDelegate = propInfo.GetMethod.CreateDelegate(getter); var getterDelegate = propInfo.GetMethod.CreateDelegate(getter);
var shouldSerialize = _shouldSerialize.MakeGenericMethod(typeInput, typeOutput); var shouldSerialize = _shouldSerialize.MakeGenericMethod(typeInput, typeOutput);
var shouldSerializeDelegate = (Func<object, Delegate, bool>)shouldSerialize.CreateDelegate(typeof(Func<object, Delegate, bool>)); var shouldSerializeDelegate = (Func<object, Delegate, bool>)shouldSerialize.CreateDelegate(typeof(Func<object, Delegate, bool>));
property.ShouldSerialize = x => shouldSerializeDelegate(x, getterDelegate); property.ShouldSerialize = x => shouldSerializeDelegate(x, getterDelegate);
converter = OptionalConverter.Instance;
var converterType = typeof(OptionalConverter<>).MakeGenericType(innerType);
converter = converterType.GetTypeInfo().GetDeclaredField("Instance").GetValue(null) as JsonConverter;
} }
} }

View File

@@ -3,22 +3,22 @@ using System;
namespace Discord.Net.Converters namespace Discord.Net.Converters
{ {
public class OptionalConverter : JsonConverter public class OptionalConverter<T> : JsonConverter
{ {
public static readonly OptionalConverter Instance = new OptionalConverter(); public static readonly OptionalConverter<T> Instance = new OptionalConverter<T>();
public override bool CanConvert(Type objectType) => true; public override bool CanConvert(Type objectType) => true;
public override bool CanRead => false; public override bool CanRead => true;
public override bool CanWrite => true; public override bool CanWrite => true;
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{ {
throw new InvalidOperationException(); return new Optional<T>(serializer.Deserialize<T>(reader));
} }
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{ {
serializer.Serialize(writer, (value as IOptional).Value); serializer.Serialize(writer, ((Optional<T>)value).Value);
} }
} }
} }

View File

@@ -82,7 +82,7 @@ namespace Discord
return builder; return builder;
} }
internal static string CleanUserMentions(string text, API.User[] mentions) internal static string CleanUserMentions(string text, ImmutableArray<User> mentions)
{ {
return _userRegex.Replace(text, new MatchEvaluator(e => return _userRegex.Replace(text, new MatchEvaluator(e =>
{ {

View File

@@ -88,7 +88,7 @@ namespace Discord
return msg; return msg;
var model = await _discord.ApiClient.GetChannelMessageAsync(_channel.Id, id).ConfigureAwait(false); var model = await _discord.ApiClient.GetChannelMessageAsync(_channel.Id, id).ConfigureAwait(false);
if (model != null) 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; return null;
} }
public async Task<IReadOnlyCollection<CachedMessage>> DownloadAsync(ulong? fromId, Direction dir, int limit) public async Task<IReadOnlyCollection<CachedMessage>> DownloadAsync(ulong? fromId, Direction dir, int limit)