Add dark theme and theme switch (#4)

* Add dark theme

* Refactor a bit

* Add back Light theme and add a switch

* Update readme
pull/7/head
Alexey Golub 7 years ago committed by GitHub
parent 22d7dd43ec
commit 95d581797b

@ -9,7 +9,9 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<EmbeddedResource Include="Services\ExportTemplate.html" /> <EmbeddedResource Include="Resources\HtmlExportService\LightTheme.css" />
<EmbeddedResource Include="Resources\HtmlExportService\DarkTheme.css" />
<EmbeddedResource Include="Resources\HtmlExportService\Template.html" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

@ -6,10 +6,13 @@
public string ChannelId { get; } public string ChannelId { get; }
public Options(string token, string channelId) public Theme Theme { get; }
public Options(string token, string channelId, Theme theme)
{ {
Token = token; Token = token;
ChannelId = channelId; ChannelId = channelId;
Theme = theme;
} }
} }
} }

@ -0,0 +1,8 @@
namespace DiscordChatExporter.Models
{
public enum Theme
{
Light,
Dark
}
}

@ -11,7 +11,7 @@ namespace DiscordChatExporter
public static class Program public static class Program
{ {
private static readonly DiscordApiService DiscordApiService = new DiscordApiService(); private static readonly DiscordApiService DiscordApiService = new DiscordApiService();
private static readonly ExportService ExportService = new ExportService(); private static readonly HtmlExportService HtmlExportService = new HtmlExportService();
private static Options GetOptions(string[] args) private static Options GetOptions(string[] args)
{ {
@ -37,8 +37,11 @@ namespace DiscordChatExporter
if (token.IsBlank() || channelId.IsBlank()) if (token.IsBlank() || channelId.IsBlank())
throw new ArgumentException("Some or all required command line arguments are missing"); throw new ArgumentException("Some or all required command line arguments are missing");
// Exract optional arguments
var theme = argsDic.GetOrDefault("theme").ParseEnumOrDefault<Theme>();
// Create option set // Create option set
return new Options(token, channelId); return new Options(token, channelId, theme);
} }
private static async Task MainAsync(string[] args) private static async Task MainAsync(string[] args)
@ -53,7 +56,7 @@ namespace DiscordChatExporter
// Export // Export
Console.WriteLine("Exporting messages..."); Console.WriteLine("Exporting messages...");
ExportService.Export($"{options.ChannelId}.html", chatLog); HtmlExportService.Export($"{options.ChannelId}.html", chatLog, options.Theme);
} }
public static void Main(string[] args) public static void Main(string[] args)

@ -0,0 +1,105 @@
body {
font-family: Whitney, Helvetica Neue, Helvetica, Arial, sans-serif;
font-size: 16px;
background-color: #36393E;
}
a {
text-decoration: none;
color: #0096CF;
}
a:hover {
text-decoration: underline;
}
div.pre, span.pre {
font-family: Consolas, Courier New, Courier, Monospace;
padding-right: 2px;
padding-left: 2px;
background-color: #2F3136;
}
div#info {
max-width: 100%;
margin-bottom: 20px;
color: rgba(255, 255, 255, 0.7);
}
div#log {
max-width: 100%;
}
div.msg {
display: flex;
margin-right: 10px;
margin-left: 10px;
padding-top: 15px;
padding-bottom: 15px;
border-top: 1px solid rgba(255, 255, 255, 0.04);
}
div.msg-avatar {
width: 40px;
height: 40px;
}
img.msg-avatar {
width: 40px;
height: 40px;
border-radius: 50%;
}
div.msg-body {
margin-left: 20px;
flex: 1;
}
span.msg-user {
font-size: 1rem;
color: #FFFFFF;
}
span.msg-date {
font-size: .75rem;
margin-left: 5px;
color: rgba(255, 255, 255, 0.2);
}
span.msg-edited {
font-size: .8rem;
margin-left: 5px;
color: rgba(255, 255, 255, 0.2);
}
div.msg-content {
font-size: .9375rem;
padding-top: 5px;
color: rgba(255, 255, 255, 0.7);
}
div.msg-attachment {
margin-top: 5px;
margin-bottom: 5px;
}
img.msg-attachment {
max-width: 50%;
max-height: 500px;
}

