Download emoji used in messages (#957)

This commit is contained in:
Roberto Blázquez 2022-10-24 21:38:06 +02:00 committed by GitHub
parent 38be44debb
commit 1131f8659d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 96 additions and 91 deletions

View file

@ -19,8 +19,8 @@ internal partial class CsvMessageWriter : MessageWriter
_writer = new StreamWriter(stream); _writer = new StreamWriter(stream);
} }
private string FormatMarkdown(string? markdown) => private ValueTask<string> FormatMarkdownAsync(string? markdown) =>
PlainTextMarkdownVisitor.Format(Context, markdown ?? ""); PlainTextMarkdownVisitor.FormatAsync(Context, markdown ?? "");
public override async ValueTask WritePreambleAsync(CancellationToken cancellationToken = default) => public override async ValueTask WritePreambleAsync(CancellationToken cancellationToken = default) =>
await _writer.WriteLineAsync("AuthorID,Author,Date,Content,Attachments,Reactions"); await _writer.WriteLineAsync("AuthorID,Author,Date,Content,Attachments,Reactions");
@ -84,7 +84,7 @@ internal partial class CsvMessageWriter : MessageWriter
await _writer.WriteAsync(','); await _writer.WriteAsync(',');
// Message content // Message content
await _writer.WriteAsync(CsvEncode(FormatMarkdown(message.Content))); await _writer.WriteAsync(CsvEncode(await FormatMarkdownAsync(message.Content)));
await _writer.WriteAsync(','); await _writer.WriteAsync(',');
// Attachments // Attachments

View file

