using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; using DiscordChatExporter.Core.Discord.Data; using DiscordChatExporter.Core.Discord.Data.Embeds; using DiscordChatExporter.Core.Exporting.Writers.MarkdownVisitors; using Tyrrrz.Extensions; namespace DiscordChatExporter.Core.Exporting.Writers { internal class PlainTextMessageWriter : MessageWriter { private readonly TextWriter _writer; public PlainTextMessageWriter(Stream stream, ExportContext context) : base(stream, context) { _writer = new StreamWriter(stream); } private string FormatMarkdown(string? markdown) => PlainTextMarkdownVisitor.Format(Context, markdown ?? ""); private async ValueTask WriteMessageHeaderAsync(Message message) { // Timestamp & author await _writer.WriteAsync($"[{Context.FormatDate(message.Timestamp)}]"); await _writer.WriteAsync($" {message.Author.FullName}"); // Whether the message is pinned if (message.IsPinned) await _writer.WriteAsync(" (pinned)"); await _writer.WriteLineAsync(); } private async ValueTask WriteAttachmentsAsync(IReadOnlyList attachments) { if (!attachments.Any()) return; await _writer.WriteLineAsync("{Attachments}"); foreach (var attachment in attachments) await _writer.WriteLineAsync(await Context.ResolveMediaUrlAsync(attachment.Url)); await _writer.WriteLineAsync(); } private async ValueTask WriteEmbedsAsync(IReadOnlyList embeds) { foreach (var embed in embeds) { await _writer.WriteLineAsync("{Embed}"); if (!string.IsNullOrWhiteSpace(embed.Author?.Name)) await _writer.WriteLineAsync(embed.Author.Name); if (!string.IsNullOrWhiteSpace(embed.Url)) await _writer.WriteLineAsync(embed.Url); if (!string.IsNullOrWhiteSpace(embed.Title)) await _writer.WriteLineAsync(FormatMarkdown(embed.Title)); if (!string.IsNullOrWhiteSpace(embed.Description)) await _writer.WriteLineAsync(FormatMarkdown(embed.Description)); foreach (var field in embed.Fields) { if (!string.IsNullOrWhiteSpace(field.Name)) await _writer.WriteLineAsync(FormatMarkdown(field.Name)); if (!string.IsNullOrWhiteSpace(field.Value)) await _writer.WriteLineAsync(FormatMarkdown(field.Value)); } if (!string.IsNullOrWhiteSpace(embed.Thumbnail?.Url)) await _writer.WriteLineAsync(await Context.ResolveMediaUrlAsync(embed.Thumbnail.ProxyUrl ?? embed.Thumbnail.Url)); if (!string.IsNullOrWhiteSpace(embed.Image?.Url)) await _writer.WriteLineAsync(await Context.ResolveMediaUrlAsync(embed.Image.ProxyUrl ?? embed.Image.Url)); if (!string.IsNullOrWhiteSpace(embed.Footer?.Text)) await _writer.WriteLineAsync(embed.Footer.Text); await _writer.WriteLineAsync(); } } private async ValueTask WriteReactionsAsync(IReadOnlyList reactions) { if (!reactions.Any()) return; await _writer.WriteLineAsync("{Reactions}"); foreach (var reaction in reactions) { await _writer.WriteAsync(reaction.Emoji.Name); if (reaction.Count > 1) await _writer.WriteAsync($" ({reaction.Count})"); await _writer.WriteAsync(' '); } await _writer.WriteLineAsync(); } public override async ValueTask WritePreambleAsync() { await _writer.WriteLineAsync('='.Repeat(62)); await _writer.WriteLineAsync($"Guild: {Context.Request.Guild.Name}"); await _writer.WriteLineAsync($"Channel: {Context.Request.Channel.Category.Name} / {Context.Request.Channel.Name}"); if (!string.IsNullOrWhiteSpace(Context.Request.Channel.Topic)) await _writer.WriteLineAsync($"Topic: {Context.Request.Channel.Topic}"); if (Context.Request.After is not null) await _writer.WriteLineAsync($"After: {Context.FormatDate(Context.Request.After.Value.ToDate())}"); if (Context.Request.Before is not null) await _writer.WriteLineAsync($"Before: {Context.FormatDate(Context.Request.Before.Value.ToDate())}"); await _writer.WriteLineAsync('='.Repeat(62)); await _writer.WriteLineAsync(); } public override async ValueTask WriteMessageAsync(Message message) { await base.WriteMessageAsync(message); // Header await WriteMessageHeaderAsync(message); // Content if (!string.IsNullOrWhiteSpace(message.Content)) await _writer.WriteLineAsync(FormatMarkdown(message.Content)); await _writer.WriteLineAsync(); // Attachments, embeds, reactions await WriteAttachmentsAsync(message.Attachments); await WriteEmbedsAsync(message.Embeds); await WriteReactionsAsync(message.Reactions); await _writer.WriteLineAsync(); } public override async ValueTask WritePostambleAsync() { await _writer.WriteLineAsync('='.Repeat(62)); await _writer.WriteLineAsync($"Exported {MessagesWritten:N0} message(s)"); await _writer.WriteLineAsync('='.Repeat(62)); } public override async ValueTask DisposeAsync() { await _writer.DisposeAsync(); await base.DisposeAsync(); } } }