diff --git a/DiscordChatExporter.Core/Discord/Data/MessageKind.cs b/DiscordChatExporter.Core/Discord/Data/MessageKind.cs
index 67fb609..c24dedb 100644
--- a/DiscordChatExporter.Core/Discord/Data/MessageKind.cs
+++ b/DiscordChatExporter.Core/Discord/Data/MessageKind.cs
@@ -1,6 +1,4 @@
-using System.Text.RegularExpressions;
-
-namespace DiscordChatExporter.Core.Discord.Data;
+namespace DiscordChatExporter.Core.Discord.Data;
// https://discord.com/developers/docs/resources/channel#message-object-message-types
public enum MessageKind
@@ -18,10 +16,6 @@ public enum MessageKind
public static class MessageKindExtensions
{
- public static bool IsSystemMessage(this MessageKind c) =>
+ public static bool IsSystemNotification(this MessageKind c) =>
c is not MessageKind.Default and not MessageKind.Reply;
-
- public static string ToCssIdFormat(this MessageKind c) =>
- string.Join("-", Regex.Split(c.ToString(), @"(?
-
+
diff --git a/DiscordChatExporter.Core/Exporting/Writers/Html/MessageGroupTemplate.cshtml b/DiscordChatExporter.Core/Exporting/Writers/Html/MessageGroupTemplate.cshtml
index 01d2e1a..992f68f 100644
--- a/DiscordChatExporter.Core/Exporting/Writers/Html/MessageGroupTemplate.cshtml
+++ b/DiscordChatExporter.Core/Exporting/Writers/Html/MessageGroupTemplate.cshtml
@@ -46,129 +46,126 @@
@foreach (var (message, i) in Model.Messages.WithIndex())
{
- var isSystemMessage = message.Kind.IsSystemMessage();
-
var isFirst = i == 0;
- @{/* Left side */}
-
- @if (isSystemMessage)
- {
-
- }
- else if(isFirst)
- {
- // Reference symbol
- if (message.Reference is not null)
- {
-
- }
-
- // Avatar
-
- }
- else
- {
-
@message.Timestamp.ToLocalString("t")
- }
-
-
- @{/* Right side */}
-
- @if (isSystemMessage)
- {
-
- // system messages are grouped even if the message author is different
- // that's why we have to update the user values with the author of the current message
- userMember = Model.ExportContext.TryGetMember(message.Author.Id);
-
- userColor = Model.ExportContext.TryGetUserColor(message.Author.Id);
-
- userNick = message.Author.IsBot
- ? message.Author.Name
- : userMember?.Nick ?? message.Author.Name;
-
+ @{/* System notification */}
+ @if (message.Kind.IsSystemNotification())
+ {
+ // System notifications are grouped even if the message author is different.
+ // That's why we have to update the user values with the author of the current message.
+ userMember = Model.ExportContext.TryGetMember(message.Author.Id);
+ userColor = Model.ExportContext.TryGetUserColor(message.Author.Id);
+ userNick = message.Author.IsBot
+ ? message.Author.Name
+ : userMember?.Nick ?? message.Author.Name;
+
+
+
+
+
+
@{/* Author name */}
- @userNick
-
- @{/* System message content */}
+ @userNick
+
+ @{/* System notification content */}
@if(message.Kind == MessageKind.ChannelPinnedMessage)
{
- pinned a message to this channel.
+ pinned a message to this channel.
}
else
{
- @Raw(FormatMarkdown(Char.ToLowerInvariant(message.Content[0]) + message.Content.Substring(1)))
+ @(char.ToLowerInvariant(message.Content[0]) + message.Content[1..])
}
@{/* Timestamp */}
@FormatDate(message.Timestamp)
- }
- else if (isFirst)
- {
- // Reference
- if (message.Reference is not null)
+
+ }
+ // Regular message
+ else
+ {
+
+ @if (isFirst)
{
-
- @if (message.ReferencedMessage is not null)
- {
-
-
@referencedUserNick
-
-
- @if (!string.IsNullOrWhiteSpace(message.ReferencedMessage.Content) && !message.ReferencedMessage.IsContentHidden())
- {
- @Raw(FormatEmbedMarkdown(message.ReferencedMessage.Content))
- }
- else if (message.ReferencedMessage.Attachments.Any() || message.ReferencedMessage.Embeds.Any())
- {
- Click to see attachment
- 🖼️
- }
- else
+ // Reference symbol
+ if (message.Reference is not null)
+ {
+
+ }
+
+ // Avatar
+
+ }
+ else
+ {
+
@message.Timestamp.ToLocalString("t")
+ }
+
+
+
+ @if (isFirst)
+ {
+ // Reference
+ if (message.Reference is not null)
+ {
+
+ @if (message.ReferencedMessage is not null)
+ {
+
+
@referencedUserNick
+
+
+ @if (!string.IsNullOrWhiteSpace(message.ReferencedMessage.Content) && !message.ReferencedMessage.IsContentHidden())
+ {
+ @Raw(FormatEmbedMarkdown(message.ReferencedMessage.Content))
+ }
+ else if (message.ReferencedMessage.Attachments.Any() || message.ReferencedMessage.Embeds.Any())
+ {
+ Click to see attachment
+ 🖼️
+ }
+ else
+ {
+ Click to see original message
+ }
+
+
+ @if (message.ReferencedMessage.EditedTimestamp is not null)
{
- Click to see original message
+ (edited)
}
-
+
+ }
+ else
+ {
+
+ Original message was deleted or could not be loaded.
+
+ }
+
+ }
- @if (message.ReferencedMessage.EditedTimestamp is not null)
- {
- (edited)
- }
-
+ @foreach (var reaction in message.Reactions)
+ {
+
+
+ @reaction.Count
+
+ }
+
+ }
+
+ }
}
diff --git a/DiscordChatExporter.Core/Exporting/Writers/Html/PreambleTemplate.cshtml b/DiscordChatExporter.Core/Exporting/Writers/Html/PreambleTemplate.cshtml
index b7dc92c..97379a3 100644
--- a/DiscordChatExporter.Core/Exporting/Writers/Html/PreambleTemplate.cshtml
+++ b/DiscordChatExporter.Core/Exporting/Writers/Html/PreambleTemplate.cshtml
@@ -254,19 +254,18 @@
unicode-bidi: bidi-override;
}
- .chatlog__system-message{
- color: #96989D
+ .chatlog__system-notification {
+ color: @Themed("rgb(150, 152, 157)", "rgb(94, 103, 114)")
}
- .chatlog__system-message-reference-link{
+ .chatlog__system-notification-reference-link {
font-weight: 500;
color: #ffffff
}
- .chatlog__system-message-icon{
+ .chatlog__system-notification-icon {
width: 18px;
height: 18px;
- margin-right: 0.25rem;
}
.chatlog__header {
@@ -804,22 +803,19 @@
diff --git a/DiscordChatExporter.Core/Exporting/Writers/HtmlMessageWriter.cs b/DiscordChatExporter.Core/Exporting/Writers/HtmlMessageWriter.cs
index afb26dc..b821639 100644
--- a/DiscordChatExporter.Core/Exporting/Writers/HtmlMessageWriter.cs
+++ b/DiscordChatExporter.Core/Exporting/Writers/HtmlMessageWriter.cs
@@ -27,29 +27,43 @@ internal class HtmlMessageWriter : MessageWriter
private bool CanJoinGroup(Message message)
{
- // First message in the group can always join
- if(_messageGroup.LastOrDefault() is not { } lastMessage)
- {
+ // If the group is empty, any message can join it
+ if (_messageGroup.LastOrDefault() is not { } lastMessage)
return true;
- }
- // Group system messages with other system messages, regardless of author
- if (message.Kind.IsSystemMessage())
+ // Reply messages cannot join existing groups because they need to appear first
+ if (message.Kind == MessageKind.Reply)
+ return false;
+
+ // Grouping for system notifications
+ if (message.Kind.IsSystemNotification())
+ {
+ // Can only be grouped with other system notifications
+ if (!lastMessage.Kind.IsSystemNotification())
+ return false;
+ }
+ // Grouping for normal messages
+ else
{
- return lastMessage.Kind.IsSystemMessage();
+ // Can only be grouped with other normal messages
+ if (lastMessage.Kind.IsSystemNotification())
+ return false;
+
+ // Messages must be within 7 minutes of each other
+ if ((message.Timestamp - lastMessage.Timestamp).Duration().TotalMinutes > 7)
+ return false;
+
+ // Messages must be from the same author
+ if (message.Author.Id != lastMessage.Author.Id)
+ return false;
+
+ // If the user changed their name after the last message, their new messages
+ // cannot join an existing group.
+ if (!string.Equals(message.Author.FullName, lastMessage.Author.FullName, StringComparison.Ordinal))
+ return false;
}
- return
- // Must be a non system message
- !message.Kind.IsSystemMessage() &&
- // Must be from the same author
- lastMessage.Author.Id == message.Author.Id &&
- // Author's name must not have changed between messages
- string.Equals(lastMessage.Author.FullName, message.Author.FullName, StringComparison.Ordinal) &&
- // Duration between messages must be 7 minutes or less
- (message.Timestamp - lastMessage.Timestamp).Duration().TotalMinutes <= 7 &&
- // Other message must not be a reply
- message.Reference is null;
+ return true;
}
// Use to preserve blocks of code inside the templates
diff --git a/DiscordChatExporter.Core/Utils/Extensions/StringExtensions.cs b/DiscordChatExporter.Core/Utils/Extensions/StringExtensions.cs
index 336b7db..8868355 100644
--- a/DiscordChatExporter.Core/Utils/Extensions/StringExtensions.cs
+++ b/DiscordChatExporter.Core/Utils/Extensions/StringExtensions.cs
@@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.Text;
+using System.Text.RegularExpressions;
namespace DiscordChatExporter.Core.Utils.Extensions;
@@ -25,6 +26,9 @@ public static class StringExtensions
}
}
+ public static string ToDashCase(this string str) =>
+ Regex.Replace(str, @"(\p{Ll})(\p{Lu})", "$1-$2");
+
public static StringBuilder AppendIfNotEmpty(this StringBuilder builder, char value) =>
builder.Length > 0
? builder.Append(value)