示例#1
0
        public async Task ExportAsync(string token, string channelId, string filePath, ExportFormat format, DateTime?from,
                                      DateTime?to)
        {
            // Get channel and guild
            var channel = await _dataService.GetChannelAsync(token, channelId);

            var guild = channel.GuildId == Guild.DirectMessages.Id
                ? Guild.DirectMessages
                : await _dataService.GetGuildAsync(token, channel.GuildId);

            // Generate file path if not set
            if (filePath.IsBlank())
            {
                filePath = $"{guild.Name} - {channel.Name}.{format.GetFileExtension()}"
                           .Replace(Path.GetInvalidFileNameChars(), '_');
            }

            // Get messages
            var messages = await _dataService.GetChannelMessagesAsync(token, channelId, from, to);

            // Group them
            var messageGroups = _messageGroupService.GroupMessages(messages);

            // Create log
            var log = new ChannelChatLog(guild, channel, messageGroups, messages.Count);

            // Export
            await _exportService.ExportAsync(format, filePath, log);
        }
        public async Task ExportAsync(ExportFormat format, string filePath, ChannelChatLog log)
        {
            using (var output = File.CreateText(filePath))
            {
                var sharedCss = Assembly.GetExecutingAssembly()
                                .GetManifestResourceString("DiscordChatExporter.Core.Resources.ExportService.Shared.css");

                if (format == ExportFormat.PlainText)
                {
                    await ExportAsPlainTextAsync(log, output);
                }
                else if (format == ExportFormat.HtmlDark)
                {
                    var css = Assembly.GetExecutingAssembly()
                              .GetManifestResourceString("DiscordChatExporter.Core.Resources.ExportService.DarkTheme.css");
                    await ExportAsHtmlAsync(log, output, $"{sharedCss}\n{css}");
                }
                else if (format == ExportFormat.HtmlLight)
                {
                    var css = Assembly.GetExecutingAssembly()
                              .GetManifestResourceString("DiscordChatExporter.Core.Resources.ExportService.LightTheme.css");
                    await ExportAsHtmlAsync(log, output, $"{sharedCss}\n{css}");
                }
                else if (format == ExportFormat.Csv)
                {
                    await ExportAsCsvAsync(log, output);
                }

                else
                {
                    throw new ArgumentOutOfRangeException(nameof(format));
                }
            }
        }
        private async Task ExportAsTextAsync(string filePath, ChannelChatLog log)
        {
            using (var writer = new StreamWriter(filePath, false, Encoding.UTF8, 128 * 1024))
            {
                // Generation info
                await writer.WriteLineAsync("https://github.com/Tyrrrz/DiscordChatExporter");

                await writer.WriteLineAsync();

                // Guild and channel info
                await writer.WriteLineAsync('='.Repeat(48));

                await writer.WriteLineAsync($"Guild: {log.Guild.Name}");

                await writer.WriteLineAsync($"Channel: {log.Channel.Name}");

                await writer.WriteLineAsync($"Topic: {log.Channel.Topic}");

                await writer.WriteLineAsync($"Messages: {log.TotalMessageCount:N0}");

                await writer.WriteLineAsync('='.Repeat(48));

                await writer.WriteLineAsync();

                // Chat log
                foreach (var group in log.MessageGroups)
                {
                    var timeStampFormatted = group.TimeStamp.ToString(_settingsService.DateFormat);
                    await writer.WriteLineAsync($"{group.Author.FullyQualifiedName}  [{timeStampFormatted}]");

                    // Messages
                    foreach (var message in group.Messages)
                    {
                        // Content
                        if (message.Content.IsNotBlank())
                        {
                            var contentFormatted = FormatMessageContentText(message);
                            await writer.WriteLineAsync(contentFormatted);
                        }

                        // Attachments
                        foreach (var attachment in message.Attachments)
                        {
                            await writer.WriteLineAsync(attachment.Url);
                        }
                    }

                    await writer.WriteLineAsync();
                }
            }
        }
        private async Task ExportAsPlainTextAsync(ChannelChatLog log, TextWriter output)
        {
            // Generation info
            await output.WriteLineAsync("https://github.com/Tyrrrz/DiscordChatExporter");

            await output.WriteLineAsync();

            // Guild and channel info
            await output.WriteLineAsync('='.Repeat(48));

            await output.WriteLineAsync($"Guild: {log.Guild.Name}");

            await output.WriteLineAsync($"Channel: {log.Channel.Name}");

            await output.WriteLineAsync($"Topic: {log.Channel.Topic}");

            await output.WriteLineAsync($"Messages: {log.TotalMessageCount:N0}");

            await output.WriteLineAsync('='.Repeat(48));

            await output.WriteLineAsync();

            // Chat log
            foreach (var group in log.MessageGroups)
            {
                var timeStampFormatted = group.TimeStamp.ToString(_settingsService.DateFormat);
                await output.WriteLineAsync($"{group.Author.FullName}  [{timeStampFormatted}]");

                // Messages
                foreach (var message in group.Messages)
                {
                    // Content
                    if (message.Content.IsNotBlank())
                    {
                        var contentFormatted = FormatMessageContentPlainText(message);
                        await output.WriteLineAsync(contentFormatted);
                    }

                    // Attachments
                    foreach (var attachment in message.Attachments)
                    {
                        await output.WriteLineAsync(attachment.Url);
                    }
                }

                await output.WriteLineAsync();
            }
        }
        public Task ExportAsync(ExportFormat format, string filePath, ChannelChatLog log)
        {
            if (format == ExportFormat.PlainText)
            {
                return(ExportAsTextAsync(filePath, log));
            }
            if (format == ExportFormat.HtmlDark)
            {
                var css = AssemblyHelper.GetResourceString("DiscordChatExporter.Core.Resources.ExportService.DarkTheme.css");
                return(ExportAsHtmlAsync(filePath, log, css));
            }
            if (format == ExportFormat.HtmlLight)
            {
                var css = AssemblyHelper.GetResourceString("DiscordChatExporter.Core.Resources.ExportService.LightTheme.css");
                return(ExportAsHtmlAsync(filePath, log, css));
            }

            throw new ArgumentOutOfRangeException(nameof(format));
        }
