diff --git a/src/Discord.Net.Rpc/API/Rpc/MessageEvent.cs b/src/Discord.Net.Rpc/API/Rpc/MessageEvent.cs index 41ff13288..75b35ef56 100644 --- a/src/Discord.Net.Rpc/API/Rpc/MessageEvent.cs +++ b/src/Discord.Net.Rpc/API/Rpc/MessageEvent.cs @@ -7,6 +7,6 @@ namespace Discord.API.Rpc [JsonProperty("channel_id")] public ulong ChannelId { get; set; } [JsonProperty("message")] - public Message Message { get; set; } + public RpcMessage Message { get; set; } } } diff --git a/src/Discord.Net.Rpc/API/Rpc/RpcMessage.cs b/src/Discord.Net.Rpc/API/Rpc/RpcMessage.cs new file mode 100644 index 000000000..47c71bab0 --- /dev/null +++ b/src/Discord.Net.Rpc/API/Rpc/RpcMessage.cs @@ -0,0 +1,17 @@ +using Newtonsoft.Json; + +namespace Discord.API.Rpc +{ + public class RpcMessage : Message + { + [JsonProperty("blocked")] + public Optional IsBlocked { get; } + [JsonProperty("content_parsed")] + public Optional ContentParsed { get; } + [JsonProperty("author_color")] + public Optional AuthorColor { get; } //#Hex + + [JsonProperty("mentions")] + public new Optional UserMentions { get; set; } + } +} diff --git a/src/Discord.Net.Rpc/Entities/Messages/RpcMessage.cs b/src/Discord.Net.Rpc/Entities/Messages/RpcMessage.cs index f98067051..d9aa2b3f0 100644 --- a/src/Discord.Net.Rpc/Entities/Messages/RpcMessage.cs +++ b/src/Discord.Net.Rpc/Entities/Messages/RpcMessage.cs @@ -1,10 +1,65 @@ -namespace Discord.Rpc +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using Model = Discord.API.Rpc.RpcMessage; + +namespace Discord.Rpc { - /*internal class RpcMessage : RpcEntity, IMessage + public abstract class RpcMessage : RpcEntity, IMessage { - internal RpcMessage(DiscordRpcClient discord, API.Message model) - : base(dicsord, model.Id) + private long _timestampTicks; + + public IMessageChannel Channel { get; } + public RpcUser Author { get; } + + public string Content { get; private set; } + public Color AuthorColor { get; private set; } + + public virtual bool IsTTS => false; + public virtual bool IsPinned => false; + public virtual bool IsBlocked => false; + public virtual DateTimeOffset? EditedTimestamp => null; + public virtual IReadOnlyCollection Attachments => ImmutableArray.Create(); + public virtual IReadOnlyCollection Embeds => ImmutableArray.Create(); + public virtual IReadOnlyCollection MentionedChannelIds => ImmutableArray.Create(); + public virtual IReadOnlyCollection MentionedRoleIds => ImmutableArray.Create(); + public virtual IReadOnlyCollection MentionedUserIds => ImmutableArray.Create(); + public virtual IReadOnlyCollection Tags => ImmutableArray.Create(); + public virtual ulong? WebhookId => null; + public bool IsWebhook => WebhookId != null; + + public DateTimeOffset Timestamp => DateTimeUtils.FromTicks(_timestampTicks); + + internal RpcMessage(DiscordRpcClient discord, ulong id, IMessageChannel channel, RpcUser author) + : base(discord, id) { + Channel = channel; + Author = author; } - }*/ + internal static RpcMessage Create(DiscordRpcClient discord, ulong channelId, Model model) + { + //model.ChannelId is always 0, needs to be passed from the event + if (model.Type == MessageType.Default) + return RpcUserMessage.Create(discord, channelId, model); + else + return RpcSystemMessage.Create(discord, channelId, model); + } + internal virtual void Update(Model model) + { + if (model.Timestamp.IsSpecified) + _timestampTicks = model.Timestamp.Value.UtcTicks; + + if (model.Content.IsSpecified) + Content = model.Content.Value; + if (model.AuthorColor.IsSpecified) + AuthorColor = new Color(Convert.ToUInt32(model.AuthorColor.Value.Substring(1), 16)); + } + + public override string ToString() => Content; + + MessageType IMessage.Type => MessageType.Default; + IUser IMessage.Author => Author; + IReadOnlyCollection IMessage.Attachments => Attachments; + IReadOnlyCollection IMessage.Embeds => Embeds; + } } diff --git a/src/Discord.Net.Rpc/Entities/Messages/RpcSystemMessage.cs b/src/Discord.Net.Rpc/Entities/Messages/RpcSystemMessage.cs new file mode 100644 index 000000000..7cf222dc7 --- /dev/null +++ b/src/Discord.Net.Rpc/Entities/Messages/RpcSystemMessage.cs @@ -0,0 +1,33 @@ +using Discord.Rest; +using System.Diagnostics; +using Model = Discord.API.Rpc.RpcMessage; + +namespace Discord.Rpc +{ + [DebuggerDisplay(@"{DebuggerDisplay,nq}")] + public class RpcSystemMessage : RpcMessage, ISystemMessage + { + public MessageType Type { get; private set; } + + internal RpcSystemMessage(DiscordRpcClient discord, ulong id, IMessageChannel channel, RpcUser author) + : base(discord, id, channel, author) + { + } + internal new static RpcSystemMessage Create(DiscordRpcClient discord, ulong channelId, Model model) + { + var entity = new RpcSystemMessage(discord, model.Id, + RestVirtualMessageChannel.Create(discord, channelId), + RpcUser.Create(discord, model.Author.Value)); + entity.Update(model); + return entity; + } + internal override void Update(Model model) + { + base.Update(model); + + Type = model.Type; + } + + private string DebuggerDisplay => $"{Author}: {Content} ({Id}, {Type})"; + } +} diff --git a/src/Discord.Net.Rpc/Entities/Messages/RpcUserMessage.cs b/src/Discord.Net.Rpc/Entities/Messages/RpcUserMessage.cs new file mode 100644 index 000000000..f400701f6 --- /dev/null +++ b/src/Discord.Net.Rpc/Entities/Messages/RpcUserMessage.cs @@ -0,0 +1,117 @@ +using Discord.API.Rest; +using Discord.Rest; +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Threading.Tasks; +using Model = Discord.API.Rpc.RpcMessage; + +namespace Discord.Rpc +{ + [DebuggerDisplay(@"{DebuggerDisplay,nq}")] + public class RpcUserMessage : RpcMessage, IUserMessage + { + private bool _isMentioningEveryone, _isTTS, _isPinned, _isBlocked; + private long? _editedTimestampTicks; + private ulong? _webhookId; + private ImmutableArray _attachments; + private ImmutableArray _embeds; + private ImmutableArray _tags; + + public override bool IsTTS => _isTTS; + public override bool IsPinned => _isPinned; + public override bool IsBlocked => _isBlocked; + public override ulong? WebhookId => _webhookId; + public override DateTimeOffset? EditedTimestamp => DateTimeUtils.FromTicks(_editedTimestampTicks); + public override IReadOnlyCollection Attachments => _attachments; + public override IReadOnlyCollection Embeds => _embeds; + public override IReadOnlyCollection MentionedChannelIds => MessageHelper.FilterTagsByKey(TagType.ChannelMention, _tags); + public override IReadOnlyCollection MentionedRoleIds => MessageHelper.FilterTagsByKey(TagType.RoleMention, _tags); + public override IReadOnlyCollection MentionedUserIds => MessageHelper.FilterTagsByKey(TagType.UserMention, _tags); + public override IReadOnlyCollection Tags => _tags; + + internal RpcUserMessage(DiscordRpcClient discord, ulong id, IMessageChannel channel, RpcUser author) + : base(discord, id, channel, author) + { + } + internal new static RpcUserMessage Create(DiscordRpcClient discord, ulong channelId, Model model) + { + var entity = new RpcUserMessage(discord, model.Id, + RestVirtualMessageChannel.Create(discord, channelId), + RpcUser.Create(discord, model.Author.Value)); + entity.Update(model); + return entity; + } + + internal override void Update(Model model) + { + base.Update(model); + + if (model.IsTextToSpeech.IsSpecified) + _isTTS = model.IsTextToSpeech.Value; + if (model.Pinned.IsSpecified) + _isPinned = model.Pinned.Value; + if (model.EditedTimestamp.IsSpecified) + _editedTimestampTicks = model.EditedTimestamp.Value?.UtcTicks; + if (model.MentionEveryone.IsSpecified) + _isMentioningEveryone = model.MentionEveryone.Value; + if (model.WebhookId.IsSpecified) + _webhookId = model.WebhookId.Value; + + if (model.IsBlocked.IsSpecified) + _isBlocked = model.IsBlocked.Value; + + if (model.Attachments.IsSpecified) + { + var value = model.Attachments.Value; + if (value.Length > 0) + { + var attachments = ImmutableArray.CreateBuilder(value.Length); + for (int i = 0; i < value.Length; i++) + attachments.Add(Attachment.Create(value[i])); + _attachments = attachments.ToImmutable(); + } + else + _attachments = ImmutableArray.Create(); + } + + if (model.Embeds.IsSpecified) + { + var value = model.Embeds.Value; + if (value.Length > 0) + { + var embeds = ImmutableArray.CreateBuilder(value.Length); + for (int i = 0; i < value.Length; i++) + embeds.Add(Embed.Create(value[i])); + _embeds = embeds.ToImmutable(); + } + else + _embeds = ImmutableArray.Create(); + } + + if (model.Content.IsSpecified) + { + var text = model.Content.Value; + _tags = MessageHelper.ParseTags(text, null, null, ImmutableArray.Create()); + model.Content = text; + } + } + + public Task ModifyAsync(Action func, RequestOptions options) + => MessageHelper.ModifyAsync(this, Discord, func, options); + public Task DeleteAsync(RequestOptions options) + => MessageHelper.DeleteAsync(this, Discord, options); + + public Task PinAsync(RequestOptions options) + => MessageHelper.PinAsync(this, Discord, options); + public Task UnpinAsync(RequestOptions options) + => MessageHelper.UnpinAsync(this, Discord, options); + + public string Resolve(TagHandling userHandling = TagHandling.Name, TagHandling channelHandling = TagHandling.Name, + TagHandling roleHandling = TagHandling.Name, TagHandling everyoneHandling = TagHandling.Ignore, TagHandling emojiHandling = TagHandling.Name) + => MentionUtils.Resolve(this, userHandling, channelHandling, roleHandling, everyoneHandling, emojiHandling); + + private string DebuggerDisplay => $"{Author}: {Content} ({Id}{(Attachments.Count > 0 ? $", {Attachments.Count} Attachments" : "")})"; + } +} diff --git a/src/Discord.Net.Rpc/Entities/Users/RpcUser.cs b/src/Discord.Net.Rpc/Entities/Users/RpcUser.cs new file mode 100644 index 000000000..33db9bba1 --- /dev/null +++ b/src/Discord.Net.Rpc/Entities/Users/RpcUser.cs @@ -0,0 +1,62 @@ +using Discord.Rest; +using System.Diagnostics; +using System.Threading.Tasks; +using Model = Discord.API.User; + +namespace Discord.Rpc +{ + [DebuggerDisplay(@"{DebuggerDisplay,nq}")] + public class RpcUser : RpcEntity, IUser, IUpdateable + { + public bool IsBot { get; private set; } + public string Username { get; private set; } + public ushort DiscriminatorValue { get; private set; } + public string AvatarId { get; private set; } + + public string AvatarUrl => API.CDN.GetUserAvatarUrl(Id, AvatarId); + public string Discriminator => DiscriminatorValue.ToString("D4"); + public string Mention => MentionUtils.MentionUser(Id); + public virtual Game? Game => null; + public virtual UserStatus Status => UserStatus.Unknown; + + internal RpcUser(DiscordRpcClient discord, ulong id) + : base(discord, id) + { + } + internal static RpcUser Create(DiscordRpcClient discord, Model model) + { + var entity = new RpcUser(discord, model.Id); + entity.Update(model); + return entity; + } + internal virtual void Update(Model model) + { + if (model.Avatar.IsSpecified) + AvatarId = model.Avatar.Value; + if (model.Discriminator.IsSpecified) + DiscriminatorValue = ushort.Parse(model.Discriminator.Value); + if (model.Bot.IsSpecified) + IsBot = model.Bot.Value; + if (model.Username.IsSpecified) + Username = model.Username.Value; + } + + public virtual async Task UpdateAsync(RequestOptions options = null) + { + var model = await Discord.ApiClient.GetUserAsync(Id, options); + Update(model); + } + + public Task CreateDMChannelAsync(RequestOptions options = null) + => UserHelper.CreateDMChannelAsync(this, Discord, options); + + public override string ToString() => $"{Username}#{Discriminator}"; + internal string DebuggerDisplay => $"{Username}#{Discriminator} ({Id}{(IsBot ? ", Bot" : "")})"; + + //IUser + Task IUser.GetDMChannelAsync(CacheMode mode, RequestOptions options) + => Task.FromResult(null); + async Task IUser.CreateDMChannelAsync(RequestOptions options) + => await CreateDMChannelAsync(options); + } +}