From 07ac8056775012b7f727aa39d8ab7feb45579e50 Mon Sep 17 00:00:00 2001 From: Sanqui Date: Mon, 28 Dec 2020 21:03:30 +0100 Subject: [PATCH] Add support for replies (#455) --- .../Discord/Models/Message.cs | 19 ++++++++-- .../Discord/Models/MessageReference.cs | 37 +++++++++++++++++++ .../Exporting/Writers/Html/Core.css | 33 +++++++++++++++++ .../Exporting/Writers/Html/Dark.css | 8 ++++ .../Exporting/Writers/Html/Light.css | 8 ++++ .../Exporting/Writers/Html/MessageGroup.cs | 18 ++++++++- .../Writers/Html/MessageGroupTemplate.cshtml | 22 +++++++++++ .../Exporting/Writers/JsonMessageWriter.cs | 11 ++++++ 8 files changed, 151 insertions(+), 5 deletions(-) create mode 100644 DiscordChatExporter.Domain/Discord/Models/MessageReference.cs diff --git a/DiscordChatExporter.Domain/Discord/Models/Message.cs b/DiscordChatExporter.Domain/Discord/Models/Message.cs index cfae917b..dc55ac13 100644 --- a/DiscordChatExporter.Domain/Discord/Models/Message.cs +++ b/DiscordChatExporter.Domain/Discord/Models/Message.cs @@ -18,7 +18,8 @@ namespace DiscordChatExporter.Domain.Discord.Models ChannelNameChange, ChannelIconChange, ChannelPinnedMessage, - GuildMemberJoin + GuildMemberJoin, + Reply = 19 } // https://discord.com/developers/docs/resources/channel#message-object @@ -48,6 +49,10 @@ namespace DiscordChatExporter.Domain.Discord.Models public IReadOnlyList MentionedUsers { get; } + public MessageReference? Reference {get; } + + public Message? ReferencedMessage {get; } + public Message( Snowflake id, MessageType type, @@ -60,7 +65,9 @@ namespace DiscordChatExporter.Domain.Discord.Models IReadOnlyList attachments, IReadOnlyList embeds, IReadOnlyList reactions, - IReadOnlyList mentionedUsers) + IReadOnlyList mentionedUsers, + MessageReference? messageReference, + Message? referencedMessage) { Id = id; Type = type; @@ -74,6 +81,8 @@ namespace DiscordChatExporter.Domain.Discord.Models Embeds = embeds; Reactions = reactions; MentionedUsers = mentionedUsers; + Reference = messageReference; + ReferencedMessage = referencedMessage; } public override string ToString() => Content; @@ -90,6 +99,8 @@ namespace DiscordChatExporter.Domain.Discord.Models var callEndedTimestamp = json.GetPropertyOrNull("call")?.GetPropertyOrNull("ended_timestamp")?.GetDateTimeOffset(); var type = (MessageType) json.GetProperty("type").GetInt32(); var isPinned = json.GetPropertyOrNull("pinned")?.GetBoolean() ?? false; + var messageReference = json.GetPropertyOrNull("message_reference")?.Pipe(MessageReference.Parse); + var referencedMessage = json.GetPropertyOrNull("referenced_message")?.Pipe(Message.Parse); var content = type switch { @@ -132,7 +143,9 @@ namespace DiscordChatExporter.Domain.Discord.Models attachments, embeds, reactions, - mentionedUsers + mentionedUsers, + messageReference, + referencedMessage ); } } diff --git a/DiscordChatExporter.Domain/Discord/Models/MessageReference.cs b/DiscordChatExporter.Domain/Discord/Models/MessageReference.cs new file mode 100644 index 00000000..9004852e --- /dev/null +++ b/DiscordChatExporter.Domain/Discord/Models/MessageReference.cs @@ -0,0 +1,37 @@ +using System.Text.Json; +using JsonExtensions.Reading; + +namespace DiscordChatExporter.Domain.Discord.Models +{ + // reference data sent with crossposted messages and replies + // https://discord.com/developers/docs/resources/channel#message-object-message-reference-structure + public partial class MessageReference + { + public string? MessageId { get; } + + public string? ChannelId { get; } + + public string? GuildId { get; } + + public MessageReference(string? message_id, string? channel_id, string? guild_id) + { + MessageId = message_id; + ChannelId = channel_id; + GuildId = guild_id; + } + + public override string ToString() => MessageId ?? "?"; + } + + public partial class MessageReference + { + public static MessageReference Parse(JsonElement json) + { + var message_id = json.GetPropertyOrNull("message_id")?.GetString(); + var channel_id = json.GetPropertyOrNull("channel_id")?.GetString(); + var guild_id = json.GetPropertyOrNull("guild_id")?.GetString(); + + return new MessageReference(message_id, channel_id, guild_id); + } + } +} \ No newline at end of file diff --git a/DiscordChatExporter.Domain/Exporting/Writers/Html/Core.css b/DiscordChatExporter.Domain/Exporting/Writers/Html/Core.css index d1567a5c..c4b4bf26 100644 --- a/DiscordChatExporter.Domain/Exporting/Writers/Html/Core.css +++ b/DiscordChatExporter.Domain/Exporting/Writers/Html/Core.css @@ -211,6 +211,39 @@ img { grid-template-columns: auto 1fr; } +.chatlog__reference-symbol { + grid-column: 1; + border-style: solid; + border-width: 2px 0 0 2px; + border-radius: 8px 0 0 0; + margin-left: 16px; + margin-top: 8px; +} + +.chatlog__reference { + grid-column: 2; + margin-left: 1.2em; + font-size: smaller; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.chatlog__reference-avatar { + border-radius: 50%; + height: 16px; + width: 16px; + vertical-align: middle; +} + +.chatlog__reference-name { + font-weight: 500; +} + +.chatlog__reference-content { + margin-left: .2em; +} + .chatlog__author-avatar-container { grid-column: 1; width: 40px; diff --git a/DiscordChatExporter.Domain/Exporting/Writers/Html/Dark.css b/DiscordChatExporter.Domain/Exporting/Writers/Html/Dark.css index 29cbf792..86ac2385 100644 --- a/DiscordChatExporter.Domain/Exporting/Writers/Html/Dark.css +++ b/DiscordChatExporter.Domain/Exporting/Writers/Html/Dark.css @@ -46,6 +46,14 @@ a { border-color: rgba(255, 255, 255, 0.1); } +.chatlog__reference-symbol { + border-color: #4f545c; +} + +.chatlog__reference { + color: #b5b6b8; +} + .chatlog__author-name { color: #ffffff; } diff --git a/DiscordChatExporter.Domain/Exporting/Writers/Html/Light.css b/DiscordChatExporter.Domain/Exporting/Writers/Html/Light.css index e5db91e5..d79bb7be 100644 --- a/DiscordChatExporter.Domain/Exporting/Writers/Html/Light.css +++ b/DiscordChatExporter.Domain/Exporting/Writers/Html/Light.css @@ -47,6 +47,14 @@ a { border-color: #eceeef; } +.chatlog__reference-symbol { + border-color: #c7ccd1; +} + +.chatlog__reference { + color: #5f5f60; +} + .chatlog__author-name { font-weight: 600; color: #2f3136; diff --git a/DiscordChatExporter.Domain/Exporting/Writers/Html/MessageGroup.cs b/DiscordChatExporter.Domain/Exporting/Writers/Html/MessageGroup.cs index 18fbeb41..b9db52f0 100644 --- a/DiscordChatExporter.Domain/Exporting/Writers/Html/MessageGroup.cs +++ b/DiscordChatExporter.Domain/Exporting/Writers/Html/MessageGroup.cs @@ -14,10 +14,21 @@ namespace DiscordChatExporter.Domain.Exporting.Writers.Html public IReadOnlyList Messages { get; } - public MessageGroup(User author, DateTimeOffset timestamp, IReadOnlyList messages) + public MessageReference? Reference { get; } + + public Message? ReferencedMessage {get; } + + public MessageGroup( + User author, + DateTimeOffset timestamp, + MessageReference? reference, + Message? referenced_message, + IReadOnlyList messages) { Author = author; Timestamp = timestamp; + Reference = reference; + ReferencedMessage = referenced_message; Messages = messages; } } @@ -27,7 +38,8 @@ namespace DiscordChatExporter.Domain.Exporting.Writers.Html public static bool CanJoin(Message message1, Message message2) => message1.Author.Id == message2.Author.Id && string.Equals(message1.Author.FullName, message2.Author.FullName, StringComparison.Ordinal) && - (message2.Timestamp - message1.Timestamp).Duration().TotalMinutes <= 7; + (message2.Timestamp - message1.Timestamp).Duration().TotalMinutes <= 7 && + message2.Reference is null; public static MessageGroup Join(IReadOnlyList messages) { @@ -36,6 +48,8 @@ namespace DiscordChatExporter.Domain.Exporting.Writers.Html return new MessageGroup( first.Author, first.Timestamp, + first.Reference, + first.ReferencedMessage, messages ); } diff --git a/DiscordChatExporter.Domain/Exporting/Writers/Html/MessageGroupTemplate.cshtml b/DiscordChatExporter.Domain/Exporting/Writers/Html/MessageGroupTemplate.cshtml index 1946d0db..cfb71add 100644 --- a/DiscordChatExporter.Domain/Exporting/Writers/Html/MessageGroupTemplate.cshtml +++ b/DiscordChatExporter.Domain/Exporting/Writers/Html/MessageGroupTemplate.cshtml @@ -16,6 +16,7 @@ var userMember = Model.ExportContext.TryGetMember(Model.MessageGroup.Author.Id); var userColor = Model.ExportContext.TryGetUserColor(Model.MessageGroup.Author.Id); var userNick = Model.MessageGroup.Author.IsBot ? Model.MessageGroup.Author.Name : userMember?.Nick ?? Model.MessageGroup.Author.Name; + var referencedUserNick = Model.MessageGroup.Author.IsBot ? Model.MessageGroup.Author.Name : userMember?.Nick ?? Model.MessageGroup.Author.Name; var userColorStyle = userColor != null ? $"color: rgb({userColor?.R},{userColor?.G},{userColor?.B})" @@ -23,6 +24,27 @@ }
+ @if (Model.MessageGroup.Reference != null) + { +
+
+
+ @if (Model.MessageGroup.ReferencedMessage != null) + { + Avatar + @Model.MessageGroup.ReferencedMessage.Author.FullName + + @Raw(FormatMarkdown(Model.MessageGroup.ReferencedMessage.Content)) + + } + else + { + + In reply to an unknown message + + } +
+ }
Avatar
diff --git a/DiscordChatExporter.Domain/Exporting/Writers/JsonMessageWriter.cs b/DiscordChatExporter.Domain/Exporting/Writers/JsonMessageWriter.cs index 4c13c4fa..71dbc4ef 100644 --- a/DiscordChatExporter.Domain/Exporting/Writers/JsonMessageWriter.cs +++ b/DiscordChatExporter.Domain/Exporting/Writers/JsonMessageWriter.cs @@ -263,6 +263,17 @@ namespace DiscordChatExporter.Domain.Exporting.Writers _writer.WriteEndArray(); + // Reference + + if (message.Reference is not null) + { + _writer.WriteStartObject("reference"); + _writer.WriteString("messageId", message.Reference.MessageId); + _writer.WriteString("channelId", message.Reference.ChannelId); + _writer.WriteString("guildId", message.Reference.GuildId); + _writer.WriteEndObject(); + } + _writer.WriteEndObject(); await _writer.FlushAsync();