示例#6
0
        public Task ExportAsync(ExportFormat format, string filePath, ChannelChatLog log)
        {
            if (format == ExportFormat.PlainText)
            {
                return(ExportAsTextAsync(filePath, log));
            }
            if (format == ExportFormat.HtmlDark)
            {
                var css = Program.GetResourceString("DiscordChatExporter.Resources.ExportService.DarkTheme.css");
                return(ExportAsHtmlAsync(filePath, log, css));
            }
            if (format == ExportFormat.HtmlLight)
            {
                var css = Program.GetResourceString("DiscordChatExporter.Resources.ExportService.LightTheme.css");
                return(ExportAsHtmlAsync(filePath, log, css));
            }

            throw new NotImplementedException();
        }
        private async Task ExportAsCsvAsync(ChannelChatLog log, TextWriter output)
        {
            using (var writer = new CsvWriter(output))
            {
                // Headers
                writer.WriteField("Author");
                writer.WriteField("Date");
                writer.WriteField("Content");
                writer.WriteField("Attachments");
                await writer.NextRecordAsync();

                // Chat log
                foreach (var group in log.MessageGroups)
                {
                    // Messages
                    foreach (var msg in group.Messages)
                    {
                        // Author
                        writer.WriteField(msg.Author.FullName);

                        // Date
                        var timeStampFormatted = msg.TimeStamp.ToString(_settingsService.DateFormat);
                        writer.WriteField(timeStampFormatted);

                        // Content
                        var contentFormatted = msg.Content.IsNotBlank() ? FormatMessageContentCsv(msg) : null;
                        writer.WriteField(contentFormatted);

                        // Attachments
                        var attachmentsFormatted = msg.Attachments.Select(a => a.Url).JoinToString(",");
                        writer.WriteField(attachmentsFormatted);

                        await writer.NextRecordAsync();
                    }
                }
            }
        }
