@@ -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; } | |||||
} | } | ||||
} | } |
@@ -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; } | |||||
/*[JsonProperty("nonce")] | |||||
public object Nonce { get; set; }*/ | |||||
public Optional<Embed[]> Embeds { get; set; } | |||||
} | } | ||||
} | } |
@@ -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) | ||||
{ | { | ||||
@@ -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) | ||||
{ | { | ||||
@@ -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) | ||||
{ | { | ||||
@@ -18,8 +18,10 @@ namespace Discord | |||||
Title = model.Title; | Title = model.Title; | ||||
Description = model.Description; | 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); | |||||
} | } | ||||
} | } | ||||
} | } |
@@ -10,7 +10,9 @@ 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; | |||||
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<Attachment>(); | |||||
} | } | ||||
else | |||||
Attachments = ImmutableArray.Create<Attachment>(); | |||||
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<Embed>(); | |||||
} | } | ||||
else | |||||
Embeds = ImmutableArray.Create<Embed>(); | |||||
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<User>(); | |||||
} | } | ||||
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 | |||||
if (model.Content.IsSpecified) | |||||
{ | { | ||||
MentionedChannelIds = ImmutableArray.Create<ulong>(); | |||||
MentionedRoleIds = ImmutableArray.Create<ulong>(); | |||||
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() | public async Task UpdateAsync() | ||||
@@ -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; | |||||
} | } | ||||
} | } | ||||
@@ -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); | |||||
} | } | ||||
} | } | ||||
} | } |
@@ -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 => | ||||
{ | { | ||||
@@ -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) | ||||