@ -14,9 +14,9 @@
string FormatDate(DateTimeOffset date) => Model.ExportContext.FormatDate(date); string FormatDate(DateTimeOffset date) => Model.ExportContext.FormatDate(date);
string FormatMarkdown(string markdown) => Model.FormatMarkdown(markdown); ValueTask<string> FormatMarkdownAsync(string markdown) => Model.FormatMarkdownAsync(markdown);
string FormatEmbedMarkdown(string markdown) => Model.FormatMarkdown(markdown, false); ValueTask<string> FormatEmbedMarkdownAsync(string markdown) => Model.FormatMarkdownAsync(markdown, false);
var firstMessage = Model.Messages.First(); var firstMessage = Model.Messages.First();
@ -125,7 +125,7 @@
<span class="chatlog__reference-link" onclick="scrollToMessage(event, '@message.ReferencedMessage.Id')"> <span class="chatlog__reference-link" onclick="scrollToMessage(event, '@message.ReferencedMessage.Id')">
@if (!string.IsNullOrWhiteSpace(message.ReferencedMessage.Content) && !message.ReferencedMessage.IsContentHidden()) @if (!string.IsNullOrWhiteSpace(message.ReferencedMessage.Content) && !message.ReferencedMessage.IsContentHidden())
{ {
<!--wmm:ignore-->@Raw(FormatEmbedMarkdown(message.ReferencedMessage.Content))<!--/wmm:ignore--> <!--wmm:ignore-->@Raw(await FormatEmbedMarkdownAsync(message.ReferencedMessage.Content))<!--/wmm:ignore-->
} }
else if (message.ReferencedMessage.Attachments.Any() || message.ReferencedMessage.Embeds.Any()) else if (message.ReferencedMessage.Attachments.Any() || message.ReferencedMessage.Embeds.Any())
{ {
@ -176,7 +176,7 @@
@{/* Text */} @{/* Text */}
@if (!string.IsNullOrWhiteSpace(message.Content) && !message.IsContentHidden()) @if (!string.IsNullOrWhiteSpace(message.Content) && !message.IsContentHidden())
{ {
<span class="chatlog__markdown-preserve"><!--wmm:ignore-->@Raw(FormatMarkdown(message.Content))<!--/wmm:ignore--></span> <span class="chatlog__markdown-preserve"><!--wmm:ignore-->@Raw(await FormatMarkdownAsync(message.Content))<!--/wmm:ignore--></span>
} }
@{/* Edited timestamp */} @{/* Edited timestamp */}
@ -296,12 +296,12 @@
@if (!string.IsNullOrWhiteSpace(embed.Url)) @if (!string.IsNullOrWhiteSpace(embed.Url))
{ {
<a class="chatlog__embed-title-link" href="@embed.Url"> <a class="chatlog__embed-title-link" href="@embed.Url">
<div class="chatlog__markdown chatlog__markdown-preserve"><!--wmm:ignore-->@Raw(FormatEmbedMarkdown(embed.Title))<!--/wmm:ignore--></div> <div class="chatlog__markdown chatlog__markdown-preserve"><!--wmm:ignore-->@Raw(await FormatEmbedMarkdownAsync(embed.Title))<!--/wmm:ignore--></div>
</a> </a>
} }
else else
{ {
<div class="chatlog__markdown chatlog__markdown-preserve"><!--wmm:ignore-->@Raw(FormatEmbedMarkdown(embed.Title))<!--/wmm:ignore--></div> <div class="chatlog__markdown chatlog__markdown-preserve"><!--wmm:ignore-->@Raw(await FormatEmbedMarkdownAsync(embed.Title))<!--/wmm:ignore--></div>
} }
</div> </div>
} }
@ -382,12 +382,12 @@
@if (!string.IsNullOrWhiteSpace(embed.Url)) @if (!string.IsNullOrWhiteSpace(embed.Url))
{ {
<a class="chatlog__embed-title-link" href="@embed.Url"> <a class="chatlog__embed-title-link" href="@embed.Url">
<div class="chatlog__markdown chatlog__markdown-preserve"><!--wmm:ignore-->@Raw(FormatEmbedMarkdown(embed.Title))<!--/wmm:ignore--></div> <div class="chatlog__markdown chatlog__markdown-preserve"><!--wmm:ignore-->@Raw(await FormatEmbedMarkdownAsync(embed.Title))<!--/wmm:ignore--></div>
</a> </a>
} }
else else
{ {
<div class="chatlog__markdown chatlog__markdown-preserve"><!--wmm:ignore-->@Raw(FormatEmbedMarkdown(embed.Title))<!--/wmm:ignore--></div> <div class="chatlog__markdown chatlog__markdown-preserve"><!--wmm:ignore-->@Raw(await FormatEmbedMarkdownAsync(embed.Title))<!--/wmm:ignore--></div>
} }
</div> </div>
} }
@ -396,7 +396,7 @@
@if (!string.IsNullOrWhiteSpace(embed.Description)) @if (!string.IsNullOrWhiteSpace(embed.Description))
{ {
<div class="chatlog__embed-description"> <div class="chatlog__embed-description">
<div class="chatlog__markdown chatlog__markdown-preserve"><!--wmm:ignore-->@Raw(FormatEmbedMarkdown(embed.Description))<!--/wmm:ignore--></div> <div class="chatlog__markdown chatlog__markdown-preserve"><!--wmm:ignore-->@Raw(await FormatEmbedMarkdownAsync(embed.Description))<!--/wmm:ignore--></div>
</div> </div>
} }
@ -410,14 +410,14 @@
@if (!string.IsNullOrWhiteSpace(field.Name)) @if (!string.IsNullOrWhiteSpace(field.Name))
{ {
<div class="chatlog__embed-field-name"> <div class="chatlog__embed-field-name">
<div class="chatlog__markdown chatlog__markdown-preserve"><!--wmm:ignore-->@Raw(FormatEmbedMarkdown(field.Name))<!--/wmm:ignore--></div> <div class="chatlog__markdown chatlog__markdown-preserve"><!--wmm:ignore-->@Raw(await FormatEmbedMarkdownAsync(field.Name))<!--/wmm:ignore--></div>
</div> </div>
} }
@if (!string.IsNullOrWhiteSpace(field.Value)) @if (!string.IsNullOrWhiteSpace(field.Value))
{ {
<div class="chatlog__embed-field-value"> <div class="chatlog__embed-field-value">
<div class="chatlog__markdown chatlog__markdown-preserve"><!--wmm:ignore-->@Raw(FormatEmbedMarkdown(field.Value))<!--/wmm:ignore--></div> <div class="chatlog__markdown chatlog__markdown-preserve"><!--wmm:ignore-->@Raw(await FormatEmbedMarkdownAsync(field.Value))<!--/wmm:ignore--></div>
</div> </div>
} }
</div> </div>