示例#8
0
        private async Task ExportAsHtmlAsync(ChannelChatLog log, TextWriter output, string css)
        {
            // Generation info
            await output.WriteLineAsync("<!-- https://github.com/Tyrrrz/DiscordChatExporter -->");

            // Html start
            await output.WriteLineAsync("<!DOCTYPE html>");

            await output.WriteLineAsync("<html lang=\"en\">");

            // HEAD
            await output.WriteLineAsync("<head>");

            await output.WriteLineAsync($"<title>{log.Guild.Name} - {log.Channel.Name}</title>");

            await output.WriteLineAsync("<meta charset=\"utf-8\" />");

            await output.WriteLineAsync("<meta name=\"viewport\" content=\"width=device-width\" />");

            await output.WriteLineAsync($"<style>{css}</style>");

            await output.WriteLineAsync("</head>");

            // Body start
            await output.WriteLineAsync("<body>");

            // Guild and channel info
            await output.WriteLineAsync("<div id=\"info\">");

            await output.WriteLineAsync("<div class=\"info-left\">");

            await output.WriteLineAsync($"<img class=\"guild-icon\" src=\"{log.Guild.IconUrl}\" />");

            await output.WriteLineAsync("</div>"); // info-left

            await output.WriteLineAsync("<div class=\"info-right\">");

            await output.WriteLineAsync($"<div class=\"guild-name\">{log.Guild.Name}</div>");

            await output.WriteLineAsync($"<div class=\"channel-name\">{log.Channel.Name}</div>");

            await output.WriteLineAsync($"<div class=\"channel-topic\">{log.Channel.Topic}</div>");

            await output.WriteLineAsync(
                $"<div class=\"channel-messagecount\">{log.TotalMessageCount:N0} messages</div>");

            await output.WriteLineAsync("</div>"); // info-right

            await output.WriteLineAsync("</div>"); // info

            // Chat log
            await output.WriteLineAsync("<div id=\"log\">");

            foreach (var group in log.MessageGroups)
            {
                await output.WriteLineAsync("<div class=\"msg\">");

                await output.WriteLineAsync("<div class=\"msg-left\">");

                await output.WriteLineAsync($"<img class=\"msg-avatar\" src=\"{group.Author.AvatarUrl}\" />");

                await output.WriteLineAsync("</div>");

                await output.WriteLineAsync("<div class=\"msg-right\">");

                await output.WriteAsync(
                    $"<span class=\"msg-user\" title=\"{HtmlEncode(group.Author.FullName)}\">");

                await output.WriteAsync(HtmlEncode(group.Author.Name));

                await output.WriteLineAsync("</span>");

                var timeStampFormatted = HtmlEncode(group.TimeStamp.ToString(_settingsService.DateFormat));
                await output.WriteLineAsync($"<span class=\"msg-date\">{timeStampFormatted}</span>");

                // Messages
                foreach (var message in group.Messages)
                {
                    // Content
                    if (message.Content.IsNotBlank())
                    {
                        await output.WriteLineAsync("<div class=\"msg-content\">");

                        var contentFormatted = FormatMessageContentHtml(message);
                        await output.WriteAsync(contentFormatted);

                        // Edited timestamp
                        if (message.EditedTimeStamp != null)
                        {
                            var editedTimeStampFormatted =
                                HtmlEncode(message.EditedTimeStamp.Value.ToString(_settingsService.DateFormat));
                            await output.WriteAsync(
                                $"<span class=\"msg-edited\" title=\"{editedTimeStampFormatted}\">(edited)</span>");
                        }

                        await output.WriteLineAsync("</div>"); // msg-content
                    }

                    // Attachments
                    foreach (var attachment in message.Attachments)
                    {
                        if (attachment.Type == AttachmentType.Image)
                        {
                            await output.WriteLineAsync("<div class=\"msg-attachment\">");

                            await output.WriteLineAsync($"<a href=\"{attachment.Url}\">");

                            await output.WriteLineAsync($"<img class=\"msg-attachment\" src=\"{attachment.Url}\" />");

                            await output.WriteLineAsync("</a>");

                            await output.WriteLineAsync("</div>");
                        }
                        else
                        {
                            await output.WriteLineAsync("<div class=\"msg-attachment\">");

                            await output.WriteLineAsync($"<a href=\"{attachment.Url}\">");

                            var fileSizeFormatted = FormatFileSize(attachment.FileSize);
                            await output.WriteLineAsync($"Attachment: {attachment.FileName} ({fileSizeFormatted})");

                            await output.WriteLineAsync("</a>");

                            await output.WriteLineAsync("</div>");
                        }
                    }

                    // Embeds
                    foreach (var embed in message.Embeds)
                    {
                        var contentFormatted = FormatEmbedHtml(embed);
                        await output.WriteAsync(contentFormatted);
                    }
                }

                await output.WriteLineAsync("</div>"); // msg-right

                await output.WriteLineAsync("</div>"); // msg
            }

            await output.WriteLineAsync("</div>"); // log

            await output.WriteLineAsync("</body>");

            await output.WriteLineAsync("</html>");
        }