@ -0,0 +1,105 @@
body {
font-family: Whitney, Helvetica Neue, Helvetica, Arial, sans-serif;
font-size: 16px;
background-color: #FFFFFF;
}
a {
text-decoration: none;
color: #00B0F4;
}
a:hover {
text-decoration: underline;
}
div.pre, span.pre {
font-family: Consolas, Courier New, Courier, Monospace;
padding-right: 2px;
padding-left: 2px;
background-color: #F9F9F9;
}
div#info {
max-width: 100%;
margin-bottom: 20px;
color: #737F8D;
}
div#log {
max-width: 100%;
}
div.msg {
display: flex;
margin-right: 10px;
margin-left: 10px;
padding-top: 15px;
padding-bottom: 15px;
border-top: 1px solid #ECEEEF;
}
div.msg-avatar {
width: 40px;
height: 40px;
}
img.msg-avatar {
width: 40px;
height: 40px;
border-radius: 50%;
}
div.msg-body {
margin-left: 20px;
flex: 1;
}
span.msg-user {
font-size: 1rem;
color: #2F3136;
}
span.msg-date {
font-size: .75rem;
margin-left: 5px;
color: #99AAB5;
}
span.msg-edited {
font-size: .8rem;
margin-left: 5px;
color: #99AAB5;
}
div.msg-content {
font-size: .9375rem;
padding-top: 5px;
color: #737F8D;
}
div.msg-attachment {
margin-top: 5px;
margin-bottom: 5px;
}
img.msg-attachment {
max-width: 50%;
max-height: 500px;
}

@ -0,0 +1,17 @@
<!-- This chat log was automatically generated by DiscordChatExporter (https://github.com/Tyrrrz/DiscordChatExporter) -->
<!DOCTYPE html>
<html lang="en">
<head>
<title>Discord Chat Log</title>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style id="theme">
</style>
</head>
<body>
<div id="info"></div>
<div id="log"></div>
</body>
</html>

@ -1,122 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Discord Chat Log</title>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
body {
font-family: Whitney, Helvetica Neue, Helvetica, Arial, sans-serif;
font-size: 15px;
background-color: #fff;
}
a {
text-decoration: none;
color: #37bcf7;
}
a:hover {
text-decoration: underline;
}
div.pre, span.pre {
font-family: Consolas, Courier New, Courier, Monospace;
padding-right: 2px;
padding-left: 2px;
background-color: #f9f9f9;
}
div#info {
max-width: 100%;
margin-bottom: 20px;
color: #939799;
}
div#log {
max-width: 100%;
}
div.msg {
display: flex;
margin-right: 10px;
margin-left: 10px;
padding-top: 15px;
padding-bottom: 15px;
border-top: 1px solid #eceeef;
}
div.msg-avatar {
width: 40px;
height: 40px;
}
img.msg-avatar {
width: 40px;
height: 40px;
border-radius: 50%;
}
div.msg-body {
margin-left: 15px;
flex: 1;
}
span.msg-user {
font-size: 1.15em;
color: #2f3136;
}
span.msg-date {
font-size: .8em;
font-weight: 200;
margin-left: 5px;
color: #b7bcbf;
}
span.msg-edited {
font-size: .8em;
font-weight: 200;
margin-left: 5px;
color: #b7bcbf;
}
div.msg-content {
padding-top: 5px;
color: #939799;
}
div.msg-attachment {
margin-top: 5px;
margin-bottom: 5px;
}
img.msg-attachment {
max-width: 50%;
max-height: 500px;
}
</style>
</head>
<body>
<div id="info"></div>
<div id="log"></div>
</body>
</html>

