mirror of
https://github.com/Tyrrrz/DiscordChatExporter.git
synced 2025-05-30 06:25:22 -04:00
Pass cancellation token to markdown visitor
This commit is contained in:
parent
e752269467
commit
b672c30071
8 changed files with 185 additions and 74 deletions
|
@ -19,8 +19,10 @@ internal partial class CsvMessageWriter : MessageWriter
|
||||||
_writer = new StreamWriter(stream);
|
_writer = new StreamWriter(stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ValueTask<string> FormatMarkdownAsync(string? markdown) =>
|
private ValueTask<string> FormatMarkdownAsync(
|
||||||
PlainTextMarkdownVisitor.FormatAsync(Context, markdown ?? "");
|
string markdown,
|
||||||
|
CancellationToken cancellationToken = default) =>
|
||||||
|
PlainTextMarkdownVisitor.FormatAsync(Context, markdown, cancellationToken);
|
||||||
|
|
||||||
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 +86,7 @@ internal partial class CsvMessageWriter : MessageWriter
|
||||||
await _writer.WriteAsync(',');
|
await _writer.WriteAsync(',');
|
||||||
|
|
||||||
// Message content
|
// Message content
|
||||||
await _writer.WriteAsync(CsvEncode(await FormatMarkdownAsync(message.Content)));
|
await _writer.WriteAsync(CsvEncode(await FormatMarkdownAsync(message.Content, cancellationToken)));
|
||||||
await _writer.WriteAsync(',');
|
await _writer.WriteAsync(',');
|
||||||
|
|
||||||
// Attachments
|
// Attachments
|
||||||
|
|
|
@ -30,10 +30,10 @@
|
||||||
ExportContext.FormatDate(date);
|
ExportContext.FormatDate(date);
|
||||||
|
|
||||||
ValueTask<string> FormatMarkdownAsync(string markdown) =>
|
ValueTask<string> FormatMarkdownAsync(string markdown) =>
|
||||||
HtmlMarkdownVisitor.FormatAsync(ExportContext, markdown);
|
HtmlMarkdownVisitor.FormatAsync(ExportContext, markdown, true, CancellationToken);
|
||||||
|
|
||||||
ValueTask<string> FormatEmbedMarkdownAsync(string markdown) =>
|
ValueTask<string> FormatEmbedMarkdownAsync(string markdown) =>
|
||||||
HtmlMarkdownVisitor.FormatAsync(ExportContext, markdown, false);
|
HtmlMarkdownVisitor.FormatAsync(ExportContext, markdown, false, CancellationToken);
|
||||||
|
|
||||||
var firstMessage = Messages.First();
|
var firstMessage = Messages.First();
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,7 @@
|
||||||
ExportContext.FormatDate(date);
|
ExportContext.FormatDate(date);
|
||||||
|
|
||||||
ValueTask<string> FormatMarkdownAsync(string markdown) =>
|
ValueTask<string> FormatMarkdownAsync(string markdown) =>
|
||||||
HtmlMarkdownVisitor.FormatAsync(ExportContext, markdown);
|
HtmlMarkdownVisitor.FormatAsync(ExportContext, markdown, true, CancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
|
|
|
@ -29,8 +29,10 @@ internal class JsonMessageWriter : MessageWriter
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private ValueTask<string> FormatMarkdownAsync(string? markdown) =>
|
private ValueTask<string> FormatMarkdownAsync(
|
||||||
PlainTextMarkdownVisitor.FormatAsync(Context, markdown ?? "");
|
string markdown,
|
||||||
|
CancellationToken cancellationToken = default) =>
|
||||||
|
PlainTextMarkdownVisitor.FormatAsync(Context, markdown, cancellationToken);
|
||||||
|
|
||||||
private async ValueTask WriteAttachmentAsync(
|
private async ValueTask WriteAttachmentAsync(
|
||||||
Attachment attachment,
|
Attachment attachment,
|
||||||
|
@ -115,8 +117,8 @@ internal class JsonMessageWriter : MessageWriter
|
||||||
{
|
{
|
||||||
_writer.WriteStartObject();
|
_writer.WriteStartObject();
|
||||||
|
|
||||||
_writer.WriteString("name", await FormatMarkdownAsync(embedField.Name));
|
_writer.WriteString("name", await FormatMarkdownAsync(embedField.Name, cancellationToken));
|
||||||
_writer.WriteString("value", await FormatMarkdownAsync(embedField.Value));
|
_writer.WriteString("value", await FormatMarkdownAsync(embedField.Value, cancellationToken));
|
||||||
_writer.WriteBoolean("isInline", embedField.IsInline);
|
_writer.WriteBoolean("isInline", embedField.IsInline);
|
||||||
|
|
||||||
_writer.WriteEndObject();
|
_writer.WriteEndObject();
|
||||||
|
@ -129,10 +131,10 @@ internal class JsonMessageWriter : MessageWriter
|
||||||
{
|
{
|
||||||
_writer.WriteStartObject();
|
_writer.WriteStartObject();
|
||||||
|
|
||||||
_writer.WriteString("title", await FormatMarkdownAsync(embed.Title));
|
_writer.WriteString("title", await FormatMarkdownAsync(embed.Title ?? "", cancellationToken));
|
||||||
_writer.WriteString("url", embed.Url);
|
_writer.WriteString("url", embed.Url);
|
||||||
_writer.WriteString("timestamp", embed.Timestamp);
|
_writer.WriteString("timestamp", embed.Timestamp);
|
||||||
_writer.WriteString("description", await FormatMarkdownAsync(embed.Description));
|
_writer.WriteString("description", await FormatMarkdownAsync(embed.Description ?? "", cancellationToken));
|
||||||
|
|
||||||
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());
|
||||||
|
@ -283,7 +285,7 @@ internal class JsonMessageWriter : MessageWriter
|
||||||
_writer.WriteBoolean("isPinned", message.IsPinned);
|
_writer.WriteBoolean("isPinned", message.IsPinned);
|
||||||
|
|
||||||
// Content
|
// Content
|
||||||
_writer.WriteString("content", await FormatMarkdownAsync(message.Content));
|
_writer.WriteString("content", await FormatMarkdownAsync(message.Content, cancellationToken));
|
||||||
|
|
||||||
// Author
|
// Author
|
||||||
_writer.WriteStartObject("author");
|
_writer.WriteStartObject("author");
|
||||||
|
|
|
@ -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;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using DiscordChatExporter.Core.Discord.Data;
|
using DiscordChatExporter.Core.Discord.Data;
|
||||||
using DiscordChatExporter.Core.Markdown;
|
using DiscordChatExporter.Core.Markdown;
|
||||||
|
@ -24,13 +25,17 @@ internal partial class HtmlMarkdownVisitor : MarkdownVisitor
|
||||||
_isJumbo = isJumbo;
|
_isJumbo = isJumbo;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override async ValueTask<MarkdownNode> VisitTextAsync(TextNode text)
|
protected override async ValueTask<MarkdownNode> VisitTextAsync(
|
||||||
|
TextNode text,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
_buffer.Append(HtmlEncode(text.Text));
|
_buffer.Append(HtmlEncode(text.Text));
|
||||||
return await base.VisitTextAsync(text);
|
return await base.VisitTextAsync(text, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override async ValueTask<MarkdownNode> VisitFormattingAsync(FormattingNode formatting)
|
protected override async ValueTask<MarkdownNode> VisitFormattingAsync(
|
||||||
|
FormattingNode formatting,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
var (openingTag, closingTag) = formatting.Kind switch
|
var (openingTag, closingTag) = formatting.Kind switch
|
||||||
{
|
{
|
||||||
|
@ -68,23 +73,27 @@ internal partial class HtmlMarkdownVisitor : MarkdownVisitor
|
||||||
};
|
};
|
||||||
|
|
||||||
_buffer.Append(openingTag);
|
_buffer.Append(openingTag);
|
||||||
var result = await base.VisitFormattingAsync(formatting);
|
var result = await base.VisitFormattingAsync(formatting, cancellationToken);
|
||||||
_buffer.Append(closingTag);
|
_buffer.Append(closingTag);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override async ValueTask<MarkdownNode> VisitInlineCodeBlockAsync(InlineCodeBlockNode inlineCodeBlock)
|
protected override async ValueTask<MarkdownNode> VisitInlineCodeBlockAsync(
|
||||||
|
InlineCodeBlockNode inlineCodeBlock,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
_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 await base.VisitInlineCodeBlockAsync(inlineCodeBlock);
|
return await base.VisitInlineCodeBlockAsync(inlineCodeBlock, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override async ValueTask<MarkdownNode> VisitMultiLineCodeBlockAsync(MultiLineCodeBlockNode multiLineCodeBlock)
|
protected override async ValueTask<MarkdownNode> VisitMultiLineCodeBlockAsync(
|
||||||
|
MultiLineCodeBlockNode multiLineCodeBlock,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
var highlightCssClass = !string.IsNullOrWhiteSpace(multiLineCodeBlock.Language)
|
var highlightCssClass = !string.IsNullOrWhiteSpace(multiLineCodeBlock.Language)
|
||||||
? $"language-{multiLineCodeBlock.Language}"
|
? $"language-{multiLineCodeBlock.Language}"
|
||||||
|
@ -95,10 +104,12 @@ internal partial class HtmlMarkdownVisitor : MarkdownVisitor
|
||||||
.Append(HtmlEncode(multiLineCodeBlock.Code))
|
.Append(HtmlEncode(multiLineCodeBlock.Code))
|
||||||
.Append("</code>");
|
.Append("</code>");
|
||||||
|
|
||||||
return await base.VisitMultiLineCodeBlockAsync(multiLineCodeBlock);
|
return await base.VisitMultiLineCodeBlockAsync(multiLineCodeBlock, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override async ValueTask<MarkdownNode> VisitLinkAsync(LinkNode link)
|
protected override async ValueTask<MarkdownNode> VisitLinkAsync(
|
||||||
|
LinkNode link,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
// 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(
|
||||||
|
@ -112,13 +123,15 @@ internal partial class HtmlMarkdownVisitor : MarkdownVisitor
|
||||||
: $"<a href=\"{HtmlEncode(link.Url)}\">"
|
: $"<a href=\"{HtmlEncode(link.Url)}\">"
|
||||||
);
|
);
|
||||||
|
|
||||||
var result = await base.VisitLinkAsync(link);
|
var result = await base.VisitLinkAsync(link, cancellationToken);
|
||||||
_buffer.Append("</a>");
|
_buffer.Append("</a>");
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override async ValueTask<MarkdownNode> VisitEmojiAsync(EmojiNode emoji)
|
protected override async ValueTask<MarkdownNode> VisitEmojiAsync(
|
||||||
|
EmojiNode emoji,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
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" : "";
|
||||||
|
@ -129,14 +142,16 @@ internal partial class HtmlMarkdownVisitor : MarkdownVisitor
|
||||||
$"class=\"chatlog__emoji {jumboClass}\" " +
|
$"class=\"chatlog__emoji {jumboClass}\" " +
|
||||||
$"alt=\"{emoji.Name}\" " +
|
$"alt=\"{emoji.Name}\" " +
|
||||||
$"title=\"{emoji.Code}\" " +
|
$"title=\"{emoji.Code}\" " +
|
||||||
$"src=\"{await _context.ResolveAssetUrlAsync(emojiImageUrl)}\"" +
|
$"src=\"{await _context.ResolveAssetUrlAsync(emojiImageUrl, cancellationToken)}\"" +
|
||||||
$">"
|
$">"
|
||||||
);
|
);
|
||||||
|
|
||||||
return await base.VisitEmojiAsync(emoji);
|
return await base.VisitEmojiAsync(emoji, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override async ValueTask<MarkdownNode> VisitMentionAsync(MentionNode mention)
|
protected override async ValueTask<MarkdownNode> VisitMentionAsync(
|
||||||
|
MentionNode mention,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
if (mention.Kind == MentionKind.Everyone)
|
if (mention.Kind == MentionKind.Everyone)
|
||||||
{
|
{
|
||||||
|
@ -191,10 +206,12 @@ internal partial class HtmlMarkdownVisitor : MarkdownVisitor
|
||||||
.Append("</span>");
|
.Append("</span>");
|
||||||
}
|
}
|
||||||
|
|
||||||
return await base.VisitMentionAsync(mention);
|
return await base.VisitMentionAsync(mention, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override async ValueTask<MarkdownNode> VisitUnixTimestampAsync(UnixTimestampNode timestamp)
|
protected override async ValueTask<MarkdownNode> VisitUnixTimestampAsync(
|
||||||
|
UnixTimestampNode timestamp,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
var dateString = timestamp.Date is not null
|
var dateString = timestamp.Date is not null
|
||||||
? _context.FormatDate(timestamp.Date.Value)
|
? _context.FormatDate(timestamp.Date.Value)
|
||||||
|
@ -210,7 +227,7 @@ internal partial class HtmlMarkdownVisitor : MarkdownVisitor
|
||||||
.Append(HtmlEncode(dateString))
|
.Append(HtmlEncode(dateString))
|
||||||
.Append("</span>");
|
.Append("</span>");
|
||||||
|
|
||||||
return await base.VisitUnixTimestampAsync(timestamp);
|
return await base.VisitUnixTimestampAsync(timestamp, cancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -221,7 +238,8 @@ internal partial class HtmlMarkdownVisitor
|
||||||
public static async ValueTask<string> FormatAsync(
|
public static async ValueTask<string> FormatAsync(
|
||||||
ExportContext context,
|
ExportContext context,
|
||||||
string markdown,
|
string markdown,
|
||||||
bool isJumboAllowed = true)
|
bool isJumboAllowed = true,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
var nodes = MarkdownParser.Parse(markdown);
|
var nodes = MarkdownParser.Parse(markdown);
|
||||||
|
|
||||||
|
@ -231,7 +249,8 @@ internal partial class HtmlMarkdownVisitor
|
||||||
|
|
||||||
var buffer = new StringBuilder();
|
var buffer = new StringBuilder();
|
||||||
|
|
||||||
await new HtmlMarkdownVisitor(context, buffer, isJumbo).VisitAsync(nodes);
|
await new HtmlMarkdownVisitor(context, buffer, isJumbo)
|
||||||
|
.VisitAsync(nodes, cancellationToken);
|
||||||
|
|
||||||
return buffer.ToString();
|
return buffer.ToString();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using DiscordChatExporter.Core.Markdown;
|
using DiscordChatExporter.Core.Markdown;
|
||||||
using DiscordChatExporter.Core.Markdown.Parsing;
|
using DiscordChatExporter.Core.Markdown.Parsing;
|
||||||
|
@ -17,13 +18,17 @@ internal partial class PlainTextMarkdownVisitor : MarkdownVisitor
|
||||||
_buffer = buffer;
|
_buffer = buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override async ValueTask<MarkdownNode> VisitTextAsync(TextNode text)
|
protected override async ValueTask<MarkdownNode> VisitTextAsync(
|
||||||
|
TextNode text,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
_buffer.Append(text.Text);
|
_buffer.Append(text.Text);
|
||||||
return await base.VisitTextAsync(text);
|
return await base.VisitTextAsync(text, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override async ValueTask<MarkdownNode> VisitEmojiAsync(EmojiNode emoji)
|
protected override async ValueTask<MarkdownNode> VisitEmojiAsync(
|
||||||
|
EmojiNode emoji,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
_buffer.Append(
|
_buffer.Append(
|
||||||
emoji.IsCustomEmoji
|
emoji.IsCustomEmoji
|
||||||
|
@ -31,10 +36,12 @@ internal partial class PlainTextMarkdownVisitor : MarkdownVisitor
|
||||||
: emoji.Name
|
: emoji.Name
|
||||||
);
|
);
|
||||||
|
|
||||||
return await base.VisitEmojiAsync(emoji);
|
return await base.VisitEmojiAsync(emoji, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override async ValueTask<MarkdownNode> VisitMentionAsync(MentionNode mention)
|
protected override async ValueTask<MarkdownNode> VisitMentionAsync(
|
||||||
|
MentionNode mention,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
if (mention.Kind == MentionKind.Everyone)
|
if (mention.Kind == MentionKind.Everyone)
|
||||||
{
|
{
|
||||||
|
@ -70,10 +77,12 @@ internal partial class PlainTextMarkdownVisitor : MarkdownVisitor
|
||||||
_buffer.Append($"@{name}");
|
_buffer.Append($"@{name}");
|
||||||
}
|
}
|
||||||
|
|
||||||
return await base.VisitMentionAsync(mention);
|
return await base.VisitMentionAsync(mention, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override async ValueTask<MarkdownNode> VisitUnixTimestampAsync(UnixTimestampNode timestamp)
|
protected override async ValueTask<MarkdownNode> VisitUnixTimestampAsync(
|
||||||
|
UnixTimestampNode timestamp,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
_buffer.Append(
|
_buffer.Append(
|
||||||
timestamp.Date is not null
|
timestamp.Date is not null
|
||||||
|
@ -81,18 +90,22 @@ internal partial class PlainTextMarkdownVisitor : MarkdownVisitor
|
||||||
: "Invalid date"
|
: "Invalid date"
|
||||||
);
|
);
|
||||||
|
|
||||||
return await base.VisitUnixTimestampAsync(timestamp);
|
return await base.VisitUnixTimestampAsync(timestamp, cancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal partial class PlainTextMarkdownVisitor
|
internal partial class PlainTextMarkdownVisitor
|
||||||
{
|
{
|
||||||
public static async ValueTask<string> FormatAsync(ExportContext context, string markdown)
|
public static async ValueTask<string> FormatAsync(
|
||||||
|
ExportContext context,
|
||||||
|
string markdown,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
var nodes = MarkdownParser.ParseMinimal(markdown);
|
var nodes = MarkdownParser.ParseMinimal(markdown);
|
||||||
var buffer = new StringBuilder();
|
var buffer = new StringBuilder();
|
||||||
|
|
||||||
await new PlainTextMarkdownVisitor(context, buffer).VisitAsync(nodes);
|
await new PlainTextMarkdownVisitor(context, buffer)
|
||||||
|
.VisitAsync(nodes, cancellationToken);
|
||||||
|
|
||||||
return buffer.ToString();
|
return buffer.ToString();
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,8 +19,10 @@ internal class PlainTextMessageWriter : MessageWriter
|
||||||
_writer = new StreamWriter(stream);
|
_writer = new StreamWriter(stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ValueTask<string> FormatMarkdownAsync(string? markdown) =>
|
private ValueTask<string> FormatMarkdownAsync(
|
||||||
PlainTextMarkdownVisitor.FormatAsync(Context, markdown ?? "");
|
string markdown,
|
||||||
|
CancellationToken cancellationToken = default) =>
|
||||||
|
PlainTextMarkdownVisitor.FormatAsync(Context, markdown, cancellationToken);
|
||||||
|
|
||||||
private async ValueTask WriteMessageHeaderAsync(Message message)
|
private async ValueTask WriteMessageHeaderAsync(Message message)
|
||||||
{
|
{
|
||||||
|
@ -48,7 +50,9 @@ internal class PlainTextMessageWriter : MessageWriter
|
||||||
{
|
{
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
await _writer.WriteLineAsync(await Context.ResolveAssetUrlAsync(attachment.Url, cancellationToken));
|
await _writer.WriteLineAsync(
|
||||||
|
await Context.ResolveAssetUrlAsync(attachment.Url, cancellationToken)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
await _writer.WriteLineAsync();
|
await _writer.WriteLineAsync();
|
||||||
|
@ -65,24 +69,44 @@ internal class PlainTextMessageWriter : MessageWriter
|
||||||
await _writer.WriteLineAsync("{Embed}");
|
await _writer.WriteLineAsync("{Embed}");
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(embed.Author?.Name))
|
if (!string.IsNullOrWhiteSpace(embed.Author?.Name))
|
||||||
|
{
|
||||||
await _writer.WriteLineAsync(embed.Author.Name);
|
await _writer.WriteLineAsync(embed.Author.Name);
|
||||||
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(embed.Url))
|
if (!string.IsNullOrWhiteSpace(embed.Url))
|
||||||
|
{
|
||||||
await _writer.WriteLineAsync(embed.Url);
|
await _writer.WriteLineAsync(embed.Url);
|
||||||
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(embed.Title))
|
if (!string.IsNullOrWhiteSpace(embed.Title))
|
||||||
await _writer.WriteLineAsync(await FormatMarkdownAsync(embed.Title));
|
{
|
||||||
|
await _writer.WriteLineAsync(
|
||||||
|
await FormatMarkdownAsync(embed.Title, cancellationToken)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(embed.Description))
|
if (!string.IsNullOrWhiteSpace(embed.Description))
|
||||||
await _writer.WriteLineAsync(await FormatMarkdownAsync(embed.Description));
|
{
|
||||||
|
await _writer.WriteLineAsync(
|
||||||
|
await FormatMarkdownAsync(embed.Description, cancellationToken)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
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(await FormatMarkdownAsync(field.Name));
|
{
|
||||||
|
await _writer.WriteLineAsync(
|
||||||
|
await FormatMarkdownAsync(field.Name, cancellationToken)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(field.Value))
|
if (!string.IsNullOrWhiteSpace(field.Value))
|
||||||
await _writer.WriteLineAsync(await FormatMarkdownAsync(field.Value));
|
{
|
||||||
|
await _writer.WriteLineAsync(
|
||||||
|
await FormatMarkdownAsync(field.Value, cancellationToken)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(embed.Thumbnail?.Url))
|
if (!string.IsNullOrWhiteSpace(embed.Thumbnail?.Url))
|
||||||
|
@ -109,7 +133,9 @@ internal class PlainTextMessageWriter : MessageWriter
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(embed.Footer?.Text))
|
if (!string.IsNullOrWhiteSpace(embed.Footer?.Text))
|
||||||
|
{
|
||||||
await _writer.WriteLineAsync(embed.Footer.Text);
|
await _writer.WriteLineAsync(embed.Footer.Text);
|
||||||
|
}
|
||||||
|
|
||||||
await _writer.WriteLineAsync();
|
await _writer.WriteLineAsync();
|
||||||
}
|
}
|
||||||
|
@ -152,7 +178,9 @@ internal class PlainTextMessageWriter : MessageWriter
|
||||||
await _writer.WriteAsync(reaction.Emoji.Name);
|
await _writer.WriteAsync(reaction.Emoji.Name);
|
||||||
|
|
||||||
if (reaction.Count > 1)
|
if (reaction.Count > 1)
|
||||||
|
{
|
||||||
await _writer.WriteAsync($" ({reaction.Count})");
|
await _writer.WriteAsync($" ({reaction.Count})");
|
||||||
|
}
|
||||||
|
|
||||||
await _writer.WriteAsync(' ');
|
await _writer.WriteAsync(' ');
|
||||||
}
|
}
|
||||||
|
@ -167,13 +195,19 @@ internal class PlainTextMessageWriter : MessageWriter
|
||||||
await _writer.WriteLineAsync($"Channel: {Context.Request.Channel.Category.Name} / {Context.Request.Channel.Name}");
|
await _writer.WriteLineAsync($"Channel: {Context.Request.Channel.Category.Name} / {Context.Request.Channel.Name}");
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(Context.Request.Channel.Topic))
|
if (!string.IsNullOrWhiteSpace(Context.Request.Channel.Topic))
|
||||||
|
{
|
||||||
await _writer.WriteLineAsync($"Topic: {Context.Request.Channel.Topic}");
|
await _writer.WriteLineAsync($"Topic: {Context.Request.Channel.Topic}");
|
||||||
|
}
|
||||||
|
|
||||||
if (Context.Request.After is not null)
|
if (Context.Request.After is not null)
|
||||||
|
{
|
||||||
await _writer.WriteLineAsync($"After: {Context.FormatDate(Context.Request.After.Value.ToDate())}");
|
await _writer.WriteLineAsync($"After: {Context.FormatDate(Context.Request.After.Value.ToDate())}");
|
||||||
|
}
|
||||||
|
|
||||||
if (Context.Request.Before is not null)
|
if (Context.Request.Before is not null)
|
||||||
|
{
|
||||||
await _writer.WriteLineAsync($"Before: {Context.FormatDate(Context.Request.Before.Value.ToDate())}");
|
await _writer.WriteLineAsync($"Before: {Context.FormatDate(Context.Request.Before.Value.ToDate())}");
|
||||||
|
}
|
||||||
|
|
||||||
await _writer.WriteLineAsync(new string('=', 62));
|
await _writer.WriteLineAsync(new string('=', 62));
|
||||||
await _writer.WriteLineAsync();
|
await _writer.WriteLineAsync();
|
||||||
|
@ -190,7 +224,11 @@ internal class PlainTextMessageWriter : MessageWriter
|
||||||
|
|
||||||
// Content
|
// Content
|
||||||
if (!string.IsNullOrWhiteSpace(message.Content))
|
if (!string.IsNullOrWhiteSpace(message.Content))
|
||||||
await _writer.WriteLineAsync(await FormatMarkdownAsync(message.Content));
|
{
|
||||||
|
await _writer.WriteLineAsync(
|
||||||
|
await FormatMarkdownAsync(message.Content, cancellationToken)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
await _writer.WriteLineAsync();
|
await _writer.WriteLineAsync();
|
||||||
|
|
||||||
|
|
|
@ -1,57 +1,94 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace DiscordChatExporter.Core.Markdown.Parsing;
|
namespace DiscordChatExporter.Core.Markdown.Parsing;
|
||||||
|
|
||||||
internal abstract class MarkdownVisitor
|
internal abstract class MarkdownVisitor
|
||||||
{
|
{
|
||||||
protected virtual ValueTask<MarkdownNode> VisitTextAsync(TextNode text) =>
|
protected virtual ValueTask<MarkdownNode> VisitTextAsync(
|
||||||
|
TextNode text,
|
||||||
|
CancellationToken cancellationToken = default) =>
|
||||||
new(text);
|
new(text);
|
||||||
|
|
||||||
protected virtual async ValueTask<MarkdownNode> VisitFormattingAsync(FormattingNode formatting)
|
protected virtual async ValueTask<MarkdownNode> VisitFormattingAsync(
|
||||||
|
FormattingNode formatting,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
await VisitAsync(formatting.Children);
|
await VisitAsync(formatting.Children, cancellationToken);
|
||||||
return formatting;
|
return formatting;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual ValueTask<MarkdownNode> VisitInlineCodeBlockAsync(InlineCodeBlockNode inlineCodeBlock) =>
|
protected virtual ValueTask<MarkdownNode> VisitInlineCodeBlockAsync(
|
||||||
|
InlineCodeBlockNode inlineCodeBlock,
|
||||||
|
CancellationToken cancellationToken = default) =>
|
||||||
new(inlineCodeBlock);
|
new(inlineCodeBlock);
|
||||||
|
|
||||||
protected virtual ValueTask<MarkdownNode> VisitMultiLineCodeBlockAsync(MultiLineCodeBlockNode multiLineCodeBlock) =>
|
protected virtual ValueTask<MarkdownNode> VisitMultiLineCodeBlockAsync(
|
||||||
|
MultiLineCodeBlockNode multiLineCodeBlock,
|
||||||
|
CancellationToken cancellationToken = default) =>
|
||||||
new(multiLineCodeBlock);
|
new(multiLineCodeBlock);
|
||||||
|
|
||||||
protected virtual async ValueTask<MarkdownNode> VisitLinkAsync(LinkNode link)
|
protected virtual async ValueTask<MarkdownNode> VisitLinkAsync(
|
||||||
|
LinkNode link,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
await VisitAsync(link.Children);
|
await VisitAsync(link.Children, cancellationToken);
|
||||||
return link;
|
return link;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual ValueTask<MarkdownNode> VisitEmojiAsync(EmojiNode emoji) =>
|
protected virtual ValueTask<MarkdownNode> VisitEmojiAsync(
|
||||||
|
EmojiNode emoji,
|
||||||
|
CancellationToken cancellationToken = default) =>
|
||||||
new(emoji);
|
new(emoji);
|
||||||
|
|
||||||
protected virtual ValueTask<MarkdownNode> VisitMentionAsync(MentionNode mention) =>
|
protected virtual ValueTask<MarkdownNode> VisitMentionAsync(
|
||||||
|
MentionNode mention,
|
||||||
|
CancellationToken cancellationToken = default) =>
|
||||||
new(mention);
|
new(mention);
|
||||||
|
|
||||||
protected virtual ValueTask<MarkdownNode> VisitUnixTimestampAsync(UnixTimestampNode timestamp) =>
|
protected virtual ValueTask<MarkdownNode> VisitUnixTimestampAsync(
|
||||||
|
UnixTimestampNode timestamp,
|
||||||
|
CancellationToken cancellationToken = default) =>
|
||||||
new(timestamp);
|
new(timestamp);
|
||||||
|
|
||||||
public async ValueTask<MarkdownNode> VisitAsync(MarkdownNode node) => node switch
|
public async ValueTask<MarkdownNode> VisitAsync(
|
||||||
{
|
MarkdownNode node,
|
||||||
TextNode text => await VisitTextAsync(text),
|
CancellationToken cancellationToken = default) => node switch
|
||||||
FormattingNode formatting => await VisitFormattingAsync(formatting),
|
{
|
||||||
InlineCodeBlockNode inlineCodeBlock => await VisitInlineCodeBlockAsync(inlineCodeBlock),
|
TextNode text =>
|
||||||
MultiLineCodeBlockNode multiLineCodeBlock => await VisitMultiLineCodeBlockAsync(multiLineCodeBlock),
|
await VisitTextAsync(text, cancellationToken),
|
||||||
LinkNode link => await VisitLinkAsync(link),
|
|
||||||
EmojiNode emoji => await VisitEmojiAsync(emoji),
|
|
||||||
MentionNode mention => await VisitMentionAsync(mention),
|
|
||||||
UnixTimestampNode timestamp => await VisitUnixTimestampAsync(timestamp),
|
|
||||||
_ => throw new ArgumentOutOfRangeException(nameof(node))
|
|
||||||
};
|
|
||||||
|
|
||||||
public async ValueTask VisitAsync(IEnumerable<MarkdownNode> nodes)
|
FormattingNode formatting =>
|
||||||
|
await VisitFormattingAsync(formatting, cancellationToken),
|
||||||
|
|
||||||
|
InlineCodeBlockNode inlineCodeBlock =>
|
||||||
|
await VisitInlineCodeBlockAsync(inlineCodeBlock, cancellationToken),
|
||||||
|
|
||||||
|
MultiLineCodeBlockNode multiLineCodeBlock =>
|
||||||
|
await VisitMultiLineCodeBlockAsync(multiLineCodeBlock, cancellationToken),
|
||||||
|
|
||||||
|
LinkNode link =>
|
||||||
|
await VisitLinkAsync(link, cancellationToken),
|
||||||
|
|
||||||
|
EmojiNode emoji =>
|
||||||
|
await VisitEmojiAsync(emoji, cancellationToken),
|
||||||
|
|
||||||
|
MentionNode mention =>
|
||||||
|
await VisitMentionAsync(mention, cancellationToken),
|
||||||
|
|
||||||
|
UnixTimestampNode timestamp =>
|
||||||
|
await VisitUnixTimestampAsync(timestamp, cancellationToken),
|
||||||
|
|
||||||
|
_ => throw new ArgumentOutOfRangeException(nameof(node))
|
||||||
|
};
|
||||||
|
|
||||||
|
public async ValueTask VisitAsync(
|
||||||
|
IEnumerable<MarkdownNode> nodes,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
foreach (var node in nodes)
|
foreach (var node in nodes)
|
||||||
await VisitAsync(node);
|
await VisitAsync(node, cancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Add table
Add a link
Reference in a new issue