示例#9
0
        private static async Task ExportAll(ExportAllOptions options)
        {
            var token   = options.Token;
            var format  = options.Format;
            var year    = options.Year;
            var month   = options.Month;
            var allUpTo = options.AllUpTo;

            var settingsService     = new SettingsService();
            var dataService         = new DataService();
            var exportService       = new ExportService(settingsService);
            var messageGroupService = new MessageGroupService(settingsService);

            DateTime?from = allUpTo ? null : (DateTime?)new DateTime(year, month, 1);
            DateTime to   = new DateTime(year, month, 1).AddMonths(1);

            Console.WriteLine("Retrieving servers...");
            var guilds = new List <Guild>(await dataService.GetUserGuildsAsync(token));

            guilds.Add(Guild.DirectMessages);
            foreach (var guild in guilds)
            {
                Console.WriteLine("Retrieving channels for " + guild.Name + "...");
                IReadOnlyList <Channel> channels;
                while (true)
                {
                    try
                    {
                        if (guild == Guild.DirectMessages)
                        {
                            channels = await dataService.GetDirectMessageChannelsAsync(token);
                        }
                        else
                        {
                            channels = await dataService.GetGuildChannelsAsync(token, guild.Id);
                        }

                        break;
                    }
                    catch (Exception e)
                    {
                        Console.WriteLine(e);

                        // If we get an error, it might be Discord asking us to kindly back off.
                        // Give them a short break.
                        Thread.Sleep(10000);
                        throw;
                    }
                }
                foreach (var channel in channels)
                {
                    var filePath = "Export/" + guild.Name + "/" + channel.Name + "/" + year + "-" + month + (format == ExportFormat.PlainText ? ".txt" : ".html");

                    // Get messages
                    Console.WriteLine("Retrieving messages for " + guild.Name + ":" + channel.Name);

                    IReadOnlyList <Message> messages = null;
                    while (true)
                    {
                        try
                        {
                            messages = await dataService.GetChannelMessagesAsync(token, channel.Id, from, to);

                            break;
                        }
                        catch (HttpErrorStatusCodeException e)
                        {
                            // This indicates a channel we don't have access to.
                            if (e.StatusCode == HttpStatusCode.Forbidden)
                            {
                                break;
                            }

                            Console.WriteLine(e);

                            // If we get an error, it might be Discord asking us to kindly back off.
                            // Give them a short break.
                            Thread.Sleep(10000);
                        }
                        catch (Exception e)
                        {
                            Console.WriteLine(e);

                            // If we get an error, it might be Discord asking us to kindly back off.
                            // Give them a short break.
                            Thread.Sleep(10000);
                            throw;
                        }
                    }
                    if (messages == null)
                    {
                        continue;
                    }

                    // Group them
                    var messageGroups = messageGroupService.GroupMessages(messages);

                    // Create log
                    var log = new ChannelChatLog(guild, channel, messageGroups, messages.Count);

                    // Export
                    Console.WriteLine("Exporting messages...");
                    Directory.CreateDirectory("Export/" + guild.Name + "/" + channel.Name + "/");
                    await exportService.ExportAsync(format, filePath, log);
                }
            }
        }