/// <summary>
        /// Converts the Discord.Net IMessage, downloads atatchments, and writes.
        /// </summary>
        /// <param name="message">The Discord.Net IMessage to be written.</param>
        /// <returns>The CsvMessage version of the Discord.Net IMessage.</returns>
        private CsvMessage ConvertMessageAndWrite(IMessage message)
        {
            DownloadResults attachments = DownloadAttachments(message);
            CsvMessage      msg         = new CsvMessage(message, attachments, _timeZone);

            _csv.WriteRecord(msg);
            _csv.NextRecord();
            Console.WriteLine($"Wrote message #{_msgWritten}");

            _msgWritten++;
            return(msg);
        }
 /// <summary>
 /// Creates a CsvMessage from a Discord.Net IMessage.
 /// </summary>
 /// <param name="message">A Discord.Net IMessage.</param>
 /// <param name="attachments">Result of each Discord attachment download.</param>
 /// <param name="timeZone">The time zone to format the timestamps to.</param>
 public CsvMessage(IMessage message, DownloadResults attachments, TimeSpan timeZone)
 {
     Time              = ConvertTimeAndFormat(message.Timestamp, timeZone);
     User              = $"{message.Author.Username}#{message.Author.Discriminator}";
     Message           = message.Content;
     Embed             = JsonSerializer.Serialize(message.Embeds, JSON_OPTIONS);
     Attachments       = JsonSerializer.Serialize(attachments.Success, JSON_OPTIONS);
     FailedAttachments = JsonSerializer.Serialize(attachments.Failed, JSON_OPTIONS);
     LastEdited        = ConvertTimeAndFormat(message.EditedTimestamp, timeZone);
     Pinned            = message.IsPinned.ToString();
     Tts = message.IsTTS.ToString();
     Id  = message.Id;
 }
 /// <summary>
 /// Calls CsvMessage(IMessage message, DownloadResults attachments, TimeSpan timeZone),
 /// but converting the TimeZone string to a TimeSpan.
 /// </summary>
 /// <param name="message">A Discord.Net IMessage.</param>
 /// <param name="attachments">Result of each Discord attachment download.</param>
 /// <param name="timeZone">The time zone to format the timestamps to, as a string.</param>
 public CsvMessage(IMessage message, DownloadResults attachments, string timeZone) :
     this(message, attachments, TimeSpan.Parse(timeZone.Replace("+", ""))) // time zone should not have a +
 {
     // empty
 }
        /// <summary>
        /// Downloads the attachment(s) that are part of the message.
        /// Note the downloads are not async.
        /// </summary>
        /// <param name="message">The message with attachments.</param>
        /// <returns>Results of all attachments in this message.</returns>
        private DownloadResults DownloadAttachments(IMessage message)
        {
            DownloadResults results = new DownloadResults();

            if (message.Attachments.Count == 0)
            {
                return(results);
            }

            if (!_includeAttachments)
            {
                foreach (IAttachment attachment in message.Attachments)
                {
                    results.Failed.Add(attachment.Filename);
                }

                return(results);
            }

            string path = Path.Join(_path, FILES_DIR, message.Id.ToString());

            Directory.CreateDirectory(path);

            uint count = 0;

            foreach (IAttachment attachment in message.Attachments)
            {
                string filePath = Path.Join(path, attachment.Filename);
                string subdir   = Path.Join(FILES_DIR, attachment.Filename); // for writing attachments/msgId/filename for easier reading in tsv

                // basic duplicate checking/renaming just in case...
                // but i think shouldn't be possible to have attachments with duplicate file names
                while (File.Exists(filePath))
                {
                    count++;
                    filePath += $"_{count}";
                    subdir   += $"_{count}";
                }

                // attempt proxy url download first
                try
                {
                    using (WebClient client = new WebClient())
                    {
                        client.DownloadFile(attachment.ProxyUrl, filePath);
                        results.Success.Add(attachment.Filename);
                    }
                    continue;
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"An error occurred while downloading {attachment.ProxyUrl}, trying another URL.");
                }

                // fallback to original url if cdn failed, which does seem to happen
                try
                {
                    using (WebClient client = new WebClient())
                    {
                        client.DownloadFile(attachment.Url, filePath);
                        results.Success.Add(attachment.Filename);
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"An error occurred while downloading {attachment.Url}. This attachment will be skipped.");
                    results.Failed.Add(attachment.Filename);
                }
            }

            return(results);
        }