mirror of
https://github.com/Tyrrrz/DiscordChatExporter.git
synced 2026-07-09 15:39:34 +02:00
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9df98b0405 | |||
| 0d2b9e8bc0 | |||
| 53cebd82eb | |||
| 37fcee2f21 | |||
| 047dccef7f | |||
| cb47902d10 | |||
| dca8b8ceb2 | |||
| a8e7c85b99 | |||
| edd51e0838 | |||
| 5e04accdff | |||
| 547bf0a463 | |||
| 948fab7c86 |
+47
-47
@@ -3,64 +3,64 @@ name: CD
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
tags:
|
tags:
|
||||||
- '*'
|
- "*"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2.3.3
|
uses: actions/checkout@v2.3.3
|
||||||
|
|
||||||
- name: Install .NET Core
|
- name: Install .NET
|
||||||
uses: actions/setup-dotnet@v1.7.2
|
uses: actions/setup-dotnet@v1.7.2
|
||||||
with:
|
with:
|
||||||
dotnet-version: 3.1.100
|
dotnet-version: 3.1.100
|
||||||
|
|
||||||
- name: Build & publish (CLI)
|
- name: Build & publish (CLI)
|
||||||
run: dotnet publish DiscordChatExporter.Cli/ -o DiscordChatExporter.Cli/bin/Publish/ --configuration Release
|
run: dotnet publish DiscordChatExporter.Cli/ -o DiscordChatExporter.Cli/bin/Publish/ --configuration Release
|
||||||
|
|
||||||
- name: Build & publish (GUI)
|
- name: Build & publish (GUI)
|
||||||
run: dotnet publish DiscordChatExporter.Gui/ -o DiscordChatExporter.Gui/bin/Publish/ --configuration Release
|
run: dotnet publish DiscordChatExporter.Gui/ -o DiscordChatExporter.Gui/bin/Publish/ --configuration Release
|
||||||
|
|
||||||
- name: Pack (CLI)
|
- name: Pack (CLI)
|
||||||
run: Compress-Archive -Path DiscordChatExporter.Cli/bin/Publish/* -DestinationPath DiscordChatExporter.Cli/bin/Publish/Archive.zip -Force
|
run: Compress-Archive -Path DiscordChatExporter.Cli/bin/Publish/* -DestinationPath DiscordChatExporter.Cli/bin/Publish/Archive.zip -Force
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
|
|
||||||
- name: Pack (GUI)
|
- name: Pack (GUI)
|
||||||
run: Compress-Archive -Path DiscordChatExporter.Gui/bin/Publish/* -DestinationPath DiscordChatExporter.Gui/bin/Publish/Archive.zip -Force
|
run: Compress-Archive -Path DiscordChatExporter.Gui/bin/Publish/* -DestinationPath DiscordChatExporter.Gui/bin/Publish/Archive.zip -Force
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
|
|
||||||
- name: Create release
|
- name: Create release
|
||||||
id: create_release
|
id: create_release
|
||||||
uses: actions/create-release@v1.0.1
|
uses: actions/create-release@v1.0.1
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
with:
|
with:
|
||||||
tag_name: ${{ github.ref }}
|
tag_name: ${{ github.ref }}
|
||||||
release_name: ${{ github.ref }}
|
release_name: ${{ github.ref }}
|
||||||
body: |
|
body: |
|
||||||
[Changelog](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/Changelog.md)
|
[Changelog](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/Changelog.md)
|
||||||
draft: false
|
draft: false
|
||||||
prerelease: false
|
prerelease: false
|
||||||
|
|
||||||
- name: Upload release asset (CLI)
|
- name: Upload release asset (CLI)
|
||||||
uses: actions/upload-release-asset@v1.0.2
|
uses: actions/upload-release-asset@v1.0.2
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
with:
|
with:
|
||||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||||
asset_path: DiscordChatExporter.Cli/bin/Publish/Archive.zip
|
asset_path: DiscordChatExporter.Cli/bin/Publish/Archive.zip
|
||||||
asset_name: DiscordChatExporter.CLI.zip
|
asset_name: DiscordChatExporter.CLI.zip
|
||||||
asset_content_type: application/zip
|
asset_content_type: application/zip
|
||||||
|
|
||||||
- name: Upload release asset (GUI)
|
- name: Upload release asset (GUI)
|
||||||
uses: actions/upload-release-asset@v1.0.2
|
uses: actions/upload-release-asset@v1.0.2
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
with:
|
with:
|
||||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||||
asset_path: DiscordChatExporter.Gui/bin/Publish/Archive.zip
|
asset_path: DiscordChatExporter.Gui/bin/Publish/Archive.zip
|
||||||
asset_name: DiscordChatExporter.zip
|
asset_name: DiscordChatExporter.zip
|
||||||
asset_content_type: application/zip
|
asset_content_type: application/zip
|
||||||
|
|||||||
+20
-20
@@ -7,28 +7,28 @@ jobs:
|
|||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2.3.3
|
uses: actions/checkout@v2.3.3
|
||||||
|
|
||||||
- name: Install .NET Core
|
- name: Install .NET
|
||||||
uses: actions/setup-dotnet@v1.7.2
|
uses: actions/setup-dotnet@v1.7.2
|
||||||
with:
|
with:
|
||||||
dotnet-version: 3.1.100
|
dotnet-version: 3.1.100
|
||||||
|
|
||||||
- name: Build & publish (CLI)
|
- name: Build & publish (CLI)
|
||||||
run: dotnet publish DiscordChatExporter.Cli/ -o DiscordChatExporter.Cli/bin/Publish/ --configuration Release
|
run: dotnet publish DiscordChatExporter.Cli/ -o DiscordChatExporter.Cli/bin/Publish/ --configuration Release
|
||||||
|
|
||||||
- name: Build & publish (GUI)
|
- name: Build & publish (GUI)
|
||||||
run: dotnet publish DiscordChatExporter.Gui/ -o DiscordChatExporter.Gui/bin/Publish/ --configuration Release
|
run: dotnet publish DiscordChatExporter.Gui/ -o DiscordChatExporter.Gui/bin/Publish/ --configuration Release
|
||||||
|
|
||||||
- name: Upload build artifacts (CLI)
|
- name: Upload build artifacts (CLI)
|
||||||
uses: actions/upload-artifact@v1
|
uses: actions/upload-artifact@v1
|
||||||
with:
|
with:
|
||||||
name: DiscordChatExporter.CLI
|
name: DiscordChatExporter.CLI
|
||||||
path: DiscordChatExporter.Cli/bin/Publish/
|
path: DiscordChatExporter.Cli/bin/Publish/
|
||||||
|
|
||||||
- name: Upload build artifact (GUI)
|
- name: Upload build artifact (GUI)
|
||||||
uses: actions/upload-artifact@v1
|
uses: actions/upload-artifact@v1
|
||||||
with:
|
with:
|
||||||
name: DiscordChatExporter
|
name: DiscordChatExporter
|
||||||
path: DiscordChatExporter.Gui/bin/Publish/
|
path: DiscordChatExporter.Gui/bin/Publish/
|
||||||
|
|||||||
@@ -1,3 +1,10 @@
|
|||||||
|
### v2.25 (23-Nov-2020)
|
||||||
|
|
||||||
|
- [JSON] Added `mentions` property to the output. This contains users referenced inside the message text, but doesn't include mentions in embeds. (Thanks [@Andrew Kolos](https://github.com/andrewkolos))
|
||||||
|
- [HTML] Added better previews for attached video and audio. They are now rendered using a browser native player element. The actual file path can still be retrieved by right clicking and selecting "Copy file path". (Thanks [@sas41](https://github.com/sas41))
|
||||||
|
- [CLI] Fixed an issue where using `--media` option without providing a rooted path via `--output` option triggered an error.
|
||||||
|
- [CLI] Fixed an issue where using `--reuse-media` option without also providing `--media` option was accepted, but didn't actually do anything. Now this produces an error instead. (Thanks [@Andrew Kolos](https://github.com/andrewkolos))
|
||||||
|
|
||||||
### v2.24 (24-Oct-2020)
|
### v2.24 (24-Oct-2020)
|
||||||
|
|
||||||
- [CLI] Updated to [CliFx v1.5](https://github.com/Tyrrrz/CliFx), which brings some UX improvements.
|
- [CLI] Updated to [CliFx v1.5](https://github.com/Tyrrrz/CliFx), which brings some UX improvements.
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using System.IO;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using CliFx;
|
using CliFx;
|
||||||
using CliFx.Attributes;
|
using CliFx.Attributes;
|
||||||
|
using CliFx.Exceptions;
|
||||||
using CliFx.Utilities;
|
using CliFx.Utilities;
|
||||||
using DiscordChatExporter.Domain.Discord.Models;
|
using DiscordChatExporter.Domain.Discord.Models;
|
||||||
using DiscordChatExporter.Domain.Exporting;
|
using DiscordChatExporter.Domain.Exporting;
|
||||||
@@ -80,5 +81,17 @@ namespace DiscordChatExporter.Cli.Commands.Base
|
|||||||
var channel = await GetDiscordClient().GetChannelAsync(channelId);
|
var channel = await GetDiscordClient().GetChannelAsync(channelId);
|
||||||
await ExportAsync(console, channel);
|
await ExportAsync(console, channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override ValueTask ExecuteAsync(IConsole console)
|
||||||
|
{
|
||||||
|
if (ShouldReuseMedia && !ShouldDownloadMedia)
|
||||||
|
{
|
||||||
|
throw new CommandException(
|
||||||
|
"The --reuse-media option cannot be used without the --media option."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return default;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -12,7 +12,10 @@ namespace DiscordChatExporter.Cli.Commands
|
|||||||
Description = "Channel ID.")]
|
Description = "Channel ID.")]
|
||||||
public string ChannelId { get; set; } = "";
|
public string ChannelId { get; set; } = "";
|
||||||
|
|
||||||
public override async ValueTask ExecuteAsync(IConsole console) =>
|
public override async ValueTask ExecuteAsync(IConsole console)
|
||||||
|
{
|
||||||
|
await base.ExecuteAsync(console);
|
||||||
await ExportAsync(console, ChannelId);
|
await ExportAsync(console, ChannelId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using DiscordChatExporter.Domain.Discord.Models.Common;
|
using DiscordChatExporter.Domain.Discord.Models.Common;
|
||||||
using DiscordChatExporter.Domain.Internal.Extensions;
|
using DiscordChatExporter.Domain.Internal.Extensions;
|
||||||
@@ -20,11 +20,14 @@ namespace DiscordChatExporter.Domain.Discord.Models
|
|||||||
|
|
||||||
public int? Height { get; }
|
public int? Height { get; }
|
||||||
|
|
||||||
public bool IsImage =>
|
public bool IsImage => ImageFileExtensions.Contains(Path.GetExtension(FileName));
|
||||||
ImageFileExtensions.Contains(Path.GetExtension(FileName), StringComparer.OrdinalIgnoreCase);
|
|
||||||
|
public bool IsVideo => VideoFileExtensions.Contains(Path.GetExtension(FileName));
|
||||||
|
|
||||||
|
public bool IsAudio => AudioFileExtensions.Contains(Path.GetExtension(FileName));
|
||||||
|
|
||||||
public bool IsSpoiler =>
|
public bool IsSpoiler =>
|
||||||
IsImage && FileName.StartsWith("SPOILER_", StringComparison.Ordinal);
|
(IsImage || IsVideo || IsAudio) && FileName.StartsWith("SPOILER_", StringComparison.Ordinal);
|
||||||
|
|
||||||
public FileSize FileSize { get; }
|
public FileSize FileSize { get; }
|
||||||
|
|
||||||
@@ -43,7 +46,17 @@ namespace DiscordChatExporter.Domain.Discord.Models
|
|||||||
|
|
||||||
public partial class Attachment
|
public partial class Attachment
|
||||||
{
|
{
|
||||||
private static readonly string[] ImageFileExtensions = {".jpg", ".jpeg", ".png", ".gif", ".bmp"};
|
private static readonly HashSet<string> ImageFileExtensions =
|
||||||
|
new HashSet<string>(StringComparer.OrdinalIgnoreCase)
|
||||||
|
{".jpg", ".jpeg", ".png", ".gif", ".bmp", ".webp"};
|
||||||
|
|
||||||
|
private static readonly HashSet<string> VideoFileExtensions =
|
||||||
|
new HashSet<string>(StringComparer.OrdinalIgnoreCase)
|
||||||
|
{".mp4", ".webm"};
|
||||||
|
|
||||||
|
private static readonly HashSet<string> AudioFileExtensions =
|
||||||
|
new HashSet<string>(StringComparer.OrdinalIgnoreCase)
|
||||||
|
{".mp3", ".wav", ".ogg", ".flac", ".m4a"};
|
||||||
|
|
||||||
public static Attachment Parse(JsonElement json)
|
public static Attachment Parse(JsonElement json)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -74,8 +74,11 @@ namespace DiscordChatExporter.Domain.Exporting
|
|||||||
{
|
{
|
||||||
var filePath = await _mediaDownloader.DownloadAsync(url);
|
var filePath = await _mediaDownloader.DownloadAsync(url);
|
||||||
|
|
||||||
// We want relative path so that the output files can be copied around without breaking
|
// We want relative path so that the output files can be copied around without breaking.
|
||||||
var relativeFilePath = Path.GetRelativePath(Request.OutputBaseDirPath, filePath);
|
// Base directory path may be null if the file is stored at the root or relative to working directory.
|
||||||
|
var relativeFilePath = !string.IsNullOrWhiteSpace(Request.OutputBaseDirPath)
|
||||||
|
? Path.GetRelativePath(Request.OutputBaseDirPath, filePath)
|
||||||
|
: filePath;
|
||||||
|
|
||||||
// HACK: for HTML, we need to format the URL properly
|
// HACK: for HTML, we need to format the URL properly
|
||||||
if (Request.Format == ExportFormat.HtmlDark || Request.Format == ExportFormat.HtmlLight)
|
if (Request.Format == ExportFormat.HtmlDark || Request.Format == ExportFormat.HtmlLight)
|
||||||
|
|||||||
@@ -49,13 +49,18 @@ img {
|
|||||||
|
|
||||||
.markdown {
|
.markdown {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
white-space: pre-wrap;
|
|
||||||
line-height: 1.3;
|
line-height: 1.3;
|
||||||
overflow-wrap: break-word;
|
overflow-wrap: break-word;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.preserve-whitespace {
|
||||||
|
white-space: pre-wrap;
|
||||||
|
}
|
||||||
|
|
||||||
.spoiler {
|
.spoiler {
|
||||||
width: fit-content;
|
/* width: fit-content; */
|
||||||
|
display: inline-block;
|
||||||
|
/* This is more consistent across browsers, the old attribute worked well under Chrome but not FireFox. */
|
||||||
}
|
}
|
||||||
|
|
||||||
.spoiler--hidden {
|
.spoiler--hidden {
|
||||||
@@ -84,24 +89,28 @@ img {
|
|||||||
box-shadow: 0 0 1px 1px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 0 1px 1px rgba(0, 0, 0, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.spoiler--hidden .spoiler-image img {
|
.spoiler--hidden .spoiler-image * {
|
||||||
filter: blur(44px);
|
filter: blur(44px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.spoiler--hidden .spoiler-image:after {
|
.spoiler--hidden .spoiler-image:after {
|
||||||
content: "SPOILER";
|
content: "SPOILER";
|
||||||
color: #dcddde;
|
color: #dcddde;
|
||||||
background-color: rgba(0, 0, 0, 0.6);
|
background-color: rgba(0, 0, 0, 0.6);
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -50%);
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
padding: 0.5em 0.7em;
|
/* padding: 0.5em 0.7em; */
|
||||||
border-radius: 20px;
|
padding: 100%;
|
||||||
letter-spacing: 0.05em;
|
/* This ruins those beutifully rounded buttons, but it's needed to prevent a FireFox bug with video and audio elemnts. */
|
||||||
font-size: 0.9em;
|
/* The bug is that you can click trough the spoiler layer and play the video or audio, I could not identify the cause. */
|
||||||
}
|
/* I leave this here, in case someone is brave enough to venture in to madness that is undocumented browser behaviour. */
|
||||||
|
border-radius: 20px;
|
||||||
|
letter-spacing: 0.05em;
|
||||||
|
font-size: 0.9em;
|
||||||
|
}
|
||||||
|
|
||||||
.spoiler--hidden:hover .spoiler-image:after {
|
.spoiler--hidden:hover .spoiler-image:after {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
var userMember = Model.ExportContext.TryGetMember(Model.MessageGroup.Author.Id);
|
var userMember = Model.ExportContext.TryGetMember(Model.MessageGroup.Author.Id);
|
||||||
var userColor = Model.ExportContext.TryGetUserColor(Model.MessageGroup.Author.Id);
|
var userColor = Model.ExportContext.TryGetUserColor(Model.MessageGroup.Author.Id);
|
||||||
var userNick = (Model.MessageGroup.Author.IsBot ? Model.MessageGroup.Author.Name : (userMember?.Nick ?? Model.MessageGroup.Author.Name));
|
var userNick = Model.MessageGroup.Author.IsBot ? Model.MessageGroup.Author.Name : userMember?.Nick ?? Model.MessageGroup.Author.Name;
|
||||||
|
|
||||||
var userColorStyle = userColor != null
|
var userColorStyle = userColor != null
|
||||||
? $"color: rgb({userColor?.R},{userColor?.G},{userColor?.B})"
|
? $"color: rgb({userColor?.R},{userColor?.G},{userColor?.B})"
|
||||||
@@ -40,36 +40,51 @@
|
|||||||
var isPinnedStyle = message.IsPinned ? "chatlog__message--pinned" : null;
|
var isPinnedStyle = message.IsPinned ? "chatlog__message--pinned" : null;
|
||||||
|
|
||||||
<div class="chatlog__message @isPinnedStyle" data-message-id="@message.Id" id="message-@message.Id">
|
<div class="chatlog__message @isPinnedStyle" data-message-id="@message.Id" id="message-@message.Id">
|
||||||
<div class="chatlog__content">
|
@if (!string.IsNullOrWhiteSpace(message.Content) || message.EditedTimestamp != null)
|
||||||
<div class="markdown">@Raw(FormatMarkdown(message.Content)) @if (message.EditedTimestamp != null) {<span class="chatlog__edited-timestamp" title="@FormatDate(message.EditedTimestamp.Value)">(edited)</span>}</div>
|
{
|
||||||
</div>
|
<div class="chatlog__content">
|
||||||
|
<div class="markdown">
|
||||||
|
<span class="preserve-whitespace">@Raw(FormatMarkdown(message.Content))</span>
|
||||||
|
|
||||||
|
@if (message.EditedTimestamp != null)
|
||||||
|
{
|
||||||
|
<span class="chatlog__edited-timestamp" title="@FormatDate(message.EditedTimestamp.Value)">(edited)</span>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
@foreach (var attachment in message.Attachments)
|
@foreach (var attachment in message.Attachments)
|
||||||
{
|
{
|
||||||
<div class="chatlog__attachment">
|
<div class="chatlog__attachment">
|
||||||
@if (attachment.IsSpoiler)
|
<div class="@(attachment.IsSpoiler ? "spoiler spoiler--hidden" : "")" onclick="@(attachment.IsSpoiler ? "showSpoiler(event, this)" : "")">
|
||||||
{
|
<div class="@(attachment.IsSpoiler ? "spoiler-image" : "")">
|
||||||
<div class="spoiler spoiler--hidden" onclick="showSpoiler(event, this)">
|
|
||||||
<div class="spoiler-image">
|
|
||||||
<a href="@await ResolveUrlAsync(attachment.Url)">
|
|
||||||
<img class="chatlog__attachment-thumbnail" src="@await ResolveUrlAsync(attachment.Url)" alt="Attachment">
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
<a href="@await ResolveUrlAsync(attachment.Url)">
|
|
||||||
@if (attachment.IsImage)
|
@if (attachment.IsImage)
|
||||||
{
|
{
|
||||||
<img class="chatlog__attachment-thumbnail" src="@await ResolveUrlAsync(attachment.Url)" alt="Attachment">
|
<a href="@await ResolveUrlAsync(attachment.Url)">
|
||||||
|
<img class="chatlog__attachment-thumbnail" src="@await ResolveUrlAsync(attachment.Url)" alt="Image attachment" title="@($"Image: {attachment.FileName} ({attachment.FileSize})")">
|
||||||
|
</a>
|
||||||
|
}
|
||||||
|
else if (attachment.IsVideo)
|
||||||
|
{
|
||||||
|
<video controls class="chatlog__attachment-thumbnail">
|
||||||
|
<source src="@await ResolveUrlAsync(attachment.Url)" alt="Video attachment" title="@($"Video: {attachment.FileName} ({attachment.FileSize})")">
|
||||||
|
</video>
|
||||||
|
}
|
||||||
|
else if (attachment.IsAudio)
|
||||||
|
{
|
||||||
|
<audio controls class="chatlog__attachment-thumbnail">
|
||||||
|
<source src="@await ResolveUrlAsync(attachment.Url)" alt="Audio attachment" title="@($"Audio: {attachment.FileName} ({attachment.FileSize})")">
|
||||||
|
</audio>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@($"Attachment: {attachment.FileName} ({attachment.FileSize})")
|
<a href="@await ResolveUrlAsync(attachment.Url)">
|
||||||
|
@($"Attachment: {attachment.FileName} ({attachment.FileSize})")
|
||||||
|
</a>
|
||||||
}
|
}
|
||||||
</a>
|
</div>
|
||||||
}
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,12 +134,12 @@
|
|||||||
@if (!string.IsNullOrWhiteSpace(embed.Url))
|
@if (!string.IsNullOrWhiteSpace(embed.Url))
|
||||||
{
|
{
|
||||||
<a class="chatlog__embed-title-link" href="@embed.Url">
|
<a class="chatlog__embed-title-link" href="@embed.Url">
|
||||||
<div class="markdown">@Raw(FormatEmbedMarkdown(embed.Title))</div>
|
<div class="markdown preserve-whitespace">@Raw(FormatEmbedMarkdown(embed.Title))</div>
|
||||||
</a>
|
</a>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<div class="markdown">@Raw(FormatEmbedMarkdown(embed.Title))</div>
|
<div class="markdown preserve-whitespace">@Raw(FormatEmbedMarkdown(embed.Title))</div>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
@@ -132,7 +147,7 @@
|
|||||||
@if (!string.IsNullOrWhiteSpace(embed.Description))
|
@if (!string.IsNullOrWhiteSpace(embed.Description))
|
||||||
{
|
{
|
||||||
<div class="chatlog__embed-description">
|
<div class="chatlog__embed-description">
|
||||||
<div class="markdown">@Raw(FormatEmbedMarkdown(embed.Description))</div>
|
<div class="markdown preserve-whitespace">@Raw(FormatEmbedMarkdown(embed.Description))</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,14 +162,14 @@
|
|||||||
@if (!string.IsNullOrWhiteSpace(field.Name))
|
@if (!string.IsNullOrWhiteSpace(field.Name))
|
||||||
{
|
{
|
||||||
<div class="chatlog__embed-field-name">
|
<div class="chatlog__embed-field-name">
|
||||||
<div class="markdown">@Raw(FormatEmbedMarkdown(field.Name))</div>
|
<div class="markdown preserve-whitespace">@Raw(FormatEmbedMarkdown(field.Name))</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
@if (!string.IsNullOrWhiteSpace(field.Value))
|
@if (!string.IsNullOrWhiteSpace(field.Value))
|
||||||
{
|
{
|
||||||
<div class="chatlog__embed-field-value">
|
<div class="chatlog__embed-field-value">
|
||||||
<div class="markdown">@Raw(FormatEmbedMarkdown(field.Value))</div>
|
<div class="markdown preserve-whitespace">@Raw(FormatEmbedMarkdown(field.Value))</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
@@ -190,22 +205,22 @@
|
|||||||
<img class="chatlog__embed-footer-icon" src="@await ResolveUrlAsync(embed.Footer.IconUrl)" alt="Footer icon">
|
<img class="chatlog__embed-footer-icon" src="@await ResolveUrlAsync(embed.Footer.IconUrl)" alt="Footer icon">
|
||||||
}
|
}
|
||||||
|
|
||||||
<span class="chatlog__embed-footer-text">
|
<span class="chatlog__embed-footer-text">
|
||||||
@if (!string.IsNullOrWhiteSpace(embed.Footer?.Text))
|
@if (!string.IsNullOrWhiteSpace(embed.Footer?.Text))
|
||||||
{
|
{
|
||||||
@embed.Footer.Text
|
@embed.Footer.Text
|
||||||
}
|
}
|
||||||
|
|
||||||
@if (!string.IsNullOrWhiteSpace(embed.Footer?.Text) && embed.Timestamp != null)
|
@if (!string.IsNullOrWhiteSpace(embed.Footer?.Text) && embed.Timestamp != null)
|
||||||
{
|
{
|
||||||
@(" • ")
|
@(" • ")
|
||||||
}
|
}
|
||||||
|
|
||||||
@if (embed.Timestamp != null)
|
@if (embed.Timestamp != null)
|
||||||
{
|
{
|
||||||
@FormatDate(embed.Timestamp.Value)
|
@FormatDate(embed.Timestamp.Value)
|
||||||
}
|
}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ using System.Threading.Tasks;
|
|||||||
using DiscordChatExporter.Domain.Discord.Models;
|
using DiscordChatExporter.Domain.Discord.Models;
|
||||||
using DiscordChatExporter.Domain.Exporting.Writers.MarkdownVisitors;
|
using DiscordChatExporter.Domain.Exporting.Writers.MarkdownVisitors;
|
||||||
using DiscordChatExporter.Domain.Internal.Extensions;
|
using DiscordChatExporter.Domain.Internal.Extensions;
|
||||||
|
using DiscordChatExporter.Domain.Markdown.Ast;
|
||||||
|
|
||||||
namespace DiscordChatExporter.Domain.Exporting.Writers
|
namespace DiscordChatExporter.Domain.Exporting.Writers
|
||||||
{
|
{
|
||||||
@@ -159,6 +160,20 @@ namespace DiscordChatExporter.Domain.Exporting.Writers
|
|||||||
await _writer.FlushAsync();
|
await _writer.FlushAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async ValueTask WriteMentionAsync(User mentionedUser)
|
||||||
|
{
|
||||||
|
_writer.WriteStartObject();
|
||||||
|
|
||||||
|
_writer.WriteString("id", mentionedUser.Id);
|
||||||
|
_writer.WriteString("name", mentionedUser.Name);
|
||||||
|
_writer.WriteNumber("discriminator", mentionedUser.Discriminator);
|
||||||
|
_writer.WriteString("nickname", Context.TryGetMember(mentionedUser.Id)?.Nick ?? mentionedUser.Name);
|
||||||
|
_writer.WriteBoolean("isBot", mentionedUser.IsBot);
|
||||||
|
|
||||||
|
_writer.WriteEndObject();
|
||||||
|
await _writer.FlushAsync();
|
||||||
|
}
|
||||||
|
|
||||||
public override async ValueTask WritePreambleAsync()
|
public override async ValueTask WritePreambleAsync()
|
||||||
{
|
{
|
||||||
// Root object (start)
|
// Root object (start)
|
||||||
@@ -239,6 +254,13 @@ namespace DiscordChatExporter.Domain.Exporting.Writers
|
|||||||
|
|
||||||
_writer.WriteEndArray();
|
_writer.WriteEndArray();
|
||||||
|
|
||||||
|
// Mentions
|
||||||
|
_writer.WriteStartArray("mentions");
|
||||||
|
foreach (var mention in message.MentionedUsers)
|
||||||
|
await WriteMentionAsync(mention);
|
||||||
|
|
||||||
|
_writer.WriteEndArray();
|
||||||
|
|
||||||
_writer.WriteEndObject();
|
_writer.WriteEndObject();
|
||||||
await _writer.FlushAsync();
|
await _writer.FlushAsync();
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ namespace DiscordChatExporter.Gui.ViewModels.Framework
|
|||||||
|
|
||||||
public void Close(T dialogResult = default)
|
public void Close(T dialogResult = default)
|
||||||
{
|
{
|
||||||
DialogResult = dialogResult;
|
DialogResult = dialogResult!;
|
||||||
Closed?.Invoke(this, EventArgs.Empty);
|
Closed?.Invoke(this, EventArgs.Empty);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||||
<Version>2.24</Version>
|
<Version>2.25</Version>
|
||||||
<Company>Tyrrrz</Company>
|
<Company>Tyrrrz</Company>
|
||||||
<Copyright>Copyright (c) Alexey Golub</Copyright>
|
<Copyright>Copyright (c) Alexey Golub</Copyright>
|
||||||
<LangVersion>latest</LangVersion>
|
<LangVersion>latest</LangVersion>
|
||||||
|
|||||||
+2
-2
@@ -1,5 +1,5 @@
|
|||||||
# Build
|
# Build
|
||||||
FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build
|
FROM mcr.microsoft.com/dotnet/sdk:3.1 AS build
|
||||||
WORKDIR /src
|
WORKDIR /src
|
||||||
|
|
||||||
COPY favicon.ico ./
|
COPY favicon.ico ./
|
||||||
@@ -10,7 +10,7 @@ COPY DiscordChatExporter.Cli DiscordChatExporter.Cli
|
|||||||
RUN dotnet publish DiscordChatExporter.Cli -o DiscordChatExporter.Cli/publish -c Release
|
RUN dotnet publish DiscordChatExporter.Cli -o DiscordChatExporter.Cli/publish -c Release
|
||||||
|
|
||||||
# Run
|
# Run
|
||||||
FROM mcr.microsoft.com/dotnet/core/runtime:3.1 AS run
|
FROM mcr.microsoft.com/dotnet/runtime:3.1 AS run
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
COPY --from=build /src/DiscordChatExporter.Cli/publish ./
|
COPY --from=build /src/DiscordChatExporter.Cli/publish ./
|
||||||
|
|||||||
@@ -6,9 +6,11 @@
|
|||||||
[](https://tyrrrz.me/donate)
|
[](https://tyrrrz.me/donate)
|
||||||
[](https://xscode.com/Tyrrrz/DiscordChatExporter)
|
[](https://xscode.com/Tyrrrz/DiscordChatExporter)
|
||||||
|
|
||||||
|
**Project status: active**.
|
||||||
|
|
||||||
DiscordChatExporter can be used to export message history from a [Discord](https://discord.com) channel to a file. It works with direct messages, group messages, server channels, supports Discord's dialect of markdown and all other rich media features.
|
DiscordChatExporter can be used to export message history from a [Discord](https://discord.com) channel to a file. It works with direct messages, group messages, server channels, supports Discord's dialect of markdown and all other rich media features.
|
||||||
|
|
||||||
Have questions? Check out the [wiki](https://github.com/Tyrrrz/DiscordChatExporter/wiki) first.
|
Have questions or need help? Check out the [wiki](https://github.com/Tyrrrz/DiscordChatExporter/wiki).
|
||||||
|
|
||||||
## Download
|
## Download
|
||||||
|
|
||||||
@@ -17,11 +19,11 @@ Have questions? Check out the [wiki](https://github.com/Tyrrrz/DiscordChatExport
|
|||||||
- [AUR](https://aur.archlinux.org/packages/discord-chat-exporter-cli/): `yay -S discord-chat-exporter-cli`
|
- [AUR](https://aur.archlinux.org/packages/discord-chat-exporter-cli/): `yay -S discord-chat-exporter-cli`
|
||||||
- [CI build](https://github.com/Tyrrrz/DiscordChatExporter/actions)
|
- [CI build](https://github.com/Tyrrrz/DiscordChatExporter/actions)
|
||||||
|
|
||||||
Note: This application requires .NET Core runtime in order to run:
|
Note: This application requires .NET Core v3.1 runtime in order to run:
|
||||||
|
|
||||||
- .NET Core runtime for Windows: [x64](https://dotnet.microsoft.com/download/dotnet-core/thank-you/runtime-desktop-3.1.0-windows-x64-installer) | [x86](https://dotnet.microsoft.com/download/dotnet-core/thank-you/runtime-desktop-3.1.0-windows-x86-installer)
|
- .NET Runtime for Windows: [x64](https://dotnet.microsoft.com/download/dotnet-core/thank-you/runtime-desktop-3.1.0-windows-x64-installer) | [x86](https://dotnet.microsoft.com/download/dotnet-core/thank-you/runtime-desktop-3.1.0-windows-x86-installer)
|
||||||
- .NET Core runtime for macOS (for CLI version only): [x64](https://dotnet.microsoft.com/download/dotnet-core/thank-you/runtime-3.1.0-macos-x64-installer)
|
- .NET Runtime for macOS (for CLI version only): [x64](https://dotnet.microsoft.com/download/dotnet-core/thank-you/runtime-3.1.0-macos-x64-installer)
|
||||||
- .NET Core runtime for Linux (for CLI version only): [Ubuntu/Debian](https://docs.microsoft.com/en-us/dotnet/core/install/linux-package-manager-ubuntu-1904#install-the-net-core-runtime)
|
- .NET Runtime for Linux (for CLI version only): [Ubuntu/Debian](https://docs.microsoft.com/en-us/dotnet/core/install/linux-package-manager-ubuntu-1904#install-the-net-core-runtime)
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user