@ -1,6 +1,8 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Resources;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using DiscordChatExporter.Models; using DiscordChatExporter.Models;
using HtmlAgilityPack; using HtmlAgilityPack;
@ -8,13 +10,18 @@ using Tyrrrz.Extensions;
namespace DiscordChatExporter.Services namespace DiscordChatExporter.Services
{ {
public class ExportService public class HtmlExportService
{ {
private HtmlDocument GetTemplate() private HtmlDocument GetTemplate()
{ {
const string templateName = "DiscordChatExporter.Services.ExportTemplate.html"; string templateName = "DiscordChatExporter.Resources.HtmlExportService.Template.html";
var assembly = Assembly.GetExecutingAssembly(); var assembly = Assembly.GetExecutingAssembly();
using (var stream = assembly.GetManifestResourceStream(templateName)) var stream = assembly.GetManifestResourceStream(templateName);
if (stream == null)
throw new MissingManifestResourceException("Could not find template resource");
using (stream)
{ {
var doc = new HtmlDocument(); var doc = new HtmlDocument();
doc.Load(stream); doc.Load(stream);
@ -22,6 +29,22 @@ namespace DiscordChatExporter.Services
} }
} }
private string GetStyle(Theme theme)
{
string styleName = $"DiscordChatExporter.Resources.HtmlExportService.{theme}Theme.css";
var assembly = Assembly.GetExecutingAssembly();
var stream = assembly.GetManifestResourceStream(styleName);
if (stream == null)
throw new MissingManifestResourceException("Could not find theme style resource");
using (stream)
using (var reader = new StreamReader(stream))
{
return reader.ReadToEnd();
}
}
private IEnumerable<MessageGroup> GroupMessages(IEnumerable<Message> messages) private IEnumerable<MessageGroup> GroupMessages(IEnumerable<Message> messages)
{ {
var result = new List<MessageGroup>(); var result = new List<MessageGroup>();
@ -99,9 +122,14 @@ namespace DiscordChatExporter.Services
return content; return content;
} }
public void Export(string filePath, ChatLog chatLog) public void Export(string filePath, ChatLog chatLog, Theme theme)
{ {
var doc = GetTemplate(); var doc = GetTemplate();
string style = GetStyle(theme);
// Set theme
var themeHtml = doc.GetElementbyId("theme");
themeHtml.InnerHtml = style;
// Info // Info
var infoHtml = doc.GetElementbyId("info"); var infoHtml = doc.GetElementbyId("info");

@ -12,7 +12,7 @@ Command line executable that can export [Discord](https://discordapp.com) channe
## Features ## Features
- Produces output styled similar to the Discord's light theme - Supports both dark and light theme
- Displays user avatars - Displays user avatars
- Groups messages by author and time - Groups messages by author and time
- Handles Discord markdown characters - Handles Discord markdown characters
@ -23,18 +23,18 @@ Command line executable that can export [Discord](https://discordapp.com) channe
## Usage ## Usage
The program expects an access token and channel ID as parameters. The program expects an access token and channel ID as parameters. At minimum, the execution should look like this:
`DiscordChatExporter.exe /token:REkOTVqm9RWOTNOLCdiuMpWd.QiglBz.Lub0E0TZ1xX4ZxCtnwtpBhWt3v1 /channelId:459360869055190534` `DiscordChatExporter.exe /token:REkOTVqm9RWOTNOLCdiuMpWd.QiglBz.Lub0E0TZ1xX4ZxCtnwtpBhWt3v1 /channelId:459360869055190534`
#### Getting access token: #### Getting access token
- Open Discord desktop or web client - Open Discord desktop or web client
- Press `Ctrl+Shift+I` - Press `Ctrl+Shift+I`
- Navigate to `Application > Storage > Local Storage > https://discordapp.com` - Navigate to `Application > Storage > Local Storage > https://discordapp.com`
- Find the value for `token` and extract it - Find the value for `token` and extract it
#### Getting channel ID: #### Getting channel ID
- Open Discord desktop or web client - Open Discord desktop or web client
- Navigate to any DM or server channel - Navigate to any DM or server channel
@ -46,6 +46,10 @@ The program expects an access token and channel ID as parameters.
- If it's a server channel, the format looks like this: - If it's a server channel, the format looks like this:
`https://discordapp.com/channels/WHATEVER/CHANNEL_ID` `https://discordapp.com/channels/WHATEVER/CHANNEL_ID`
#### Optional arguments
- `/theme:[Dark/Light]` - sets the style of the output
## Libraries used ## Libraries used
- [HtmlAgilityPack](https://github.com/zzzprojects/html-agility-pack) - [HtmlAgilityPack](https://github.com/zzzprojects/html-agility-pack)

Loading…
Cancel
Save