|
|
@ -1,11 +1,10 @@
|
|
|
|
using System;
|
|
|
|
using System;
|
|
|
|
using System.Collections.Generic;
|
|
|
|
using System.Collections.Generic;
|
|
|
|
using System.Drawing;
|
|
|
|
|
|
|
|
using System.Globalization;
|
|
|
|
using System.Globalization;
|
|
|
|
using System.Linq;
|
|
|
|
using System.Linq;
|
|
|
|
using System.Net;
|
|
|
|
using System.Text;
|
|
|
|
using System.Text.RegularExpressions;
|
|
|
|
|
|
|
|
using DiscordChatExporter.Core.Internal;
|
|
|
|
using DiscordChatExporter.Core.Internal;
|
|
|
|
|
|
|
|
using DiscordChatExporter.Core.Markdown;
|
|
|
|
using DiscordChatExporter.Core.Models;
|
|
|
|
using DiscordChatExporter.Core.Models;
|
|
|
|
using Scriban.Runtime;
|
|
|
|
using Scriban.Runtime;
|
|
|
|
using Tyrrrz.Extensions;
|
|
|
|
using Tyrrrz.Extensions;
|
|
|
@ -73,8 +72,6 @@ namespace DiscordChatExporter.Core.Services
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private string HtmlEncode(string str) => WebUtility.HtmlEncode(str);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private string Format(IFormattable obj, string format) =>
|
|
|
|
private string Format(IFormattable obj, string format) =>
|
|
|
|
obj.ToString(format, CultureInfo.InvariantCulture);
|
|
|
|
obj.ToString(format, CultureInfo.InvariantCulture);
|
|
|
|
|
|
|
|
|
|
|
@ -95,254 +92,150 @@ namespace DiscordChatExporter.Core.Services
|
|
|
|
return $"{size:0.#} {units[unit]}";
|
|
|
|
return $"{size:0.#} {units[unit]}";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private string FormatColor(Color color)
|
|
|
|
private string FormatMarkdownPlainText(IEnumerable<Node> nodes)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
return $"{color.R},{color.G},{color.B},{color.A}";
|
|
|
|
var buffer = new StringBuilder();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private string FormatContentPlainText(string content)
|
|
|
|
foreach (var node in nodes)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
// New lines
|
|
|
|
if (node is FormattedNode formattedNode)
|
|
|
|
content = content.Replace("\n", Environment.NewLine);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// User mentions (<@id> and <@!id>)
|
|
|
|
|
|
|
|
var mentionedUserIds = Regex.Matches(content, "<@!?(\\d+)>")
|
|
|
|
|
|
|
|
.Cast<Match>()
|
|
|
|
|
|
|
|
.Select(m => m.Groups[1].Value)
|
|
|
|
|
|
|
|
.ExceptBlank()
|
|
|
|
|
|
|
|
.ToArray();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var mentionedUserId in mentionedUserIds)
|
|
|
|
|
|
|
|
{
|
|
|
|
{
|
|
|
|
var mentionedUser = _log.Mentionables.GetUser(mentionedUserId);
|
|
|
|
var innerText = FormatMarkdownPlainText(formattedNode.Children);
|
|
|
|
content = Regex.Replace(content, $"<@!?{mentionedUserId}>", $"@{mentionedUser.FullName}");
|
|
|
|
buffer.Append($"{formattedNode.Token}{innerText}{formattedNode.Token}");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Channel mentions (<#id>)
|
|
|
|
else if (node is MentionNode mentionNode && mentionNode.Type != MentionType.Meta)
|
|
|
|
var mentionedChannelIds = Regex.Matches(content, "<#(\\d+)>")
|
|
|
|
{
|
|
|
|
.Cast<Match>()
|
|
|
|
if (mentionNode.Type == MentionType.User)
|
|
|
|
.Select(m => m.Groups[1].Value)
|
|
|
|
|
|
|
|
.ExceptBlank()
|
|
|
|
|
|
|
|
.ToArray();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var mentionedChannelId in mentionedChannelIds)
|
|
|
|
|
|
|
|
{
|
|
|
|
{
|
|
|
|
var mentionedChannel = _log.Mentionables.GetChannel(mentionedChannelId);
|
|
|
|
var user = _log.Mentionables.GetUser(mentionNode.Id);
|
|
|
|
content = content.Replace($"<#{mentionedChannelId}>", $"#{mentionedChannel.Name}");
|
|
|
|
buffer.Append($"@{user.Name}");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Role mentions (<@&id>)
|
|
|
|
else if (mentionNode.Type == MentionType.Channel)
|
|
|
|
var mentionedRoleIds = Regex.Matches(content, "<@&(\\d+)>")
|
|
|
|
|
|
|
|
.Cast<Match>()
|
|
|
|
|
|
|
|
.Select(m => m.Groups[1].Value)
|
|
|
|
|
|
|
|
.ExceptBlank()
|
|
|
|
|
|
|
|
.ToArray();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var mentionedRoleId in mentionedRoleIds)
|
|
|
|
|
|
|
|
{
|
|
|
|
{
|
|
|
|
var mentionedRole = _log.Mentionables.GetRole(mentionedRoleId);
|
|
|
|
var channel = _log.Mentionables.GetChannel(mentionNode.Id);
|
|
|
|
content = content.Replace($"<@&{mentionedRoleId}>", $"@{mentionedRole.Name}");
|
|
|
|
buffer.Append($"#{channel.Name}");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Custom emojis (<:name:id>)
|
|
|
|
else if (mentionNode.Type == MentionType.Role)
|
|
|
|
content = Regex.Replace(content, "<(:.*?:)\\d*>", "$1");
|
|
|
|
{
|
|
|
|
|
|
|
|
var role = _log.Mentionables.GetRole(mentionNode.Id);
|
|
|
|
return content;
|
|
|
|
buffer.Append($"@{role.Name}");
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private string FormatContentHtml(string content, bool allowLinks = false)
|
|
|
|
else if (node is EmojiNode emojiNode)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
// HTML-encode content
|
|
|
|
buffer.Append($":{emojiNode.Name}:");
|
|
|
|
content = HtmlEncode(content);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Encode multiline codeblocks (```text```)
|
|
|
|
|
|
|
|
content = Regex.Replace(content,
|
|
|
|
|
|
|
|
@"```+(?:[^`]*?\n)?([^`]+)\n?```+",
|
|
|
|
|
|
|
|
m => $"\x1AM{m.Groups[1].Value.Base64Encode()}\x1AM");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Encode inline codeblocks (`text`)
|
|
|
|
|
|
|
|
content = Regex.Replace(content,
|
|
|
|
|
|
|
|
@"`([^`]+)`",
|
|
|
|
|
|
|
|
m => $"\x1AI{m.Groups[1].Value.Base64Encode()}\x1AI");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Encode links
|
|
|
|
else
|
|
|
|
if (allowLinks)
|
|
|
|
|
|
|
|
{
|
|
|
|
{
|
|
|
|
content = Regex.Replace(content, @"\[(.*?)\]\((.*?)\)",
|
|
|
|
buffer.Append(node.Lexeme);
|
|
|
|
m => $"\x1AL{m.Groups[1].Value.Base64Encode()}|{m.Groups[2].Value.Base64Encode()}\x1AL");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Encode URLs
|
|
|
|
return buffer.ToString();
|
|
|
|
content = Regex.Replace(content,
|
|
|
|
}
|
|
|
|
@"(\b(?:(?:https?|ftp|file)://|www\.|ftp\.)(?:\([-a-zA-Z0-9+&@#/%?=~_|!:,\.\[\];]*\)|[-a-zA-Z0-9+&@#/%?=~_|!:,\.\[\];])*(?:\([-a-zA-Z0-9+&@#/%?=~_|!:,\.\[\];]*\)|[-a-zA-Z0-9+&@#/%=~_|$]))",
|
|
|
|
|
|
|
|
m => $"\x1AU{m.Groups[1].Value.Base64Encode()}\x1AU");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Process bold (**text**)
|
|
|
|
|
|
|
|
content = Regex.Replace(content, @"(\*\*)(?=\S)(.+?[*_]*)(?<=\S)\1", "<b>$2</b>");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Process underline (__text__)
|
|
|
|
|
|
|
|
content = Regex.Replace(content, @"(__)(?=\S)(.+?)(?<=\S)\1", "<u>$2</u>");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Process italic (*text* or _text_)
|
|
|
|
|
|
|
|
content = Regex.Replace(content, @"(\*|_)(?=\S)(.+?)(?<=\S)\1", "<i>$2</i>");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Process strike through (~~text~~)
|
|
|
|
|
|
|
|
content = Regex.Replace(content, @"(~~)(?=\S)(.+?)(?<=\S)\1", "<s>$2</s>");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Decode and process multiline codeblocks
|
|
|
|
private string FormatMarkdownPlainText(string input)
|
|
|
|
content = Regex.Replace(content, "\x1AM(.*?)\x1AM",
|
|
|
|
=> FormatMarkdownPlainText(MarkdownParser.Parse(input));
|
|
|
|
m => $"<div class=\"pre pre--multiline\">{m.Groups[1].Value.Base64Decode()}</div>");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Decode and process inline codeblocks
|
|
|
|
private string FormatMarkdownHtml(IEnumerable<Node> nodes)
|
|
|
|
content = Regex.Replace(content, "\x1AI(.*?)\x1AI",
|
|
|
|
{
|
|
|
|
m => $"<span class=\"pre pre--inline\">{m.Groups[1].Value.Base64Decode()}</span>");
|
|
|
|
var buffer = new StringBuilder();
|
|
|
|
|
|
|
|
|
|
|
|
// Decode and process links
|
|
|
|
foreach (var node in nodes)
|
|
|
|
if (allowLinks)
|
|
|
|
{
|
|
|
|
|
|
|
|
if (node is TextNode textNode)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
content = Regex.Replace(content, "\x1AL(.*?)\\|(.*?)\x1AL",
|
|
|
|
buffer.Append(textNode.Text.HtmlEncode());
|
|
|
|
m => $"<a href=\"{m.Groups[2].Value.Base64Decode()}\">{m.Groups[1].Value.Base64Decode()}</a>");
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Decode and process URLs
|
|
|
|
else if (node is FormattedNode formattedNode)
|
|
|
|
content = Regex.Replace(content, "\x1AU(.*?)\x1AU",
|
|
|
|
{
|
|
|
|
m => $"<a href=\"{m.Groups[1].Value.Base64Decode()}\">{m.Groups[1].Value.Base64Decode()}</a>");
|
|
|
|
var innerHtml = FormatMarkdownHtml(formattedNode.Children);
|
|
|
|
|
|
|
|
|
|
|
|
// Process new lines
|
|
|
|
if (formattedNode.Formatting == TextFormatting.Bold)
|
|
|
|
content = content.Replace("\n", "<br />");
|
|
|
|
buffer.Append($"<strong>{innerHtml}</strong>");
|
|
|
|
|
|
|
|
|
|
|
|
// Meta mentions (@everyone)
|
|
|
|
else if (formattedNode.Formatting == TextFormatting.Italic)
|
|
|
|
content = content.Replace("@everyone", "<span class=\"mention\">@everyone</span>");
|
|
|
|
buffer.Append($"<em>{innerHtml}</em>");
|
|
|
|
|
|
|
|
|
|
|
|
// Meta mentions (@here)
|
|
|
|
else if (formattedNode.Formatting == TextFormatting.Underline)
|
|
|
|
content = content.Replace("@here", "<span class=\"mention\">@here</span>");
|
|
|
|
buffer.Append($"<u>{innerHtml}</u>");
|
|
|
|
|
|
|
|
|
|
|
|
// User mentions (<@id> and <@!id>)
|
|
|
|
else if (formattedNode.Formatting == TextFormatting.Strikethrough)
|
|
|
|
var mentionedUserIds = Regex.Matches(content, "<@!?(\\d+)>")
|
|
|
|
buffer.Append($"<s>{innerHtml}</s>");
|
|
|
|
.Cast<Match>()
|
|
|
|
|
|
|
|
.Select(m => m.Groups[1].Value)
|
|
|
|
|
|
|
|
.ExceptBlank()
|
|
|
|
|
|
|
|
.ToArray();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var mentionedUserId in mentionedUserIds)
|
|
|
|
else if (formattedNode.Formatting == TextFormatting.Spoiler)
|
|
|
|
{
|
|
|
|
buffer.Append($"<span class=\"spoiler\">{innerHtml}</span>");
|
|
|
|
var mentionedUser = _log.Mentionables.GetUser(mentionedUserId);
|
|
|
|
|
|
|
|
content = Regex.Replace(content, $"<@!?{mentionedUserId}>",
|
|
|
|
|
|
|
|
$"<span class=\"mention\" title=\"{HtmlEncode(mentionedUser.FullName)}\">" +
|
|
|
|
|
|
|
|
$"@{HtmlEncode(mentionedUser.Name)}" +
|
|
|
|
|
|
|
|
"</span>");
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Channel mentions (<#id>)
|
|
|
|
else if (node is InlineCodeBlockNode inlineCodeBlockNode)
|
|
|
|
var mentionedChannelIds = Regex.Matches(content, "<#(\\d+)>")
|
|
|
|
|
|
|
|
.Cast<Match>()
|
|
|
|
|
|
|
|
.Select(m => m.Groups[1].Value)
|
|
|
|
|
|
|
|
.ExceptBlank()
|
|
|
|
|
|
|
|
.ToArray();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var mentionedChannelId in mentionedChannelIds)
|
|
|
|
|
|
|
|
{
|
|
|
|
{
|
|
|
|
var mentionedChannel = _log.Mentionables.GetChannel(mentionedChannelId);
|
|
|
|
buffer.Append($"<span class=\"pre pre--inline\">{inlineCodeBlockNode.Code.HtmlEncode()}</span>");
|
|
|
|
content = content.Replace($"<#{mentionedChannelId}>",
|
|
|
|
|
|
|
|
"<span class=\"mention\">" +
|
|
|
|
|
|
|
|
$"#{HtmlEncode(mentionedChannel.Name)}" +
|
|
|
|
|
|
|
|
"</span>");
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Role mentions (<@&id>)
|
|
|
|
else if (node is MultilineCodeBlockNode multilineCodeBlockNode)
|
|
|
|
var mentionedRoleIds = Regex.Matches(content, "<@&(\\d+)>")
|
|
|
|
|
|
|
|
.Cast<Match>()
|
|
|
|
|
|
|
|
.Select(m => m.Groups[1].Value)
|
|
|
|
|
|
|
|
.ExceptBlank()
|
|
|
|
|
|
|
|
.ToArray();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var mentionedRoleId in mentionedRoleIds)
|
|
|
|
|
|
|
|
{
|
|
|
|
{
|
|
|
|
var mentionedRole = _log.Mentionables.GetRole(mentionedRoleId);
|
|
|
|
var languageCssClass = multilineCodeBlockNode.Language.IsNotBlank()
|
|
|
|
content = content.Replace($"<@&{mentionedRoleId}>",
|
|
|
|
? "language-" + multilineCodeBlockNode.Language
|
|
|
|
"<span class=\"mention\">" +
|
|
|
|
: null;
|
|
|
|
$"@{HtmlEncode(mentionedRole.Name)}" +
|
|
|
|
|
|
|
|
"</span>");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Custom emojis (<:name:id>)
|
|
|
|
buffer.Append(
|
|
|
|
var isJumboable = Regex.Replace(content, "<(:.*?:)(\\d*)>", "").IsBlank();
|
|
|
|
$"<div class=\"pre pre--multiline {languageCssClass}\">{multilineCodeBlockNode.Code.HtmlEncode()}</div>");
|
|
|
|
var emojiClass = isJumboable ? "emoji emoji--large" : "emoji";
|
|
|
|
|
|
|
|
content = Regex.Replace(content, "<(:.*?:)(\\d*)>",
|
|
|
|
|
|
|
|
$"<img class=\"{emojiClass}\" title=\"$1\" src=\"https://cdn.discordapp.com/emojis/$2.png\" />");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return content;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private string FormatContentCsv(string content)
|
|
|
|
else if (node is MentionNode mentionNode)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
// Escape quotes
|
|
|
|
if (mentionNode.Type == MentionType.Meta)
|
|
|
|
content = content.Replace("\"", "\"\"");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Escape commas and semicolons
|
|
|
|
|
|
|
|
if (content.Contains(",") || content.Contains(";"))
|
|
|
|
|
|
|
|
content = $"\"{content}\"";
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// User mentions (<@id> and <@!id>)
|
|
|
|
|
|
|
|
var mentionedUserIds = Regex.Matches(content, "<@!?(\\d+)>")
|
|
|
|
|
|
|
|
.Cast<Match>()
|
|
|
|
|
|
|
|
.Select(m => m.Groups[1].Value)
|
|
|
|
|
|
|
|
.ExceptBlank()
|
|
|
|
|
|
|
|
.ToArray();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var mentionedUserId in mentionedUserIds)
|
|
|
|
|
|
|
|
{
|
|
|
|
{
|
|
|
|
var mentionedUser = _log.Mentionables.GetUser(mentionedUserId);
|
|
|
|
buffer.Append($"<span class=\"mention\">@{mentionNode.Id.HtmlEncode()}</span>");
|
|
|
|
content = Regex.Replace(content, $"<@!?{mentionedUserId}>", $"@{mentionedUser.FullName}");
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Channel mentions (<#id>)
|
|
|
|
else if (mentionNode.Type == MentionType.User)
|
|
|
|
var mentionedChannelIds = Regex.Matches(content, "<#(\\d+)>")
|
|
|
|
|
|
|
|
.Cast<Match>()
|
|
|
|
|
|
|
|
.Select(m => m.Groups[1].Value)
|
|
|
|
|
|
|
|
.ExceptBlank()
|
|
|
|
|
|
|
|
.ToArray();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var mentionedChannelId in mentionedChannelIds)
|
|
|
|
|
|
|
|
{
|
|
|
|
{
|
|
|
|
var mentionedChannel = _log.Mentionables.GetChannel(mentionedChannelId);
|
|
|
|
var user = _log.Mentionables.GetUser(mentionNode.Id);
|
|
|
|
content = content.Replace($"<#{mentionedChannelId}>", $"#{mentionedChannel.Name}");
|
|
|
|
buffer.Append($"<span class=\"mention\" title=\"{user.FullName}\">@{user.Name.HtmlEncode()}</span>");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Role mentions (<@&id>)
|
|
|
|
else if (mentionNode.Type == MentionType.Channel)
|
|
|
|
var mentionedRoleIds = Regex.Matches(content, "<@&(\\d+)>")
|
|
|
|
|
|
|
|
.Cast<Match>()
|
|
|
|
|
|
|
|
.Select(m => m.Groups[1].Value)
|
|
|
|
|
|
|
|
.ExceptBlank()
|
|
|
|
|
|
|
|
.ToArray();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var mentionedRoleId in mentionedRoleIds)
|
|
|
|
|
|
|
|
{
|
|
|
|
{
|
|
|
|
var mentionedRole = _log.Mentionables.GetRole(mentionedRoleId);
|
|
|
|
var channel = _log.Mentionables.GetChannel(mentionNode.Id);
|
|
|
|
content = content.Replace($"<@&{mentionedRoleId}>", $"@{mentionedRole.Name}");
|
|
|
|
buffer.Append($"<span class=\"mention\">#{channel.Name.HtmlEncode()}</span>");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Custom emojis (<:name:id>)
|
|
|
|
else if (mentionNode.Type == MentionType.Role)
|
|
|
|
content = Regex.Replace(content, "<(:.*?:)\\d*>", "$1");
|
|
|
|
{
|
|
|
|
|
|
|
|
var role = _log.Mentionables.GetRole(mentionNode.Id);
|
|
|
|
return content;
|
|
|
|
buffer.Append($"<span class=\"mention\">@{role.Name.HtmlEncode()}</span>");
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private string FormatContent(string content, bool allowLinks = false)
|
|
|
|
else if (node is EmojiNode emojiNode)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
if (_format == ExportFormat.PlainText)
|
|
|
|
buffer.Append($"<img class=\"emoji\" title=\"{emojiNode.Name}\" src=\"https://cdn.discordapp.com/emojis/{emojiNode.Id}.png\" />");
|
|
|
|
return FormatContentPlainText(content);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (_format == ExportFormat.HtmlDark)
|
|
|
|
else if (node is LinkNode linkNode)
|
|
|
|
return FormatContentHtml(content, allowLinks);
|
|
|
|
{
|
|
|
|
|
|
|
|
buffer.Append($"<a href=\"{Uri.EscapeUriString(linkNode.Url)}\">{linkNode.Title.HtmlEncode()}</a>");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (_format == ExportFormat.HtmlLight)
|
|
|
|
return buffer.ToString();
|
|
|
|
return FormatContentHtml(content, allowLinks);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (_format == ExportFormat.Csv)
|
|
|
|
private string FormatMarkdownHtml(string input)
|
|
|
|
return FormatContentCsv(content);
|
|
|
|
=> FormatMarkdownHtml(MarkdownParser.Parse(input));
|
|
|
|
|
|
|
|
|
|
|
|
throw new ArgumentOutOfRangeException(nameof(_format));
|
|
|
|
private string FormatMarkdown(string input)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
return _format == ExportFormat.HtmlDark || _format == ExportFormat.HtmlLight
|
|
|
|
|
|
|
|
? FormatMarkdownHtml(input)
|
|
|
|
|
|
|
|
: FormatMarkdownPlainText(input);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public ScriptObject GetScriptObject()
|
|
|
|
public ScriptObject GetScriptObject()
|
|
|
@ -350,7 +243,7 @@ namespace DiscordChatExporter.Core.Services
|
|
|
|
// Create instance
|
|
|
|
// Create instance
|
|
|
|
var scriptObject = new ScriptObject();
|
|
|
|
var scriptObject = new ScriptObject();
|
|
|
|
|
|
|
|
|
|
|
|
// Import chat log
|
|
|
|
// Import model
|
|
|
|
scriptObject.SetValue("Model", _log, true);
|
|
|
|
scriptObject.SetValue("Model", _log, true);
|
|
|
|
|
|
|
|
|
|
|
|
// Import functions
|
|
|
|
// Import functions
|
|
|
@ -358,8 +251,7 @@ namespace DiscordChatExporter.Core.Services
|
|
|
|
scriptObject.Import(nameof(Format), new Func<IFormattable, string, string>(Format));
|
|
|
|
scriptObject.Import(nameof(Format), new Func<IFormattable, string, string>(Format));
|
|
|
|
scriptObject.Import(nameof(FormatDate), new Func<DateTime, string>(FormatDate));
|
|
|
|
scriptObject.Import(nameof(FormatDate), new Func<DateTime, string>(FormatDate));
|
|
|
|
scriptObject.Import(nameof(FormatFileSize), new Func<long, string>(FormatFileSize));
|
|
|
|
scriptObject.Import(nameof(FormatFileSize), new Func<long, string>(FormatFileSize));
|
|
|
|
scriptObject.Import(nameof(FormatColor), new Func<Color, string>(FormatColor));
|
|
|
|
scriptObject.Import(nameof(FormatMarkdown), new Func<string, string>(FormatMarkdown));
|
|
|
|
scriptObject.Import(nameof(FormatContent), new Func<string, bool, string>(FormatContent));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return scriptObject;
|
|
|
|
return scriptObject;
|
|
|
|
}
|
|
|
|
}
|
|
|
|