View file

@ -1,4 +1,5 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks;
using DiscordChatExporter.Core.Discord.Data; using DiscordChatExporter.Core.Discord.Data;
using DiscordChatExporter.Core.Exporting.Writers.MarkdownVisitors; using DiscordChatExporter.Core.Exporting.Writers.MarkdownVisitors;
@ -16,6 +17,6 @@ internal class MessageGroupTemplateContext
Messages = messages; Messages = messages;
} }
public string FormatMarkdown(string? markdown, bool isJumboAllowed = true) => public ValueTask<string> FormatMarkdownAsync(string? markdown, bool isJumboAllowed = true) =>
HtmlMarkdownVisitor.Format(ExportContext, markdown ?? "", isJumboAllowed); HtmlMarkdownVisitor.FormatAsync(ExportContext, markdown ?? "", isJumboAllowed);
} }

View file

@ -18,7 +18,7 @@
string FormatDate(DateTimeOffset date) => Model.ExportContext.FormatDate(date); string FormatDate(DateTimeOffset date) => Model.ExportContext.FormatDate(date);
string FormatMarkdown(string markdown) => Model.FormatMarkdown(markdown); ValueTask<string> FormatMarkdownAsync(string markdown) => Model.FormatMarkdownAsync(markdown);
} }
<!DOCTYPE html> <!DOCTYPE html>
@ -851,7 +851,7 @@
@if (!string.IsNullOrWhiteSpace(Model.ExportContext.Request.Channel.Topic)) @if (!string.IsNullOrWhiteSpace(Model.ExportContext.Request.Channel.Topic))
{ {
<div class="preamble__entry preamble__entry--small">@Raw(FormatMarkdown(Model.ExportContext.Request.Channel.Topic))</div> <div class="preamble__entry preamble__entry--small">@Raw(await FormatMarkdownAsync(Model.ExportContext.Request.Channel.Topic))</div>
} }
@if (Model.ExportContext.Request.After is not null || Model.ExportContext.Request.Before is not null) @if (Model.ExportContext.Request.After is not null || Model.ExportContext.Request.Before is not null)

View file

@ -1,4 +1,5 @@
using DiscordChatExporter.Core.Exporting.Writers.MarkdownVisitors; using System.Threading.Tasks;
using DiscordChatExporter.Core.Exporting.Writers.MarkdownVisitors;
namespace DiscordChatExporter.Core.Exporting.Writers.Html; namespace DiscordChatExporter.Core.Exporting.Writers.Html;
@ -14,6 +15,6 @@ internal class PreambleTemplateContext
ThemeName = themeName; ThemeName = themeName;
} }
public string FormatMarkdown(string? markdown, bool isJumboAllowed = true) => public ValueTask<string> FormatMarkdownAsync(string? markdown, bool isJumboAllowed = true) =>
HtmlMarkdownVisitor.Format(ExportContext, markdown ?? "", isJumboAllowed); HtmlMarkdownVisitor.FormatAsync(ExportContext, markdown ?? "", isJumboAllowed);
} }

View file

@ -29,8 +29,8 @@ internal class JsonMessageWriter : MessageWriter
}); });
} }
private string FormatMarkdown(string? markdown) => private ValueTask<string> FormatMarkdownAsync(string? markdown) =>
PlainTextMarkdownVisitor.Format(Context, markdown ?? ""); PlainTextMarkdownVisitor.FormatAsync(Context, markdown ?? "");
private async ValueTask WriteAttachmentAsync( private async ValueTask WriteAttachmentAsync(
Attachment attachment, Attachment attachment,
@ -100,8 +100,8 @@ internal class JsonMessageWriter : MessageWriter
{ {
_writer.WriteStartObject(); _writer.WriteStartObject();
_writer.WriteString("name", FormatMarkdown(embedField.Name)); _writer.WriteString("name", await FormatMarkdownAsync(embedField.Name));
_writer.WriteString("value", FormatMarkdown(embedField.Value)); _writer.WriteString("value", await FormatMarkdownAsync(embedField.Value));
_writer.WriteBoolean("isInline", embedField.IsInline); _writer.WriteBoolean("isInline", embedField.IsInline);
_writer.WriteEndObject(); _writer.WriteEndObject();
@ -114,10 +114,10 @@ internal class JsonMessageWriter : MessageWriter
{ {
_writer.WriteStartObject(); _writer.WriteStartObject();
_writer.WriteString("title", FormatMarkdown(embed.Title)); _writer.WriteString("title", await FormatMarkdownAsync(embed.Title));
_writer.WriteString("url", embed.Url); _writer.WriteString("url", embed.Url);
_writer.WriteString("timestamp", embed.Timestamp); _writer.WriteString("timestamp", embed.Timestamp);
_writer.WriteString("description", FormatMarkdown(embed.Description)); _writer.WriteString("description", await FormatMarkdownAsync(embed.Description));
if (embed.Color is not null) if (embed.Color is not null)
_writer.WriteString("color", embed.Color.Value.ToHex()); _writer.WriteString("color", embed.Color.Value.ToHex());
@ -268,7 +268,7 @@ internal class JsonMessageWriter : MessageWriter
_writer.WriteBoolean("isPinned", message.IsPinned); _writer.WriteBoolean("isPinned", message.IsPinned);
// Content // Content
_writer.WriteString("content", FormatMarkdown(message.Content)); _writer.WriteString("content", await FormatMarkdownAsync(message.Content));
// Author // Author
_writer.WriteStartObject("author"); _writer.WriteStartObject("author");

View file

@ -3,6 +3,7 @@ using System.Linq;
using System.Net; using System.Net;
using System.Text; using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading.Tasks;
using DiscordChatExporter.Core.Discord.Data; using DiscordChatExporter.Core.Discord.Data;
using DiscordChatExporter.Core.Markdown; using DiscordChatExporter.Core.Markdown;
using DiscordChatExporter.Core.Markdown.Parsing; using DiscordChatExporter.Core.Markdown.Parsing;
@ -23,13 +24,13 @@ internal partial class HtmlMarkdownVisitor : MarkdownVisitor
_isJumbo = isJumbo; _isJumbo = isJumbo;
} }
protected override MarkdownNode VisitText(TextNode text) protected override ValueTask<MarkdownNode> VisitTextAsync(TextNode text)
{ {
_buffer.Append(HtmlEncode(text.Text)); _buffer.Append(HtmlEncode(text.Text));
return base.VisitText(text); return base.VisitTextAsync(text);
} }
protected override MarkdownNode VisitFormatting(FormattingNode formatting) protected override ValueTask<MarkdownNode> VisitFormattingAsync(FormattingNode formatting)
{ {
var (tagOpen, tagClose) = formatting.Kind switch var (tagOpen, tagClose) = formatting.Kind switch
{ {
@ -67,23 +68,23 @@ internal partial class HtmlMarkdownVisitor : MarkdownVisitor
}; };
_buffer.Append(tagOpen); _buffer.Append(tagOpen);
var result = base.VisitFormatting(formatting); var result = base.VisitFormattingAsync(formatting);
_buffer.Append(tagClose); _buffer.Append(tagClose);
return result; return result;
} }
protected override MarkdownNode VisitInlineCodeBlock(InlineCodeBlockNode inlineCodeBlock) protected override ValueTask<MarkdownNode> VisitInlineCodeBlockAsync(InlineCodeBlockNode inlineCodeBlock)
{ {
_buffer _buffer
.Append("<code class=\"chatlog__markdown-pre chatlog__markdown-pre--inline\">") .Append("<code class=\"chatlog__markdown-pre chatlog__markdown-pre--inline\">")
.Append(HtmlEncode(inlineCodeBlock.Code)) .Append(HtmlEncode(inlineCodeBlock.Code))
.Append("</code>"); .Append("</code>");
return base.VisitInlineCodeBlock(inlineCodeBlock); return base.VisitInlineCodeBlockAsync(inlineCodeBlock);
} }
protected override MarkdownNode VisitMultiLineCodeBlock(MultiLineCodeBlockNode multiLineCodeBlock) protected override ValueTask<MarkdownNode> VisitMultiLineCodeBlockAsync(MultiLineCodeBlockNode multiLineCodeBlock)
{ {
var highlightCssClass = !string.IsNullOrWhiteSpace(multiLineCodeBlock.Language) var highlightCssClass = !string.IsNullOrWhiteSpace(multiLineCodeBlock.Language)
? $"language-{multiLineCodeBlock.Language}" ? $"language-{multiLineCodeBlock.Language}"
@ -94,10 +95,10 @@ internal partial class HtmlMarkdownVisitor : MarkdownVisitor
.Append(HtmlEncode(multiLineCodeBlock.Code)) .Append(HtmlEncode(multiLineCodeBlock.Code))
.Append("</code>"); .Append("</code>");
return base.VisitMultiLineCodeBlock(multiLineCodeBlock); return base.VisitMultiLineCodeBlockAsync(multiLineCodeBlock);
} }
protected override MarkdownNode VisitLink(LinkNode link) protected override ValueTask<MarkdownNode> VisitLinkAsync(LinkNode link)
{ {
// Try to extract message ID if the link refers to a Discord message // Try to extract message ID if the link refers to a Discord message
var linkedMessageId = Regex.Match( var linkedMessageId = Regex.Match(
@ -111,24 +112,24 @@ internal partial class HtmlMarkdownVisitor : MarkdownVisitor
: $"<a href=\"{HtmlEncode(link.Url)}\">" : $"<a href=\"{HtmlEncode(link.Url)}\">"
); );
var result = base.VisitLink(link); var result = base.VisitLinkAsync(link);
_buffer.Append("</a>"); _buffer.Append("</a>");
return result; return result;
} }
protected override MarkdownNode VisitEmoji(EmojiNode emoji) protected override async ValueTask<MarkdownNode> VisitEmojiAsync(EmojiNode emoji)
{ {
var emojiImageUrl = Emoji.GetImageUrl(emoji.Id, emoji.Name, emoji.IsAnimated); var emojiImageUrl = Emoji.GetImageUrl(emoji.Id, emoji.Name, emoji.IsAnimated);
var jumboClass = _isJumbo ? "chatlog__emoji--large" : ""; var jumboClass = _isJumbo ? "chatlog__emoji--large" : "";
_buffer _buffer
.Append($"<img loading=\"lazy\" class=\"chatlog__emoji {jumboClass}\" alt=\"{emoji.Name}\" title=\"{emoji.Code}\" src=\"{emojiImageUrl}\">"); .Append($"<img loading=\"lazy\" class=\"chatlog__emoji {jumboClass}\" alt=\"{emoji.Name}\" title=\"{emoji.Code}\" src=\"{await _context.ResolveMediaUrlAsync(emojiImageUrl)}\">");
return base.VisitEmoji(emoji); return await base.VisitEmojiAsync(emoji);
} }
protected override MarkdownNode VisitMention(MentionNode mention) protected override ValueTask<MarkdownNode> VisitMentionAsync(MentionNode mention)
{ {
if (mention.Kind == MentionKind.Everyone) if (mention.Kind == MentionKind.Everyone)
{ {
@ -183,10 +184,10 @@ internal partial class HtmlMarkdownVisitor : MarkdownVisitor
.Append("</span>"); .Append("</span>");
} }
return base.VisitMention(mention); return base.VisitMentionAsync(mention);
} }
protected override MarkdownNode VisitUnixTimestamp(UnixTimestampNode timestamp) protected override ValueTask<MarkdownNode> VisitUnixTimestampAsync(UnixTimestampNode timestamp)
{ {
var dateString = timestamp.Date is not null var dateString = timestamp.Date is not null
? _context.FormatDate(timestamp.Date.Value) ? _context.FormatDate(timestamp.Date.Value)
@ -202,7 +203,7 @@ internal partial class HtmlMarkdownVisitor : MarkdownVisitor
.Append(HtmlEncode(dateString)) .Append(HtmlEncode(dateString))
.Append("</span>"); .Append("</span>");
return base.VisitUnixTimestamp(timestamp); return base.VisitUnixTimestampAsync(timestamp);
} }
} }
@ -210,7 +211,7 @@ internal partial class HtmlMarkdownVisitor
{ {
private static string HtmlEncode(string text) => WebUtility.HtmlEncode(text); private static string HtmlEncode(string text) => WebUtility.HtmlEncode(text);
public static string Format(ExportContext context, string markdown, bool isJumboAllowed = true) public static async ValueTask<string> FormatAsync(ExportContext context, string markdown, bool isJumboAllowed = true)
{ {
var nodes = MarkdownParser.Parse(markdown); var nodes = MarkdownParser.Parse(markdown);
@ -220,7 +221,7 @@ internal partial class HtmlMarkdownVisitor
var buffer = new StringBuilder(); var buffer = new StringBuilder();
new HtmlMarkdownVisitor(context, buffer, isJumbo).Visit(nodes); await new HtmlMarkdownVisitor(context, buffer, isJumbo).VisitAsync(nodes);
return buffer.ToString(); return buffer.ToString();
} }

View file

@ -1,4 +1,5 @@
using System.Text; using System.Text;
using System.Threading.Tasks;
using DiscordChatExporter.Core.Markdown; using DiscordChatExporter.Core.Markdown;
using DiscordChatExporter.Core.Markdown.Parsing; using DiscordChatExporter.Core.Markdown.Parsing;
using DiscordChatExporter.Core.Utils.Extensions; using DiscordChatExporter.Core.Utils.Extensions;
@ -16,13 +17,13 @@ internal partial class PlainTextMarkdownVisitor : MarkdownVisitor
_buffer = buffer; _buffer = buffer;
} }
protected override MarkdownNode VisitText(TextNode text) protected override ValueTask<MarkdownNode> VisitTextAsync(TextNode text)
{ {
_buffer.Append(text.Text); _buffer.Append(text.Text);
return base.VisitText(text); return base.VisitTextAsync(text);
} }
protected override MarkdownNode VisitEmoji(EmojiNode emoji) protected override ValueTask<MarkdownNode> VisitEmojiAsync(EmojiNode emoji)
{ {
_buffer.Append( _buffer.Append(
emoji.IsCustomEmoji emoji.IsCustomEmoji
@ -30,10 +31,10 @@ internal partial class PlainTextMarkdownVisitor : MarkdownVisitor
: emoji.Name : emoji.Name
); );
return base.VisitEmoji(emoji); return base.VisitEmojiAsync(emoji);
} }
protected override MarkdownNode VisitMention(MentionNode mention) protected override ValueTask<MarkdownNode> VisitMentionAsync(MentionNode mention)
{ {
if (mention.Kind == MentionKind.Everyone) if (mention.Kind == MentionKind.Everyone)
{ {
@ -69,10 +70,10 @@ internal partial class PlainTextMarkdownVisitor : MarkdownVisitor
_buffer.Append($"@{name}"); _buffer.Append($"@{name}");
} }
return base.VisitMention(mention); return base.VisitMentionAsync(mention);
} }
protected override MarkdownNode VisitUnixTimestamp(UnixTimestampNode timestamp) protected override ValueTask<MarkdownNode> VisitUnixTimestampAsync(UnixTimestampNode timestamp)
{ {
_buffer.Append( _buffer.Append(
timestamp.Date is not null timestamp.Date is not null
@ -80,18 +81,18 @@ internal partial class PlainTextMarkdownVisitor : MarkdownVisitor
: "Invalid date" : "Invalid date"
); );
return base.VisitUnixTimestamp(timestamp); return base.VisitUnixTimestampAsync(timestamp);
} }
} }
internal partial class PlainTextMarkdownVisitor internal partial class PlainTextMarkdownVisitor
{ {
public static string Format(ExportContext context, string markdown) public static async ValueTask<string> FormatAsync(ExportContext context, string markdown)
{ {
var nodes = MarkdownParser.ParseMinimal(markdown); var nodes = MarkdownParser.ParseMinimal(markdown);
var buffer = new StringBuilder(); var buffer = new StringBuilder();
new PlainTextMarkdownVisitor(context, buffer).Visit(nodes); await new PlainTextMarkdownVisitor(context, buffer).VisitAsync(nodes);
return buffer.ToString(); return buffer.ToString();
} }

View file

@ -19,8 +19,8 @@ internal class PlainTextMessageWriter : MessageWriter
_writer = new StreamWriter(stream); _writer = new StreamWriter(stream);
} }
private string FormatMarkdown(string? markdown) => private ValueTask<string> FormatMarkdownAsync(string? markdown) =>
PlainTextMarkdownVisitor.Format(Context, markdown ?? ""); PlainTextMarkdownVisitor.FormatAsync(Context, markdown ?? "");
private async ValueTask WriteMessageHeaderAsync(Message message) private async ValueTask WriteMessageHeaderAsync(Message message)
{ {
@ -71,18 +71,18 @@ internal class PlainTextMessageWriter : MessageWriter
await _writer.WriteLineAsync(embed.Url); await _writer.WriteLineAsync(embed.Url);
if (!string.IsNullOrWhiteSpace(embed.Title)) if (!string.IsNullOrWhiteSpace(embed.Title))
await _writer.WriteLineAsync(FormatMarkdown(embed.Title)); await _writer.WriteLineAsync(await FormatMarkdownAsync(embed.Title));
if (!string.IsNullOrWhiteSpace(embed.Description)) if (!string.IsNullOrWhiteSpace(embed.Description))
await _writer.WriteLineAsync(FormatMarkdown(embed.Description)); await _writer.WriteLineAsync(await FormatMarkdownAsync(embed.Description));
foreach (var field in embed.Fields) foreach (var field in embed.Fields)
{ {
if (!string.IsNullOrWhiteSpace(field.Name)) if (!string.IsNullOrWhiteSpace(field.Name))
await _writer.WriteLineAsync(FormatMarkdown(field.Name)); await _writer.WriteLineAsync(await FormatMarkdownAsync(field.Name));
if (!string.IsNullOrWhiteSpace(field.Value)) if (!string.IsNullOrWhiteSpace(field.Value))
await _writer.WriteLineAsync(FormatMarkdown(field.Value)); await _writer.WriteLineAsync(await FormatMarkdownAsync(field.Value));
} }
if (!string.IsNullOrWhiteSpace(embed.Thumbnail?.Url)) if (!string.IsNullOrWhiteSpace(embed.Thumbnail?.Url))
@ -174,7 +174,7 @@ internal class PlainTextMessageWriter : MessageWriter
// Content // Content
if (!string.IsNullOrWhiteSpace(message.Content)) if (!string.IsNullOrWhiteSpace(message.Content))
await _writer.WriteLineAsync(FormatMarkdown(message.Content)); await _writer.WriteLineAsync(await FormatMarkdownAsync(message.Content));
await _writer.WriteLineAsync(); await _writer.WriteLineAsync();

View file

@ -1,56 +1,57 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks;
namespace DiscordChatExporter.Core.Markdown.Parsing; namespace DiscordChatExporter.Core.Markdown.Parsing;
internal abstract class MarkdownVisitor internal abstract class MarkdownVisitor
{ {
protected virtual MarkdownNode VisitText(TextNode text) => protected virtual ValueTask<MarkdownNode> VisitTextAsync(TextNode text) =>
text; new(text);
protected virtual MarkdownNode VisitFormatting(FormattingNode formatting) protected virtual async ValueTask<MarkdownNode> VisitFormattingAsync(FormattingNode formatting)
{ {
Visit(formatting.Children); await VisitAsync(formatting.Children);
return formatting; return formatting;
} }
protected virtual MarkdownNode VisitInlineCodeBlock(InlineCodeBlockNode inlineCodeBlock) => protected virtual ValueTask<MarkdownNode> VisitInlineCodeBlockAsync(InlineCodeBlockNode inlineCodeBlock) =>
inlineCodeBlock; new(inlineCodeBlock);
protected virtual MarkdownNode VisitMultiLineCodeBlock(MultiLineCodeBlockNode multiLineCodeBlock) => protected virtual ValueTask<MarkdownNode> VisitMultiLineCodeBlockAsync(MultiLineCodeBlockNode multiLineCodeBlock) =>
multiLineCodeBlock; new(multiLineCodeBlock);
protected virtual MarkdownNode VisitLink(LinkNode link) protected virtual async ValueTask<MarkdownNode> VisitLinkAsync(LinkNode link)
{ {
Visit(link.Children); await VisitAsync(link.Children);
return link; return link;
} }
protected virtual MarkdownNode VisitEmoji(EmojiNode emoji) => protected virtual ValueTask<MarkdownNode> VisitEmojiAsync(EmojiNode emoji) =>
emoji; new(emoji);
protected virtual MarkdownNode VisitMention(MentionNode mention) => protected virtual ValueTask<MarkdownNode> VisitMentionAsync(MentionNode mention) =>
mention; new(mention);
protected virtual MarkdownNode VisitUnixTimestamp(UnixTimestampNode timestamp) => protected virtual ValueTask<MarkdownNode> VisitUnixTimestampAsync(UnixTimestampNode timestamp) =>
timestamp; new(timestamp);
public MarkdownNode Visit(MarkdownNode node) => node switch public async ValueTask<MarkdownNode> VisitAsync(MarkdownNode node) => node switch
{ {
TextNode text => VisitText(text), TextNode text => await VisitTextAsync(text),
FormattingNode formatting => VisitFormatting(formatting), FormattingNode formatting => await VisitFormattingAsync(formatting),
InlineCodeBlockNode inlineCodeBlock => VisitInlineCodeBlock(inlineCodeBlock), InlineCodeBlockNode inlineCodeBlock => await VisitInlineCodeBlockAsync(inlineCodeBlock),
MultiLineCodeBlockNode multiLineCodeBlock => VisitMultiLineCodeBlock(multiLineCodeBlock), MultiLineCodeBlockNode multiLineCodeBlock => await VisitMultiLineCodeBlockAsync(multiLineCodeBlock),
LinkNode link => VisitLink(link), LinkNode link => await VisitLinkAsync(link),
EmojiNode emoji => VisitEmoji(emoji), EmojiNode emoji => await VisitEmojiAsync(emoji),
MentionNode mention => VisitMention(mention), MentionNode mention => await VisitMentionAsync(mention),
UnixTimestampNode timestamp => VisitUnixTimestamp(timestamp), UnixTimestampNode timestamp => await VisitUnixTimestampAsync(timestamp),
_ => throw new ArgumentOutOfRangeException(nameof(node)) _ => throw new ArgumentOutOfRangeException(nameof(node))
}; };
public void Visit(IEnumerable<MarkdownNode> nodes) public async ValueTask VisitAsync(IEnumerable<MarkdownNode> nodes)
{ {
foreach (var node in nodes) foreach (var node in nodes)
Visit(node); await VisitAsync(node);
} }
} }