Compare commits

..

51 Commits

Author SHA1 Message Date
Tyrrrz a46bf9bf11 Update version 2023-11-16 00:43:07 +02:00
Tyrrrz 8dc024d244 Add more comments to the dockerfile 2023-11-16 00:38:18 +02:00
Tyrrrz 9a125db0ea Reformat docker commands 2023-11-15 17:03:36 +02:00
Tyrrrz d9c35f4405 Clean up dockerfile 2023-11-15 15:25:10 +02:00
Tyrrrz 5846129cc6 Use native OpenFolderDialog in GUI 2023-11-14 23:44:29 +02:00
Tyrrrz 6f5f747632 Standardize paths in docker 2023-11-14 23:31:12 +02:00
Tyrrrz 6a71c59b6d Don't need to create a user in Docker anymore with .NET 8 2023-11-14 23:24:28 +02:00
Tyrrrz 73574ade48 Convert some classes to use primary constructors 2023-11-14 22:57:02 +02:00
Tyrrrz 743db67f91 Update references to .NET Runtime 2023-11-14 20:20:35 +02:00
Tyrrrz 5d7a74eece Switch to .NET 8 2023-11-14 20:05:27 +02:00
Tyrrrz 129df42de4 Keep file:/// for absolute paths in HTML exports
Closes #1155
2023-11-13 18:09:09 +02:00
Tyrrrz 6508455f3a Move the tooltip to the correct place 2023-11-12 23:47:16 +02:00
Tyrrrz 50b26c557f Disable "Reuse assets" and "Assets path" options in GUI export setup if "Download assets" is disabled 2023-11-12 23:45:52 +02:00
Tyrrrz b709fc0f15 Update NuGet packages 2023-11-12 23:39:38 +02:00
Tyrrrz c118a0bb94 Fix broken links in docs 2023-11-11 17:49:43 +02:00
Oleksii Holub 1418bec0c7 Update dotnet installation instructions 2023-11-11 17:21:33 +02:00
Oleksii Holub 750fd69a50 Update Docker instructions 2023-11-11 17:10:43 +02:00
Tyrrrz b1297bb7df Set default locale in Docker 2023-11-11 16:56:49 +02:00
Tyrrrz 3189111a0e Install both the iclu-libs and icu-data-full in Docker 2023-11-11 16:52:55 +02:00
Tyrrrz 6ec2c04aa3 Use the full ICU in docker 2023-11-11 16:48:26 +02:00
Tyrrrz 7c014bf64d Add a note about interactivity in the CLI 2023-11-11 16:39:36 +02:00
Tyrrrz c875f8f4f0 Install ICU libs in Docker 2023-11-11 16:27:17 +02:00
Tyrrrz 69e2c337d8 Use alpine images again in Docker 2023-11-11 00:05:27 +02:00
Tyrrrz 77c770acdd Update gitignore file 2023-11-09 13:41:43 +02:00
Tyrrrz 6f8749a105 Update version 2023-11-09 13:11:59 +02:00
Tyrrrz 4896d748aa Update NuGet packages 2023-11-09 13:06:00 +02:00
Tyrrrz 4876ed192a Use ISO 8601 date format in CSV
Closes #1152
2023-11-07 22:16:25 +02:00
Tyrrrz 4f6e6f4f0b Write embed videos in JSON export
Closes #1120
2023-11-07 16:03:39 +02:00
Tyrrrz 8c75f04115 Add labels to the dockerfile 2023-11-07 15:09:31 +02:00
Tyrrrz df04b123cd Reorder props 2023-11-01 15:30:15 +02:00
Tyrrrz 578ed9be17 Prepare for .NET 8 2023-10-31 23:40:41 +02:00
Tyrrrz ebceacc707 Minor workflow cleanup 2023-10-29 23:59:09 +02:00
Tyrrrz eaab5786f2 Workflow improvements 2023-10-29 01:24:47 +03:00
Tyrrrz ebf7d65334 Fix step name in deploy job 2023-10-26 16:15:09 +03:00
Tyrrrz 52a23828ce Update version 2023-10-26 16:06:53 +03:00
Tyrrrz 2edd781e32 Update NuGet packages 2023-10-26 15:59:56 +03:00
Tyrrrz 19cdf47c6e Consolidate some docker jobs 2023-10-26 03:11:07 +03:00
Tyrrrz 65e0a63bda Consistency with other projects 2023-10-26 02:49:26 +03:00
Tyrrrz 692438d10c Reduce repetition in workflows using matrices 2023-10-26 02:45:20 +03:00
Tyrrrz c607c6f307 Fix deploy job 2023-10-26 00:03:55 +03:00
Tyrrrz aae43821e7 Simplify CI workflow 2023-10-24 15:13:30 +03:00
Tyrrrz 7b9a4e1aff Fix closing backticks in docs 2023-10-24 14:32:29 +03:00
LostXOR d8e57f94a8 Add command to fix broken permissions to Linux installation instructions (#1148)
Co-authored-by: Oleksii Holub <1935960+Tyrrrz@users.noreply.github.com>
2023-10-24 00:26:52 +03:00
Tyrrrz 7b7df564ed Allow empty names in interactions 2023-10-22 19:22:51 +03:00
Tyrrrz 80b59d1254 Update version 2023-10-19 18:52:19 +03:00
Tyrrrz 6579563816 Don't unfurl URLs in notify job 2023-10-17 16:18:44 +03:00
Tyrrrz e2971ed3a6 Improve notify job message 2023-10-17 15:01:59 +03:00
Tyrrrz a0a263f074 Improve error message for forum channels
Related to #1144
2023-10-16 17:22:28 +03:00
Tyrrrz 1438fd9f77 Temp fix for the docs link in the readme 2023-10-16 17:12:40 +03:00
Tyrrrz b298b45cf7 Escape markup in CLI 2023-10-13 01:17:13 +03:00
Tyrrrz 8d01c6be35 Improve notify message format 2023-10-12 16:00:47 +03:00
61 changed files with 731 additions and 653 deletions
+8 -11
View File
@@ -2,7 +2,8 @@
Docker distribution of DiscordChatExporter provides a way to run the app in a virtualized and isolated environment. Due to the nature of Docker, you also don't need to install any prerequisites otherwise required by DCE.
Note that only the CLI flavor of DiscordChatExporter is available for use with Docker.
> **Note**:
> Only the CLI flavor of DiscordChatExporter is available for use with Docker.
## Pulling
@@ -14,9 +15,9 @@ docker pull tyrrrz/discordchatexporter:stable
Note the `:stable` tag. DiscordChatExporter images are tagged according to the following patterns:
- `stable`: Latest stable version release and maps to the latest GitHub release. This tag is updated with release of each new version. Recommended for personal use.
- `latest`: Built from the latest commit and maps to the latest CI build on GitHub. This tag is updated with each new commit to `master` branch. Not recommended for everyday use, but you can use it if you want to try out some features that haven't been officially released yet.
- `x.y.z` (e.g. `2.30.1`): Fixed version release and maps to the corresponding tag on GitHub. This tag is never updated once that version has been released. Recommended for use in automation scenarios.
- `stable` — latest stable version release. This tag is updated with each release of a new project version. Recommended for personal use.
- `x.y.z` (e.g. `2.30.1`) — specific stable version release. This tag is pushed when the corresponding version is released and never updated thereafter. Recommended for use in automation scenarios.
- `latest` — latest (potentially unstable) build. This tag is updated with each new commit to the `master` branch. Not recommended, unless you want to test a new feature that has not been released in a stable version yet.
You can see all available tags [here](https://hub.docker.com/r/tyrrrz/discordchatexporter/tags?ordering=name).
@@ -34,13 +35,13 @@ To export a channel:
docker run --rm -v /path/on/machine:/out tyrrrz/discordchatexporter:stable export -t TOKEN -c CHANNELID
```
If you want colored output and real-time progress reporting, pass the `-it` (interactive) option:
If you want colored output and real-time progress reporting, pass the `-it` (interactive + pseudo-terminal) option:
```console
docker run --rm -it -v /path/on/machine:/out tyrrrz/discordchatexporter:stable export -t TOKEN -c CHANNELID
```
Note the `-v /path/on/machine:/out` option, which instructs Docker to bind the `/out` directory inside the container to a path on your host machine. Replace `/path/on/machine` with the directory you want the files to be saved at.
The `-v /path/on/machine:/out` option instructs Docker to bind the `/out` directory inside the container to a path on your host machine. Replace `/path/on/machine` with the directory you want the files to be saved at.
> **Note**:
> If you are running SELinux, you will need to add the `:z` option after `/out`, e.g.:
@@ -58,14 +59,10 @@ You can also use the current working directory as the output directory by specif
For more information, please refer to the [Dockerfile](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/DiscordChatExporter.Cli.dockerfile) and [Docker documentation](https://docs.docker.com/engine/reference/run).
To get your Token and Channel IDs, please refer to [this page](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Token-and-IDs.md).
To get your Token and Channel IDs, please refer to [this page](Token-and-IDs.md).
## Environment variables
DiscordChatExpoter CLI accepts the `DISCORD_TOKEN` environment variable as a fallback for the `--token` option. You can set this variable either with the `--env` Docker option or with a combination of the `--env-file` Docker option and a `.env` file.
Please refer to the [Docker documentation](https://docs.docker.com/engine/reference/commandline/run/#set-environment-variables--e---env---env-file) for more information.
## Permission issues with Linux hosts
When bounding volumes between the container and the host, files mirrored from the Docker container will be owned by the Docker user (in most setups this is `root`). Please refer to this [issue comment](https://github.com/Tyrrrz/DiscordChatExporter/issues/800#issuecomment-1030471970) for instructions on how to override this behavior.
+23 -39
View File
@@ -1,42 +1,36 @@
# Install .NET runtime
**.NET v7.0 Runtime** is required by **DiscordChatExporter v2.37 and newer**.
**.NET 8.0 Runtime** is required by **DiscordChatExporter**.
If you are using GUI flavor of DiscordChatExporter, the runtime will be installed automatically (since v2.27.1).
If you are using the CLI from Docker, installing the runtime on the host machine is not needed.
> **Note**:
> Installing the **.NET Runtime** is not required if you running **DiscordChatExporter** using [Docker](Docker.md).
- Install
- [Windows and macOS](#windows-and-macos)
- [Linux](#linux)
- [Docker](#docker)
- [Verify installation](#verify-installation-optional)
## Windows
## Windows and macOS
Both the GUI and the CLI flavors of **DiscordChatExporter** for Windows come with a bootstrapper that should automatically install all the required prerequisites. Simply run the application and follow the presented instructions.
**Download directly from one of the links:**
If, for some reason, you need to install the runtime manually, use one of the download links below:
- [.NET v7.0 Runtime for **Windows x64**](https://dotnet.microsoft.com/download/dotnet/thank-you/runtime-desktop-7.0.11-windows-x64-installer)
- [.NET v7.0 Runtime for **Windows x86**](https://dotnet.microsoft.com/download/dotnet/thank-you/runtime-desktop-7.0.11-windows-x86-installer)
- [.NET v7.0 Runtime for **Macs with Apple silicon**](https://dotnet.microsoft.com/download/dotnet/thank-you/runtime-7.0.11-macos-arm64-installer)
- [.NET v7.0 Runtime for **Macs with an Intel processor**](https://dotnet.microsoft.com/download/dotnet/thank-you/runtime-7.0.11-macos-x64-installer)
- [.NET 8.0 Runtime for **Windows x64**](https://dotnet.microsoft.com/en-us/download/dotnet/thank-you/runtime-8.0.0-windows-x86-installer)
- [.NET 8.0 Runtime for **Windows x86**](https://dotnet.microsoft.com/en-us/download/dotnet/thank-you/runtime-8.0.0-windows-x64-installer)
- [.NET 8.0 Runtime for **Windows arm64**](https://dotnet.microsoft.com/en-us/download/dotnet/thank-you/runtime-8.0.0-windows-arm64-installer)
> **Note**:
> [How can I tell if my computer is running a 32-bit (x86) or a 64-bit (x64) version of Windows?](https://support.microsoft.com/help/15056/windows-32-64-bit-faq)
## macOS
Use one of the download links below:
- [.NET 8.0 Runtime for **macOS x64**](https://dotnet.microsoft.com/en-us/download/dotnet/thank-you/runtime-8.0.0-macos-x64-installer)
- [.NET 8.0 Runtime for **macOS arm64**](https://dotnet.microsoft.com/en-us/download/dotnet/thank-you/runtime-8.0.0-macos-arm64-installer)
> **Note**:
> [Is my Mac using an Intel processor or Apple silicon?](https://support.apple.com/HT211814)
**Otherwise:**
[Check out the latest version on this page](https://dotnet.microsoft.com/download/dotnet/7.0).
Look for the download link in the '**.NET Runtime**' section, under **Installers**:
[![Screenshot from download page](https://i.imgur.com/t3PB3NC.png)](https://dotnet.microsoft.com/download/dotnet/7.0)
> [Is my Mac using an Intel processor (x64) or Apple Silicon (arm64)?](https://support.apple.com/HT211814)
## Linux
Installing the .NET Runtime works differently depending on which Linux distribution you use.
Check out the instructions for your distro:
Installing the .NET Runtime works differently depending on which Linux distribution you use. Check out the instructions for your distro:
- [Ubuntu](https://docs.microsoft.com/dotnet/core/install/linux-ubuntu)
- [Debian](https://docs.microsoft.com/dotnet/core/install/linux-debian)
@@ -44,25 +38,15 @@ Check out the instructions for your distro:
For other distros, please check the _'Install on Linux'_ menu on the left of [this page](https://docs.microsoft.com/dotnet/core/install/linux).
## Docker
Installing the .NET Runtime is not needed. Please refer to the [Docker usage instructions](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Docker.md) page for more information.
## Verify installation (optional)
You can check which version of **.NET Runtime** is installed by following these instructions:
You can check which version of **.NET Runtime** is installed by running the following command in a terminal:
### Step 1
```console
dotnet --info
```
**Windows:** Open Command Prompt (aka `cmd`)
**macOS and Linux:** Open `Terminal`
### Step 2
Run `dotnet --info`
If the **.NET Runtime** is correctly installed, the command will output the following:
If the **.NET Runtime** is correctly installed, the command will output something similar to the following:
```console
.NET runtimes installed:
+6 -5
View File
@@ -2,15 +2,16 @@
Welcome to the getting started page!
Here you'll learn how to use every **DiscordChatExporter** (DCE for short) feature.
For other things you can do with DCE, check out the [Guides](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Readme.md#Guides) section.
For other things you can do with DCE, check out the [Guides](Readme.md#guides) section.
If you still have unanswered questions after reading this page or if you have encountered a problem, please visit our [FAQ & Troubleshooting](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Troubleshooting.md) section.
If you still have unanswered questions after reading this page or if you have encountered a problem, please visit our [FAQ & Troubleshooting](Troubleshooting.md) section.
The information presented on this page is valid for **all** platforms.
## GUI or CLI?
![GUI vs CLI](https://i.imgur.com/j9OTxRB.png)
**DCE** has two different versions:
- **Graphical User Interface** (**GUI**) - it's the preferred version for newcomers as it is easy to use.
@@ -19,12 +20,12 @@ The information presented on this page is valid for **all** platforms.
You can get it by [downloading](https://github.com/Tyrrrz/DiscordChatExporter/releases/latest) the `DiscordChatExporter.CLI.zip` file.
If you're not comfortable with **Windows'** Command-line (cmd), please choose the GUI.
[**macOS**](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/MacOS.md), [**Linux**](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Linux.md) and [**Docker**](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Docker.md) users can only use the CLI version.
[**macOS**](MacOS.md), [**Linux**](Linux.md) and [**Docker**](Docker.md) users can only use the CLI version.
There are dedicated guides for each version:
- [Using the GUI](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Using-the-GUI.md)
- [Using the CLI](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Using-the-CLI.md)
- [Using the GUI](Using-the-GUI.md)
- [Using the CLI](Using-the-CLI.md)
## File formats
+9 -3
View File
@@ -2,7 +2,7 @@
## Installing .NET Runtime
Please follow the [instructions provided here](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Dotnet.md).
Please follow the [instructions provided here](Dotnet.md).
## Downloading and using DiscordChatExporter.Cli
@@ -15,6 +15,12 @@ Please follow the [instructions provided here](https://github.com/Tyrrrz/Discord
dotnet DiscordChatExporter.Cli.dll export -t TOKEN -c CHANNEL
```
> [How to get Token and Channel IDs](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Token-and-IDs.md).
If the above command throws a "Permission denied" error, execute this command to fix the permissions:
There's much more you can do with DCE.CLI! Read the [CLI explained](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Getting-started.md#dcecli-commands-) page to get started.
```console
chmod 644 *.dll DiscordChatExporter.*
```
> [How to get Token and Channel IDs](Token-and-IDs.md).
There's much more you can do with DCE.CLI! Read the [CLI explained](Using-the-CLI.md) page to get started.
+3 -3
View File
@@ -4,7 +4,7 @@
## Installing .NET Runtime
Please follow the [instructions provided here](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Dotnet.md).
Please follow the [instructions provided here](Dotnet.md).
## Downloading and using DiscordChatExporter.Cli
@@ -17,6 +17,6 @@ Please follow the [instructions provided here](https://github.com/Tyrrrz/Discord
dotnet DiscordChatExporter.Cli.dll export -t TOKEN -c CHANNEL
```
> [How to get Token and Channel IDs](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Token-and-IDs.md).
> [How to get Token and Channel IDs](Token-and-IDs.md).
There's much more you can do with DCE.CLI! Read the [CLI explained](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Getting-started.md#dcecli-commands-) page to get started.
There's much more you can do with DCE.CLI! Read the [CLI explained](Using-the-CLI.md) page to get started.
+16 -16
View File
@@ -2,21 +2,21 @@
## Installation & Usage
- [Get .NET Core Runtime](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Dotnet.md) (Required for CLI; Installed automatically for GUI; Not required in Docker)
- [Windows](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Getting-started.md#gui-or-cli) | [macOS](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/MacOS.md) | [Linux](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Linux.md) | [Docker](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Docker.md)
- [Get .NET Core Runtime](Dotnet.md) (Required for CLI; Installed automatically for GUI; Not required in Docker)
- [Windows](Getting-started.md#gui-or-cli) | [macOS](MacOS.md) | [Linux](Linux.md) | [Docker](Docker.md)
- Getting started:
- [Using the GUI](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Getting-started.md#using-the-gui)
- [Using the CLI](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Getting-started.md#using-the-cli)
- [File formats](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Getting-started.md#file-formats)
- [Using the GUI](Using-the-CLI.md)
- [Using the CLI](Using-the-CLI.md)
- [File formats](Getting-started.md#file-formats)
## Guides
- [How to get Token and Channel IDs](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Token-and-IDs.md)
- [How to use message filters](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Message-filters.md)
- [How to get Token and Channel IDs](Token-and-IDs.md)
- [How to use message filters](Message-filters.md)
- Export scheduling with CLI:
- [Windows](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Scheduling-Windows.md)
- [macOS](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Scheduling-MacOS.md)
- [Linux](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Scheduling-Linux.md)
- [Windows](Scheduling-Windows.md)
- [macOS](Scheduling-MacOS.md)
- [Linux](Scheduling-Linux.md)
## Video tutorial
@@ -26,9 +26,9 @@
## FAQ & Troubleshooting
- [General questions](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Troubleshooting.md#general)
- [First steps help](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Troubleshooting.md#first-steps)
- [It's crashing/failing](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Troubleshooting.md#DCE-is-crashingfailing)
- [.NET Core Runtime is required](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Troubleshooting.md#net-core-runtime-is-required)
- [Errors](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Troubleshooting.md#errors)
- [**More help**](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Troubleshooting.md)
- [General questions](Troubleshooting.md#general)
- [First steps help](Troubleshooting.md#first-steps)
- [It's crashing/failing](Troubleshooting.md#DCE-is-crashingfailing)
- [.NET Core Runtime is required](Troubleshooting.md#net-core-runtime-is-required)
- [Errors](Troubleshooting.md#errors)
- [**More help**](Troubleshooting.md)
+4 -6
View File
@@ -1,10 +1,8 @@
# Scheduling exports with Cron
Make sure you already have **DiscordChatExporter.CLI** and **.NET Core** properly installed ([instructions here](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Linux.md)).
Make sure you already have **DiscordChatExporter.CLI** and **.NET Core** properly installed ([instructions here](Linux.md)).
You can use Cron on macOS, but [this method](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/scheduling-MacOS.md) is preferred.
---
## Creating the script
1. Open Terminal and create a new text file with `nano /path/to/DiscordChatExporter/cron.sh`
@@ -66,8 +64,8 @@ exit 0
3. Replace:
- `tokenhere` with your [Token](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Token-and-IDs.md).
- `channelhere` with a [Channel ID](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Token-and-IDs.md).
- `tokenhere` with your [Token](Token-and-IDs.md).
- `channelhere` with a [Channel ID](Token-and-IDs.md).
- `dceFOLDERpathhere` with DCE's **directory path** (e.g. `/path/to/folder`, NOT `/path/to/folder/DiscordChatExporter.dll`).
- `filenamehere` with the exported channel's filename, without spaces.
- `dirhere` with the export directory (e.g. /home/user/Documents/Discord\ Exports).
+4 -4
View File
@@ -2,7 +2,7 @@
Scheduling on macOS is a bit tricky, but it should work if you follow the instructions accordingly.
Make sure you already have **DiscordChatExporter.CLI** and **.NET Core** properly installed ([instructions here](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/macOS-usage-instructions.md)).
Make sure you already have **DiscordChatExporter.CLI** and **.NET Core** properly installed ([instructions here](MacOS.md)).
## Creating the script
@@ -70,8 +70,8 @@ exit 0
4. Replace:
- `tokenhere` with your [Token](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Token-and-IDs.md)
- `channelhere` with a [Channel ID](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Token-and-IDs.md)
- `tokenhere` with your [Token](Token-and-IDs.md)
- `channelhere` with a [Channel ID](Token-and-IDs.md)
- `dceFOLDERpathhere` with DCE's **directory's path** (e.g. `/Users/user/Desktop/DiscordChatExporterFolder`, NOT `/Users/user/Desktop/DiscordChatExporterFolder/DiscordChatExporter.DLL`)
- `filenamehere` with the exported channel's filename, without spaces
- `dirhere` with the directory you want the files to be saved at (e.g. `/Users/user/Documents/Discord\ Exports`)
@@ -168,7 +168,7 @@ Omitted keys are interpreted as wildcards, for example, if you delete the Minute
Be aware that if you set the day to '31', the script will only run on months that have the 31st day.
**Check the examples below ([or skip to step 3 (loading the file)](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/scheduling-MacOS.md#3-loading-the-plist-into-launchctl)):**
**Check the examples below ([or skip to step 3 (loading the file)](#3-loading-the-plist-into-launchctl)):**
Export everyday at 5:15 PM:
+5 -3
View File
@@ -2,10 +2,12 @@
We'll be using [DiscordChatExporter CLI](https://github.com/Tyrrrz/DiscordChatExporter/releases/latest), PowerShell, and Task Scheduler.
## Creating the script
1. Open a text editor such as Notepad and paste:
```console
# Info: https://github.com/Tyrrrz/DiscordChatExporter/wiki
# Info: https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs
$TOKEN = "tokenhere"
$CHANNEL = "channelhere"
@@ -31,8 +33,8 @@ exit
2. Replace:
- `tokenhere` with your [Token](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Token-and-IDs.md)
- `channelhere` with a [Channel ID](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Token-and-IDs.md)
- `tokenhere` with your [Token](Token-and-IDs.md)
- `channelhere` with a [Channel ID](Token-and-IDs.md)
- `exefolderhere` with the .exe **directory's path** (e.g. C:\Users\User\Desktop\DiscordChatExporter)
- `filenamehere` with a filename without spaces
- `dirhere` with the export directory (e.g. C:\Users\User\Documents\Exports)
+2 -2
View File
@@ -232,7 +232,7 @@ Prerequisite step: Navigate to [discord.com](https://discord.com) and login.
4. Launch Discord.
5. To find your user token, continue [here](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Token-and-IDs.md#in-chrome).
5. To find your user token, continue [here](#in-chrome).
#### Via settings menu (BetterDiscord only)
@@ -252,7 +252,7 @@ Prerequisite step: Navigate to [discord.com](https://discord.com) and login.
<br />
4. Press <kbd>Esc</kbd>. The settings page will close.
5. To find your user token, continue [here](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Token-and-IDs.md#in-chrome).
5. To find your user token, continue [here](#in-chrome).
## How to get a Bot Token
+16 -21
View File
@@ -20,7 +20,7 @@ A token can be used to log into your account, so treat it like a password and ne
### How can I reset my token?
Follow the [instructions here](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Token-and-IDs.md).
Follow the [instructions here](Token-and-IDs.md).
### Will I get banned if I use this?
@@ -66,31 +66,26 @@ No.
### How can I find my token?
Check the following page: [Obtaining token](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Token-and-IDs.md)
Check the following page: [Obtaining token](Token-and-IDs.md)
### When I open DCE a black window pops up quickly or nothing shows up
If you have [.NET Core Runtime correctly installed](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Dotnet.md), you might have downloaded the CLi flavor, try [downloading the GUI](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Getting-started.md#gui-or-cli) instead.
### I can't open DCE, it sends me to a Visual Studio webpage
Check the following page:
[Installation and usage](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Readme.md#installation--usage)
If you have [.NET Core Runtime correctly installed](Dotnet.md), you might have downloaded the CLi flavor, try [downloading the GUI](Getting-started.md#gui-or-cli) instead.
### How do I run DCE on macOS or Linux?
Check the following pages:
- [macOS usage instructions](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/MacOS.md)
- [Linux usage instructions](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Linux.md)
- [macOS usage instructions](MacOS.md)
- [Linux usage instructions](Linux.md)
### How can I set DCE to export automatically at certain times?
Check the following pages to learn how to schedule **DiscordChatExporter.CLI** runs (advanced):
- [Windows scheduling](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/scheduling-windows.md)
- [macOS scheduling](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/scheduling-MacOS.md)
- [Linux scheduling](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/scheduling-Linux.md)
- [Windows scheduling](Scheduling-Windows.md)
- [macOS scheduling](Scheduling-MacOS.md)
- [Linux scheduling](Scheduling-Linux.md)
### The exported file is too large, I can't open it
@@ -98,13 +93,13 @@ Try opening it with a different program, try partitioning or use a different fil
### DCE is crashing/failing
Check the following page: [Installing .NET Core Runtime](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Dotnet.md)
Check the following page: [Installing .NET Core Runtime](Dotnet.md)
If you already have .NET Core installed, please check if your problem is a [known issue](https://github.com/Tyrrrz/DiscordChatExporter/issues?q=is%3Aissue) before [opening a bug report](https://github.com/Tyrrrz/DiscordChatExporter/issues/new).
### .NET Core Runtime is required
Check the following page: [Installing .NET Core Runtime](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Dotnet.md)
Check the following page: [Installing .NET Core Runtime](Dotnet.md)
### I see messages in the export, but they have no content
@@ -116,25 +111,25 @@ Your bot is missing the 'Message Content Intent'. Go to the [Discord Developer P
Check the following page:
- [Using the CLI](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Getting-started.md#using-the-cli)
- [Using the CLI](Using-the-CLI.md)
If you're using **Docker**, please refer to the [Docker Usage Instructions](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Docker.md) instead.
If you're using **Docker**, please refer to the [Docker Usage Instructions](Docker.md) instead.
### Where can I find the 'Channel IDs'?
Check the following page:
- [Obtaining Channel IDs](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Token-and-IDs.md)
- [Obtaining Channel IDs](Token-and-IDs.md)
### I can't find Docker exported chats
Check the following page:
- [Docker usage instructions](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/docker)
- [Docker usage instructions](Docker.md)
### I can't export Direct Messages
Make sure you're [copying the DM Channel ID](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Token-and-IDs.md#how-to-get-a-direct-message-channel-id), not the person's user ID.
Make sure you're [copying the DM Channel ID](Token-and-IDs.md#how-to-get-a-direct-message-channel-id), not the person's user ID.
## Errors
@@ -149,7 +144,7 @@ DiscordChatExporter.Domain.Exceptions.DiscordChatExporterException: Authenticati
DiscordChatExporter.Domain.Exceptions.DiscordChatExporterException: Requested resource does not exist.
```
↳ Check your channel ID, it might be invalid. [Read this if you need help](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Token-and-IDs.md).
↳ Check your channel ID, it might be invalid. [Read this if you need help](Token-and-IDs.md).
```console
DiscordChatExporter.Domain.Exceptions.DiscordChatExporterException: Access is forbidden.
+130 -110
View File
@@ -3,8 +3,8 @@
## Guide
> **Note**:
> Make sure you have [.NET Core installed](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Dotnet.md) before attempting to run the commands below.
> **Docker** users, please refer to the [Docker usage instructions](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Docker.md).
> Make sure you have [.NET Core installed](Dotnet.md) before attempting to run the commands below.
> **Docker** users, please refer to the [Docker usage instructions](Docker.md).
## Step 1
@@ -25,7 +25,8 @@ You can also drag and drop the folder on **every platform**.
## Step 3
Now we're ready to run the commands. The examples on this page follow the Windows file path format, change the file paths according to your system.
Now we're ready to run the commands. The examples on this page follow the Windows file path format, change the file
paths according to your system.
Type the following in Command Prompt (Terminal), then press ENTER to run it. This will list DCE's options.
@@ -33,22 +34,23 @@ Type the following in Command Prompt (Terminal), then press ENTER to run it. Thi
dotnet DiscordChatExporter.Cli.dll
```
> **Docker** users, please refer to the [Docker usage instructions](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Docker.md).
> **Docker** users, please refer to the [Docker usage instructions](Docker.md).
## CLI commands
| Command | Description |
| ------------------------------------------------------------------------------------------------------------- | --------------------------------------------------- |
| [export](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Getting-started.md#export) | Exports a channel |
| [exportdm](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Getting-started.md#exportdm) | Exports all direct message channels |
| [exportguild](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Getting-started.md#exportguild) | Exports all channels within the specified server |
| [exportall](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Getting-started.md#exportall) | Exports all accessible channels |
| [channels](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Getting-started.md#channels) | Outputs the list of channels in the given server |
| [dm](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Getting-started.md#dm) | Outputs the list of direct message channels |
| [guilds](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Getting-started.md#guilds) | Outputs the list of accessible servers |
| [guide](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Getting-started.md#guide) | Explains how to obtain token, guild, and channel ID |
| Command | Description |
|-----------------------------|-----------------------------------------------------|
| [export](#export) | Exports a channel |
| [exportdm](#exportdm) | Exports all direct message channels |
| [exportguild](#exportguild) | Exports all channels within the specified server |
| [exportall](#exportall) | Exports all accessible channels |
| [channels](#channels) | Outputs the list of channels in the given server |
| [dm](#dm) | Outputs the list of direct message channels |
| [guilds](#guilds) | Outputs the list of accessible servers |
| [guide](#guide) | Explains how to obtain token, guild, and channel ID |
To use the commands, you'll need a token. For the instructions on how to get a token, please refer to [this page](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Token-and-IDs.md), or run `dotnet DiscordChatExporter.Cli.dll guide`.
To use the commands, you'll need a token. For the instructions on how to get a token, please refer
to [this page](Token-and-IDs.md), or run `dotnet DiscordChatExporter.Cli.dll guide`.
To get help with a specific command, please run:
```console
@@ -59,21 +61,21 @@ dotnet DiscordChatExporter.Cli.dll command -h
With this command, you can export **server channels** and **direct messages**.
| | Option | Description |
| ------ | --------------------------------------------------------------------------------------------------------------------- | ----------- |
| **\*** | [-c](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Getting-started.md#basic-usage) | [Channel ID(s)](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Token-and-IDs.md#how-to-get-guild-id-or-guild-channel-id) |
| **\*** | [-t](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Getting-started.md#basic-usage) | [Authorization token](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Token-and-IDs.md#how-to-get-user-token). Environment variable: `DISCORD_TOKEN` |
| | [-o](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Getting-started.md#changing-the-output-filename) | Output file or directory path |
| | [-f](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Getting-started.md#changing-the-format) | [Output file format](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Getting-started.md#file-formats). Default: HtmlDark |
| | [--after](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Using-the-CLI.md#date-ranges) | Only include messages sent after this date |
| | [--before](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Using-the-CLI.md#date-ranges) | Only include messages sent before this date |
| | [-p](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Using-the-CLI.md#partitioning) | Split output into partitions, each limited to this number of messages (e.g. 100) or file size (e.g. 10mb) |
| | [--filter](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Message-filters.md) | Special notation for filtering the messages that get included in the export |
| | [--media](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Using-the-CLI.md#downloading-assets) | Download assets referenced by the export (user avatars, attached files, embedded images, etc.). Default: false |
| | [--reuse-media](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Using-the-CLI.md#reuse-assets) | Reuse previously downloaded assets to avoid redundant requests. Default: false |
| | [--media-dir](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Using-the-CLI.md#downloading-assets) | Directory to store assets from all exported channels in the same place |
| | [--locale](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Using-the-CLI.md#changing-the-date-format) | Format used when writing dates. Default: en-US |
| | -h | Shows help text |
| | Option | Description |
|--------|---------------------------------------|----------------------------------------------------------------------------------------------------------------|
| **\*** | [-c](#basic-usage) | [Channel ID(s)](Token-and-IDs.md#how-to-get-guild-id-or-guild-channel-id) |
| **\*** | [-t](#basic-usage) | [Authorization token](Token-and-IDs.md#how-to-get-user-token). Environment variable: `DISCORD_TOKEN` |
| | [-o](#changing-the-output-filename) | Output file or directory path |
| | [-f](#changing-the-format) | [Output file format](Getting-started.md#file-formats). Default: HtmlDark |
| | [--after](#date-ranges) | Only include messages sent after this date |
| | [--before](#date-ranges) | Only include messages sent before this date |
| | [-p](#partitioning) | Split output into partitions, each limited to this number of messages (e.g. 100) or file size (e.g. 10mb) |
| | [--filter](Message-filters.md) | Special notation for filtering the messages that get included in the export |
| | [--media](#downloading-assets) | Download assets referenced by the export (user avatars, attached files, embedded images, etc.). Default: false |
| | [--reuse-media](#reuse-assets) | Reuse previously downloaded assets to avoid redundant requests. Default: false |
| | [--media-dir](#downloading-assets) | Directory to store assets from all exported channels in the same place |
| | [--locale](#changing-the-date-format) | Format used when writing dates. Default: en-US |
| | -h | Shows help text |
> **Note**:
> Options with an asterisk (**\***) are required. The order of the options doesn't matter.
@@ -88,7 +90,8 @@ dotnet DiscordChatExporter.Cli.dll export -t "mfa.Ifrn" -c 53555
#### Changing the format
You can change the export format to `HtmlDark`, `HtmlLight`, `PlainText` `Json` or `Csv` with `-f format`. The default format is `HtmlDark`.
You can change the export format to `HtmlDark`, `HtmlLight`, `PlainText` `Json` or `Csv` with `-f format`. The default
format is `HtmlDark`.
```console
dotnet DiscordChatExporter.Cli.dll export -t "mfa.Ifrn" -c 53555 -f Json
@@ -104,7 +107,8 @@ dotnet DiscordChatExporter.Cli.dll export -t "mfa.Ifrn" -c 53555 -o myserver.htm
#### Changing the output directory
You can change the export directory by using `-o` and providing a path that ends with a slash or does not have a file extension.
You can change the export directory by using `-o` and providing a path that ends with a slash or does not have a file
extension.
If any of the folders in the path have a space in its name, escape them with quotes (").
```console
@@ -129,7 +133,9 @@ You can use template tokens to generate the output file path based on the guild
dotnet DiscordChatExporter.Cli.dll export -t "mfa.Ifrn" -c 53555 -o "C:\Discord Exports\%G\%T\%C.html"
```
Assuming you are exporting a channel named `"my-channel"` in the `"Text channels"` category from a server called `"My server"`, you will get the following output file path: `C:\Discord Exports\My server\Text channels\my-channel.html`
Assuming you are exporting a channel named `"my-channel"` in the `"Text channels"` category from a server
called `"My server"`, you will get the following output file
path: `C:\Discord Exports\My server\Text channels\my-channel.html`
Here is the full list of supported template tokens:
@@ -163,7 +169,8 @@ dotnet DiscordChatExporter.Cli.dll export -t "mfa.Ifrn" -c 53555 -p 20mb
#### Downloading assets
If this option is set, the export will include additional files such as user avatars, attached files, images, etc.
Only files that are referenced by the export are downloaded, which means that, for example, user avatars will not be downloaded when using the plain text (TXT) export format.
Only files that are referenced by the export are downloaded, which means that, for example, user avatars will not be
downloaded when using the plain text (TXT) export format.
A folder containing the assets will be created along with the exported chat. They must be kept together.
```console
@@ -172,7 +179,8 @@ dotnet DiscordChatExporter.Cli.dll export -t "mfa.Ifrn" -c 53555 --media
#### Reusing assets
Previously downloaded assets can be reused to skip redundant downloads as long as the chat is always exported to the same folder. Using this option can speed up future exports. This option requires the `--media` option.
Previously downloaded assets can be reused to skip redundant downloads as long as the chat is always exported to the
same folder. Using this option can speed up future exports. This option requires the `--media` option.
```console
dotnet DiscordChatExporter.Cli.dll export -t "mfa.Ifrn" -c 53555 --media --reuse-media
@@ -180,7 +188,8 @@ dotnet DiscordChatExporter.Cli.dll export -t "mfa.Ifrn" -c 53555 --media --reuse
#### Changing the media directory
By default, the media directory is created alongside the exported chat. You can change this by using `--media-dir` and providing a path that ends with a slash. All of the exported media will be stored in this directory.
By default, the media directory is created alongside the exported chat. You can change this by using `--media-dir` and
providing a path that ends with a slash. All of the exported media will be stored in this directory.
```console
dotnet DiscordChatExporter.Cli.dll export -t "mfa.Ifrn" -c 53555 --media --media-dir "C:\Discord Media"
@@ -188,7 +197,8 @@ dotnet DiscordChatExporter.Cli.dll export -t "mfa.Ifrn" -c 53555 --media --media
#### Changing the date format
You can customize how dates are formatted in the exported files by using `--locale` and inserting one of Discord's locales. The default locale is `en-US`.
You can customize how dates are formatted in the exported files by using `--locale` and inserting one of Discord's
locales. The default locale is `en-US`.
```console
dotnet DiscordChatExporter.Cli.dll export -t "mfa.Ifrn" -c 53555 --locale "de-DE"
@@ -211,35 +221,38 @@ dotnet DiscordChatExporter.Cli.dll export -t "mfa.Ifrn" -c 53555 --after "2019-0
```
**Messages sent in a date range**
Use `--before` and `--after` to export messages sent during the provided date range. E.g. messages sent between September 17th, 2019 11:34 PM and September 18th:
Use `--before` and `--after` to export messages sent during the provided date range. E.g. messages sent between
September 17th, 2019 11:34 PM and September 18th:
```console
dotnet DiscordChatExporter.Cli.dll export -t "mfa.Ifrn" -c 53555 --after "2019-09-17 23:34" --before "2019-09-18"
```
You can try different formats like `17-SEP-2019 11:34 PM` or even refine your ranges down to milliseconds `17-SEP-2019 23:45:30.6170`!
You can try different formats like `17-SEP-2019 11:34 PM` or even refine your ranges down to
milliseconds `17-SEP-2019 23:45:30.6170`!
Don't forget to quote (") the date if it has spaces!
More info about .NET date formats [here](https://docs.microsoft.com/en-us/dotnet/standard/base-types/custom-date-and-time-format-strings).
More info about .NET date
formats [here](https://docs.microsoft.com/en-us/dotnet/standard/base-types/custom-date-and-time-format-strings).
### `exportdm`
This command exports all your **direct messages**.
| | Option | Description |
| ------ | --------------------------------------------------------------------------------------------------------------------- | ----------- |
| **\*** | [-c](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Getting-started.md#basic-usage) | [Channel ID(s)](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Token-and-IDs.md#how-to-get-guild-id-or-guild-channel-id) |
| **\*** | [-t](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Getting-started.md#basic-usage) | [Authorization token](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Token-and-IDs.md#how-to-get-user-token). Environment variable: `DISCORD_TOKEN` |
| | [-o](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Getting-started.md#changing-the-output-filename) | Output file or directory path |
| | [-f](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Getting-started.md#changing-the-format) | [Output file format](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Getting-started.md#file-formats). Default: HtmlDark |
| | [--after](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Using-the-CLI.md#date-ranges) | Only include messages sent after this date |
| | [--before](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Using-the-CLI.md#date-ranges) | Only include messages sent before this date |
| | [-p](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Using-the-CLI.md#partitioning) | Split output into partitions, each limited to this number of messages (e.g. 100) or file size (e.g. 10mb) |
| | [--filter](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Message-filters.md) | Special notation for filtering the messages that get included in the export |
| | [--media](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Using-the-CLI.md#downloading-assets) | Download assets referenced by the export (user avatars, attached files, embedded images, etc.). Default: false |
| | [--reuse-media](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Using-the-CLI.md#reuse-assets) | Reuse previously downloaded assets to avoid redundant requests. Default: false |
| | [--media-dir](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Using-the-CLI.md#downloading-assets) | Directory to store assets from all exported channels in the same place |
| | [--locale](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Using-the-CLI.md#changing-the-date-format) | Format used when writing dates. Default: en-US |
| | -h | Shows help text |
| | Option | Description |
|--------|---------------------------------------|----------------------------------------------------------------------------------------------------------------|
| **\*** | [-c](#basic-usage) | [Channel ID(s)](Token-and-IDs.md#how-to-get-guild-id-or-guild-channel-id) |
| **\*** | [-t](#basic-usage) | [Authorization token](Token-and-IDs.md#how-to-get-user-token). Environment variable: `DISCORD_TOKEN` |
| | [-o](#changing-the-output-filename) | Output file or directory path |
| | [-f](#changing-the-format) | [Output file format](Getting-started.md#file-formats). Default: HtmlDark |
| | [--after](#date-ranges) | Only include messages sent after this date |
| | [--before](#date-ranges) | Only include messages sent before this date |
| | [-p](#partitioning) | Split output into partitions, each limited to this number of messages (e.g. 100) or file size (e.g. 10mb) |
| | [--filter](Message-filters.md) | Special notation for filtering the messages that get included in the export |
| | [--media](#downloading-assets) | Download assets referenced by the export (user avatars, attached files, embedded images, etc.). Default: false |
| | [--reuse-media](#reuse-assets) | Reuse previously downloaded assets to avoid redundant requests. Default: false |
| | [--media-dir](#downloading-assets) | Directory to store assets from all exported channels in the same place |
| | [--locale](#changing-the-date-format) | Format used when writing dates. Default: en-US |
| | -h | Shows help text |
> **Note**:
> Options with an asterisk (**\***) are required. The order of the options doesn't matter.
@@ -248,30 +261,32 @@ This command exports all your **direct messages**.
This command exports all channels of a **server**.
| | Option | Description |
| ------ | ------------------------------------------------------------------------------------------------------------------------- | ----------- |
| **\*** | -g | [Server ID](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Token-and-IDs.md#how-to-get-guild-id-or-guild-channel-id) |
| **\*** | [-t](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Getting-started.md#basic-usage) | [Authorization token](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Token-and-IDs.md#how-to-get-user-token). Environment variable: `DISCORD_TOKEN` |
| | [-o](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Getting-started.md#changing-the-output-filename) | Output file or directory path |
| | [-f](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Getting-started.md#changing-the-format) | [Output file format](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Getting-started.md#file-formats). Default: HtmlDark |
| | [--after](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Using-the-CLI.md#date-ranges) | Only include messages sent after this date |
| | [--before](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Using-the-CLI.md#date-ranges) | Only include messages sent before this date |
| | [-p](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Using-the-CLI.md#partitioning) | Split output into partitions, each limited to this number of messages (e.g. 100) or file size (e.g. 10mb) |
| | [--filter](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Message-filters.md) | Special notation for filtering the messages that get included in the export |
| | [--media](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Using-the-CLI.md#downloading-assets) | Download assets referenced by the export (user avatars, attached files, embedded images, etc.). Default: false |
| | [--reuse-media](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Using-the-CLI.md#reuse-assets) | Reuse previously downloaded assets to avoid redundant requests. Default: false |
| | [--media-dir](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Using-the-CLI.md#downloading-assets) | Directory to store assets from all exported channels in the same place |
| | [--locale](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Using-the-CLI.md#changing-the-date-format) | Format used when writing dates. Default: en-US |
| | [--include-threads](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Using-the-CLI.md#including-threads) | Specify whether to include threads (and which type) in the export. By default, threads are not included. It has possible values of `none`, `active`, or `all`, indicating which threads should be included. To include both active and archived threads, use `--include-threads all`. |
| | [--include-vc](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Using-the-CLI.md#including-voice-channels) | Include voice channels in the export. Default: true |
| | -h | Shows help text |
| | Option | Description |
|--------|-------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **\*** | -g | [Server ID](Token-and-IDs.md#how-to-get-guild-id-or-guild-channel-id) |
| **\*** | [-t](#basic-usage) | [Authorization token](Token-and-IDs.md#how-to-get-user-token). Environment variable: `DISCORD_TOKEN` |
| | [-o](#changing-the-output-filename) | Output file or directory path |
| | [-f](#changing-the-format) | [Output file format](Getting-started.md#file-formats). Default: HtmlDark |
| | [--after](#date-ranges) | Only include messages sent after this date |
| | [--before](#date-ranges) | Only include messages sent before this date |
| | [-p](#partitioning) | Split output into partitions, each limited to this number of messages (e.g. 100) or file size (e.g. 10mb) |
| | [--filter](Message-filters.md) | Special notation for filtering the messages that get included in the export |
| | [--media](#downloading-assets) | Download assets referenced by the export (user avatars, attached files, embedded images, etc.). Default: false |
| | [--reuse-media](#reuse-assets) | Reuse previously downloaded assets to avoid redundant requests. Default: false |
| | [--media-dir](#downloading-assets) | Directory to store assets from all exported channels in the same place |
| | [--locale](#changing-the-date-format) | Format used when writing dates. Default: en-US |
| | [--include-threads](#including-threads) | Specify whether to include threads (and which type) in the export. By default, threads are not included. It has possible values of `none`, `active`, or `all`, indicating which threads should be included. To include both active and archived threads, use `--include-threads all`. |
| | [--include-vc](#including-voice-channels) | Include voice channels in the export. Default: true |
| | -h | Shows help text |
> **Note**:
> Options with an asterisk (**\***) are required. The order of the options doesn't matter.
#### Including threads
By default, threads are not included in the export. You can change this behavior by using `--include-threads` and specifying which threads should be included. It has possible values of `none`, `active`, or `all`, indicating which threads should be included. To include both active and archived threads, use `--include-threads all`.
By default, threads are not included in the export. You can change this behavior by using `--include-threads` and
specifying which threads should be included. It has possible values of `none`, `active`, or `all`, indicating which
threads should be included. To include both active and archived threads, use `--include-threads all`.
```console
dotnet DiscordChatExporter.Cli.dll exportguild -t "mfa.Ifrn" -g 21814 --include-threads all
@@ -279,7 +294,9 @@ dotnet DiscordChatExporter.Cli.dll exportguild -t "mfa.Ifrn" -g 21814 --include-
#### Including voice channels
By default, voice channels are included in the export. You can change this behavior by using `--include-vc` and specifying whether to include voice channels in the export. It has possible values of `true` or `false`, to exclude voice channels, use `--include-vc false`.
By default, voice channels are included in the export. You can change this behavior by using `--include-vc` and
specifying whether to include voice channels in the export. It has possible values of `true` or `false`, to exclude
voice channels, use `--include-vc false`.
```console
dotnet DiscordChatExporter.Cli.dll exportguild -t "mfa.Ifrn" -g 21814 --include-vc false
@@ -289,23 +306,23 @@ dotnet DiscordChatExporter.Cli.dll exportguild -t "mfa.Ifrn" -g 21814 --include-
This command exports **all accessible channels**, including server channels and DMs.
| | Option | Description |
| ------ | ------------------------------------------------------------------------------------------------------------------------- | ----------- |
| **\*** | [-t](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Getting-started.md#basic-usage) | [Authorization token](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Token-and-IDs.md#how-to-get-user-token). Environment variable: `DISCORD_TOKEN` |
| | [-o](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Getting-started.md#changing-the-output-filename) | Output file or directory path |
| | [-f](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Getting-started.md#changing-the-format) | [Output file format](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Getting-started.md#file-formats). Default: HtmlDark |
| | [--after](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Using-the-CLI.md#date-ranges) | Only include messages sent after this date |
| | [--before](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Using-the-CLI.md#date-ranges) | Only include messages sent before this date |
| | [-p](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Using-the-CLI.md#partitioning) | Split output into partitions, each limited to this number of messages (e.g. 100) or file size (e.g. 10mb) |
| | [--filter](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Message-filters.md) | Special notation for filtering the messages that get included in the export |
| | [--media](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Using-the-CLI.md#downloading-assets) | Download assets referenced by the export (user avatars, attached files, embedded images, etc.). Default: false |
| | [--reuse-media](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Using-the-CLI.md#reuse-assets) | Reuse previously downloaded assets to avoid redundant requests. Default: false |
| | [--media-dir](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Using-the-CLI.md#downloading-assets) | Directory to store assets from all exported channels in the same place |
| | [--locale](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Using-the-CLI.md#changing-the-date-format) | Format used when writing dates. Default: en-US |
| | [--include-threads](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Using-the-CLI.md#including-threads) | Specify whether to include threads (and which type) in the export. By default, threads are not included. It has possible values of `none`, `active`, or `all`, indicating which threads should be included. To include both active and archived threads, use `--include-threads all`. |
| | [--include-vc](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Using-the-CLI.md#including-voice-channels) | Include voice channels in the export. Default: true |
| | [--include-dm](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Using-the-CLI.md#excluding-dms) | Include direct messages in the export. Default: true |
| | -h | Shows help text |
| | Option | Description |
|--------|-------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **\*** | [-t](#basic-usage) | [Authorization token](Token-and-IDs.md#how-to-get-user-token). Environment variable: `DISCORD_TOKEN` |
| | [-o](#changing-the-output-filename) | Output file or directory path |
| | [-f](#changing-the-format) | [Output file format](Getting-started.md#file-formats). Default: HtmlDark |
| | [--after](#date-ranges) | Only include messages sent after this date |
| | [--before](#date-ranges) | Only include messages sent before this date |
| | [-p](#partitioning) | Split output into partitions, each limited to this number of messages (e.g. 100) or file size (e.g. 10mb) |
| | [--filter](Message-filters.md) | Special notation for filtering the messages that get included in the export |
| | [--media](#downloading-assets) | Download assets referenced by the export (user avatars, attached files, embedded images, etc.). Default: false |
| | [--reuse-media](#reuse-assets) | Reuse previously downloaded assets to avoid redundant requests. Default: false |
| | [--media-dir](#downloading-assets) | Directory to store assets from all exported channels in the same place |
| | [--locale](#changing-the-date-format) | Format used when writing dates. Default: en-US |
| | [--include-threads](#including-threads) | Specify whether to include threads (and which type) in the export. By default, threads are not included. It has possible values of `none`, `active`, or `all`, indicating which threads should be included. To include both active and archived threads, use `--include-threads all`. |
| | [--include-vc](#including-voice-channels) | Include voice channels in the export. Default: true |
| | [--include-dm](#excluding-dms) | Include direct messages in the export. Default: true |
| | -h | Shows help text |
> **Note**:
> Options with an asterisk (**\***) are required. The order of the options doesn't matter.
@@ -324,18 +341,19 @@ This command outputs a **server's channels** in the following format:
`Channel ID | Channel Name`
To save the output to a file, run the command below. If the file already exists, it will be overwritten.
| | Option | Description |
| ------ | ---------------------------------------------------------------------------------------------------- | ----------- |
| **\*** | -g | [Server ID](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Token-and-IDs.md#how-to-get-guild-id-or-guild-channel-id) |
| **\*** | [-t](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Getting-started.md#basic-usage) | [Authorization token](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Token-and-IDs.md#how-to-get-user-token). Environment variable: `DISCORD_TOKEN` |
| | [--include-threads](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Using-the-CLI.md#including-threads) | Specify whether to include threads (and which type) in the output. By default, threads are not included. It has possible values of `none`, `active`, or `all`, indicating which threads should be included. To include both active and archived threads, use `--include-threads all`. |
| | [--include-vc](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Using-the-CLI.md#including-voice-channels) | Include voice channels in the output. Default: true |
| | -h | Shows help text |
| | Option | Description |
|--------|-------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **\*** | -g | [Server ID](Token-and-IDs.md#how-to-get-guild-id-or-guild-channel-id) |
| **\*** | [-t](#basic-usage) | [Authorization token](Token-and-IDs.md#how-to-get-user-token). Environment variable: `DISCORD_TOKEN` |
| | [--include-threads](#including-threads) | Specify whether to include threads (and which type) in the output. By default, threads are not included. It has possible values of `none`, `active`, or `all`, indicating which threads should be included. To include both active and archived threads, use `--include-threads all`. |
| | [--include-vc](#including-voice-channels) | Include voice channels in the output. Default: true |
| | -h | Shows help text |
> **Note**:
> Options with an asterisk (**\***) are required. The order of the options doesn't matter.
To save the output to a file, run the command with `> C:\path\…` like the example below. If the file already exists, it will be overwritten.
To save the output to a file, run the command with `> C:\path\…` like the example below. If the file already exists, it
will be overwritten.
```console
dotnet DiscordChatExporter.Cli.dll channels -t "mfa.Ifrn" -g 21814 > C:\path\to\output.txt
@@ -345,15 +363,16 @@ dotnet DiscordChatExporter.Cli.dll channels -t "mfa.Ifrn" -g 21814 > C:\path\to\
This command outputs a list of your **direct messages**.
| | Option | Description |
| ------ | ---------------------------------------------------------------------------------------------------- | ----------- |
| **\*** | [-t](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Getting-started.md#basic-usage) | [Authorization token](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Token-and-IDs.md#how-to-get-user-token). Environment variable: `DISCORD_TOKEN` |
| | -h | Shows help text |
| | Option | Description |
|--------|--------------------|------------------------------------------------------------------------------------------------------|
| **\*** | [-t](#basic-usage) | [Authorization token](Token-and-IDs.md#how-to-get-user-token). Environment variable: `DISCORD_TOKEN` |
| | -h | Shows help text |
> **Note**:
> Options with an asterisk (**\***) are required. The order of the options doesn't matter.
To save the output to a file, run the command with `> C:\path\…` like the example below. If the file already exists, it will be overwritten.
To save the output to a file, run the command with `> C:\path\…` like the example below. If the file already exists, it
will be overwritten.
```console
dotnet DiscordChatExporter.Cli.dll dm -t "mfa.Ifrn" > C:\path\to\output.txt
@@ -363,15 +382,16 @@ dotnet DiscordChatExporter.Cli.dll dm -t "mfa.Ifrn" > C:\path\to\output.txt
This command outputs a list of your **servers**.
| | Option | Description |
| ------ | ---------------------------------------------------------------------------------------------------- | ----------- |
| **\*** | [-t](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Getting-started.md#basic-usage) | [Authorization token](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Token-and-IDs.md#how-to-get-user-token). Environment variable: `DISCORD_TOKEN` |
| | -h | Shows help text |
| | Option | Description |
|--------|--------------------|------------------------------------------------------------------------------------------------------|
| **\*** | [-t](#basic-usage) | [Authorization token](Token-and-IDs.md#how-to-get-user-token). Environment variable: `DISCORD_TOKEN` |
| | -h | Shows help text |
> **Note**:
> Options with an asterisk (**\***) are required. The order of the options doesn't matter.
To save the output to a file, run the command with `> C:\path\…` like the example below. If the file already exists, it will be overwritten.
To save the output to a file, run the command with `> C:\path\…` like the example below. If the file already exists, it
will be overwritten.
```console
dotnet DiscordChatExporter.Cli.dll guilds -t "mfa.Ifrn" > C:\path\to\output.txt
@@ -379,4 +399,4 @@ dotnet DiscordChatExporter.Cli.dll guilds -t "mfa.Ifrn" > C:\path\to\output.txt
### `guide`
This command explains [how to get your token, guild, and channel IDs](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Token-and-IDs.md).
This command explains [how to get your token, guild, and channel IDs](Token-and-IDs.md).
+1 -1
View File
@@ -46,7 +46,7 @@ In this screen you can customize the following:
- **Partition limit** (Optional) - Split output into partitions, each limited to this number of messages (e.g. 100) or file size (e.g. 10mb). For example, a channel with 36 messages set to be partitioned every 10 messages will output 4 files.
- **Message Filter** (Optional) - Special notation for filtering the messages that get included in the export. See [Message filters](https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Message-filters.md) for more info.
- **Message Filter** (Optional) - Special notation for filtering the messages that get included in the export. See [Message filters](Message-filters.md) for more info.
- **Format markdown** (Optional) - Disable markdown processing when exporting. You can use this to produce JSON or plain text exports without unwrapping mentions, custom emoji, and certain other special tokens.
+13 -35
View File
@@ -1,8 +1,12 @@
name: docker
on: [push, pull_request]
on:
push:
pull_request:
jobs:
# Outputs from this job aren't really used, but it's here to verify that
# the Dockerfile builds correctly on pull requests.
build:
runs-on: ubuntu-latest
permissions:
@@ -18,20 +22,21 @@ jobs:
- name: Build image
run: >
docker buildx build
docker buildx build .
--file DiscordChatExporter.Cli.dockerfile
--platform linux/amd64,linux/arm64
--output type=tar,dest=DiscordChatExporter.Cli.Docker.tar
.
- name: Upload artifacts
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # 3.1.3
with:
name: DiscordChatExporter.Cli.Docker
path: DiscordChatExporter.Cli.Docker.tar
if-no-files-found: error
deploy-latest:
if: ${{ github.event_name == 'push' && github.ref_type == 'branch' && github.ref_name == 'master' }}
deploy:
# Deploy to DockerHub only on tag push or master branch push
if: ${{ github.event_name == 'push' && (github.ref_type == 'tag' || github.ref_type == 'branch' && github.ref_name == 'master') }}
runs-on: ubuntu-latest
permissions:
contents: read
@@ -50,37 +55,10 @@ jobs:
- name: Build & push image
run: >
docker buildx build
docker buildx build .
--file DiscordChatExporter.Cli.dockerfile
--platform linux/amd64,linux/arm64
--push
--tag tyrrrz/discordchatexporter:latest
.
deploy-stable:
if: ${{ github.event_name == 'push' && github.ref_type == 'tag' }}
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Checkout
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # 4.1.0
- name: Install Docker Buildx
uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # 3.0.0
- name: Login to DockerHub
run: >
echo ${{ secrets.DOCKER_TOKEN }} |
docker login --username tyrrrz --password-stdin
- name: Build & push image
run: >
docker buildx build
--file DiscordChatExporter.Cli.dockerfile
--platform linux/amd64,linux/arm64
--push
--tag tyrrrz/discordchatexporter:stable
--tag tyrrrz/discordchatexporter:${{ github.ref_name }}
.
${{ github.ref_type == 'tag' && '--tag tyrrrz/discordchatexporter:$GITHUB_REF_NAME' || '' }}
${{ github.ref_type == 'tag' && '--tag tyrrrz/discordchatexporter:stable' || '' }}
+65 -57
View File
@@ -1,6 +1,8 @@
name: main
on: [push, pull_request]
on:
push:
pull_request:
env:
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
@@ -20,11 +22,13 @@ jobs:
- name: Install .NET
uses: actions/setup-dotnet@3447fd6a9f9e57506b15f895c5b76d3b197dc7c2 # 3.2.0
with:
dotnet-version: 7.0.x
dotnet-version: 8.0.x
- name: Run tests
# Tests need access to secrets, so we can't run them against PRs because of limited trust
if: ${{ github.event_name != 'pull_request' }}
env:
DISCORD_TOKEN: ${{ secrets.DISCORD_TOKEN }}
run: >
dotnet test
--configuration Release
@@ -33,8 +37,6 @@ jobs:
--
RunConfiguration.CollectSourceInformation=true
DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.Format=opencover
env:
DISCORD_TOKEN: ${{ secrets.DISCORD_TOKEN }}
- name: Upload coverage
uses: codecov/codecov-action@eaaf4bedf32dbdc6b720b63067d99c4d77d6047d # 3.1.4
@@ -42,6 +44,12 @@ jobs:
token: ${{ secrets.CODECOV_TOKEN }}
pack:
strategy:
matrix:
app:
- DiscordChatExporter.Cli
- DiscordChatExporter.Gui
runs-on: windows-latest
permissions:
actions: write
@@ -54,82 +62,82 @@ jobs:
- name: Install .NET
uses: actions/setup-dotnet@3447fd6a9f9e57506b15f895c5b76d3b197dc7c2 # 3.2.0
with:
dotnet-version: 7.0.x
dotnet-version: 8.0.x
- name: Publish (CLI)
- name: Publish app
run: >
dotnet publish DiscordChatExporter.Cli
--output DiscordChatExporter.Cli/publish/
dotnet publish ${{ matrix.app }}
--output ${{ matrix.app }}/bin/publish/
--configuration Release
- name: Publish (GUI)
run: >
dotnet publish DiscordChatExporter.Gui
--output DiscordChatExporter.Gui/publish/
--configuration Release
- name: Upload artifacts (CLI)
- name: Upload artifacts
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # 3.1.3
with:
name: DiscordChatExporter.Cli
path: DiscordChatExporter.Cli/publish/
name: ${{ matrix.app }}
path: ${{ matrix.app }}/bin/publish/
if-no-files-found: error
- name: Upload artifacts (GUI)
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # 3.1.3
with:
name: DiscordChatExporter
path: DiscordChatExporter.Gui/publish/
deploy:
release:
if: ${{ github.event_name == 'push' && github.ref_type == 'tag' }}
needs:
- test
- pack
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Create release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: >
gh release create ${{ github.ref_name }}
--repo ${{ github.event.repository.full_name }}
--title ${{ github.ref_name }}
--notes "[Changelog](${{ github.event.repository.html_url }}/blob/${{ github.ref_name }}/Changelog.md)"
--verify-tag
deploy:
needs: release
strategy:
matrix:
app:
- DiscordChatExporter.Cli
- DiscordChatExporter.Gui
include:
- app: DiscordChatExporter.Cli
asset: DiscordChatExporter.Cli
- app: DiscordChatExporter.Gui
# GUI asset isn't suffixed, unlike the CLI asset
asset: DiscordChatExporter
runs-on: ubuntu-latest
permissions:
actions: read
contents: write
steps:
- name: Download artifacts (CLI)
- name: Download artifacts
uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # 3.0.2
with:
name: DiscordChatExporter.Cli
path: DiscordChatExporter.Cli
name: ${{ matrix.app }}
path: ${{ matrix.app }}/
- name: Download artifacts (GUI)
uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # 3.0.2
with:
name: DiscordChatExporter
path: DiscordChatExporter.Gui
- name: Create package
# Change into the artifacts directory to avoid including the directory itself in the zip archive
working-directory: ${{ matrix.app }}/
run: zip -r ../${{ matrix.asset }}.zip .
- name: Create package (CLI)
shell: pwsh
run: >
Compress-Archive
-Path DiscordChatExporter.Cli/*
-DestinationPath DiscordChatExporter.Cli.zip
-Force
- name: Create package (GUI)
shell: pwsh
run: >
Compress-Archive
-Path DiscordChatExporter.Gui/*
-DestinationPath DiscordChatExporter.zip
-Force
- name: Create release
- name: Upload release asset
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: >
gh release create "${{ github.ref_name }}"
"DiscordChatExporter.Cli.zip"
"DiscordChatExporter.zip"
--repo "${{ github.event.repository.full_name }}"
--title "${{ github.ref_name }}"
--notes "[Changelog](${{ github.event.repository.html_url }}/blob/${{ github.ref_name }}/Changelog.md)"
--verify-tag
gh release upload ${{ github.ref_name }}
${{ matrix.asset }}.zip
--repo ${{ github.event.repository.full_name }}
notify:
needs: deploy
@@ -148,6 +156,6 @@ jobs:
body: |
{
"avatar_url": "https://raw.githubusercontent.com/${{ github.event.repository.full_name }}/${{ github.ref_name }}/favicon.png",
"content": "**${{ github.event.repository.name }}** new version released!\nVersion: `${{ github.ref_name }}`\nDownload: <${{ github.event.repository.html_url }}/releases/tag/${{ github.ref_name }}>\nChangelog: <${{ github.event.repository.html_url }}/blob/${{ github.ref_name }}/Changelog.md>"
"content": "**${{ github.event.repository.name }}** v${{ github.ref_name }} has been released!\n🔗 [Download](<${{ github.event.repository.html_url }}/releases/tag/${{ github.ref_name }}>) • [Changelog](<${{ github.event.repository.html_url }}/blob/${{ github.ref_name }}/Changelog.md>)"
}
retry-count: 5
+6 -17
View File
@@ -1,23 +1,12 @@
# User-specific files
.vs/
.idea/
*.suo
*.user
*.userosscache
*.sln.docstates
.idea/
.vs/
.vscode/
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
[Xx]64/
[Xx]86/
[Bb]uild/
bld/
[Bb]in/
[Oo]bj/
bin/
obj/
# Coverage
*.opencover.xml
# Test results
TestResults/
+30
View File
@@ -1,5 +1,35 @@
# Changelog
## v2.42.4 (16-Nov-2023)
- General changes:
- Switched from .NET 7.0 to .NET 8.0. If running on Windows, the application should update all required prerequisites automatically. Alternatively, you can download the latest version of the runtime for your system [here](https://dotnet.microsoft.com/download/dotnet/8.0).
- CLI changes:
- Added a message that is displayed when the application is running with the output stream redirected, informing the user that they will not get rich progress reporting in this mode.
- GUI changes:
- Changed the presentation of the "Reuse assets" and "Assets path" options, such that the corresponding controls are now disabled if the "Download assets" option is not enabled.
- HTML changes:
- Fixed an issue where exporting a channel with a custom assets path could result in an HTML output that did not render properly in Firefox.
## v2.42.3 (09-Nov-2023)
- JSON changes:
- Added the `video` field to the embed object, which may contain additional information about an embedded video resource.
- CSV changes:
- Fixed an issue where the `Date` field was incorrectly formatted. Now, the dates follow the `ISO 8601` standard.
## v2.42.2 (26-Oct-2023)
- General changes:
- Fixed an issue where trying to export a channel that contained application interactions with empty command names crashed the application.
## v2.42.1 (19-Oct-2023)
- General changes:
- Improved the error message shown when trying to export a forum channel. Such channels cannot be exported directly, you need to fetch and export their individual threads instead.
- CLI changes:
- Fixed an issue where fetching threads with `[` or `]` in their name crashed the application.
## v2.42 (12-Oct-2023)
- General changes:
+2 -2
View File
@@ -1,8 +1,8 @@
<Project>
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<Version>2.42</Version>
<TargetFramework>net8.0</TargetFramework>
<Version>2.42.4</Version>
<Company>Tyrrrz</Company>
<Copyright>Copyright (c) Oleksii Holub</Copyright>
<LangVersion>preview</LangVersion>
@@ -11,19 +11,19 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="AngleSharp" Version="1.0.4" />
<PackageReference Include="AngleSharp" Version="1.0.6" />
<PackageReference Include="coverlet.collector" Version="6.0.0" PrivateAssets="all" />
<PackageReference Include="CSharpier.MsBuild" Version="0.25.0" PrivateAssets="all" />
<PackageReference Include="CSharpier.MsBuild" Version="0.26.1" PrivateAssets="all" />
<PackageReference Include="FluentAssertions" Version="6.12.0" />
<PackageReference Include="GitHubActionsTestLogger" Version="2.3.3" PrivateAssets="all" />
<PackageReference Include="JsonExtensions" Version="1.2.0" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="7.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageReference Include="ReflectionMagic" Version="5.0.0" />
<PackageReference Include="xunit" Version="2.5.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.1" PrivateAssets="all" />
<PackageReference Include="xunit" Version="2.6.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3" PrivateAssets="all" />
</ItemGroup>
<ItemGroup>
+25 -15
View File
@@ -1,18 +1,13 @@
# -- Build
# Specify the platform here so that we pull the SDK image matching the host platform,
# instead of the target platform specified during build by the `--platform` option.
# Use the .NET 8.0 preview because the `--arch` option is only available in that version.
# https://github.com/dotnet/dotnet-docker/issues/4388#issuecomment-1459038566
# TODO: switch images to Alpine once .NET 8.0 is released.
# Currently, the correct preview version is only available on Debian.
# https://github.com/dotnet/dotnet-docker/blob/main/samples/selecting-tags.md
FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:8.0-preview AS build
FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:8.0-alpine AS build
# Expose the target architecture set by the `docker build --platform` option, so that
# we can build the assembly for the correct platform.
ARG TARGETARCH
WORKDIR /build
WORKDIR /tmp/app
COPY favicon.ico .
COPY NuGet.config .
@@ -22,25 +17,40 @@ COPY DiscordChatExporter.Cli DiscordChatExporter.Cli
# Publish a self-contained assembly so we can use a slimmer runtime image
RUN dotnet publish DiscordChatExporter.Cli \
--configuration Release \
-p:CSharpier_Bypass=true \
--configuration Release \
--self-contained \
--use-current-runtime \
--arch $TARGETARCH \
--output publish/
--output DiscordChatExporter.Cli/bin/publish/
# -- Run
# Use `runtime-deps` instead of `runtime` because we have a self-contained assembly
FROM --platform=$TARGETPLATFORM mcr.microsoft.com/dotnet/runtime-deps:7.0 AS run
FROM --platform=$TARGETPLATFORM mcr.microsoft.com/dotnet/runtime-deps:8.0-alpine AS run
# Create a non-root user to run the app, so that the output files can be accessed by the host
LABEL org.opencontainers.image.title="DiscordChatExporter.Cli"
LABEL org.opencontainers.image.description="DiscordChatExporter is an application that can be used to export message history from any Discord channel to a file."
LABEL org.opencontainers.image.authors="tyrrrz.me"
LABEL org.opencontainers.image.url="https://github.com/Tyrrrz/DiscordChatExporter"
LABEL org.opencontainers.image.source="https://github.com/Tyrrrz/DiscordChatExporter/blob/master/DiscordChatExporter.Cli.dockerfile"
LABEL org.opencontainers.image.licenses="MIT"
# Alpine image doesn't come with the ICU libraries pre-installed, so we need to install them manually.
# We need the full ICU data because we allow the user to specify any locale for formatting purposes.
RUN apk add --no-cache icu-libs
RUN apk add --no-cache icu-data-full
ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=false
ENV LC_ALL=en_US.UTF-8
ENV LANG=en_US.UTF-8
# Use a non-root user to ensure that the files shared with the host are accessible by the host user.
# We can use the default user provided by the base image for this purpose.
# https://github.com/Tyrrrz/DiscordChatExporter/issues/851
RUN adduser --disabled-password --no-create-home dce
USER dce
USER $APP_UID
# This directory is exposed to the user for mounting purposes, so it's important that it always
# stays the same for backwards compatibility.
WORKDIR /out
COPY --from=build /build/publish /opt/dce
ENTRYPOINT ["/opt/dce/DiscordChatExporter.Cli"]
COPY --from=build /tmp/app/DiscordChatExporter.Cli/bin/publish /opt/app
ENTRYPOINT ["/opt/app/DiscordChatExporter.Cli"]
@@ -37,15 +37,28 @@ public abstract class DiscordCommandBase : ICommand
{
using (console.WithForegroundColor(ConsoleColor.DarkYellow))
{
console.Error.WriteLine(
"Warning: Option --bot is deprecated and should not be used. "
+ "The type of the provided token is now inferred automatically. "
+ "Please update your workflows as this option may be completely removed in a future version."
);
console
.Error
.WriteLine(
"Warning: Option --bot is deprecated and should not be used. "
+ "The type of the provided token is now inferred automatically. "
+ "Please update your workflows as this option may be completely removed in a future version."
);
}
}
#pragma warning restore CS0618
// Note about interactivity
if (console.IsOutputRedirected)
{
console
.Output
.WriteLine(
"Note: Output streams are redirected, rich console interactions are disabled. "
+ "If you are running this command in Docker, consider allocating a pseudo-terminal for better user experience (docker run -it ...)."
);
}
return default;
}
}
@@ -16,7 +16,6 @@ using DiscordChatExporter.Core.Exceptions;
using DiscordChatExporter.Core.Exporting;
using DiscordChatExporter.Core.Exporting.Filtering;
using DiscordChatExporter.Core.Exporting.Partitioning;
using DiscordChatExporter.Core.Utils;
using DiscordChatExporter.Core.Utils.Extensions;
using Gress;
using Spectre.Console;
@@ -163,7 +162,7 @@ public abstract class ExportCommandBase : DiscordCommandBase
|| OutputPath.Contains('%')
// Otherwise, require an existing directory or an unambiguous directory path
|| Directory.Exists(OutputPath)
|| PathEx.IsDirectoryPath(OutputPath);
|| Path.EndsInDirectorySeparator(OutputPath);
if (!isValidOutputPath)
{
@@ -200,7 +199,7 @@ public abstract class ExportCommandBase : DiscordCommandBase
try
{
await ctx.StartTaskAsync(
channel.GetHierarchicalName(),
Markup.Escape(channel.GetHierarchicalName()),
async progress =>
{
var guild = await Discord.GetGuildAsync(
@@ -244,9 +243,11 @@ public abstract class ExportCommandBase : DiscordCommandBase
// Print the result
using (console.WithForegroundColor(ConsoleColor.White))
{
await console.Output.WriteLineAsync(
$"Successfully exported {channels.Count - errorsByChannel.Count} channel(s)."
);
await console
.Output
.WriteLineAsync(
$"Successfully exported {channels.Count - errorsByChannel.Count} channel(s)."
);
}
// Print errors
@@ -256,9 +257,11 @@ public abstract class ExportCommandBase : DiscordCommandBase
using (console.WithForegroundColor(ConsoleColor.Red))
{
await console.Error.WriteLineAsync(
$"Failed to export {errorsByChannel.Count} the following channel(s):"
);
await console
.Error
.WriteLineAsync(
$"Failed to export {errorsByChannel.Count} the following channel(s):"
);
}
foreach (var (channel, error) in errorsByChannel)
@@ -320,33 +323,51 @@ public abstract class ExportCommandBase : DiscordCommandBase
// Support Ukraine callout
if (!IsUkraineSupportMessageDisabled)
{
console.Output.WriteLine(
"┌────────────────────────────────────────────────────────────────────┐"
);
console.Output.WriteLine(
"│ Thank you for supporting Ukraine <3 │"
);
console.Output.WriteLine(
"│ │"
);
console.Output.WriteLine(
"│ As Russia wages a genocidal war against my country, │"
);
console.Output.WriteLine(
"│ I'm grateful to everyone who continues to │"
);
console.Output.WriteLine(
"│ stand with Ukraine in our fight for freedom. │"
);
console.Output.WriteLine(
"│ │"
);
console.Output.WriteLine(
"│ Learn more: https://tyrrrz.me/ukraine │"
);
console.Output.WriteLine(
"└────────────────────────────────────────────────────────────────────┘"
);
console
.Output
.WriteLine(
"┌────────────────────────────────────────────────────────────────────┐"
);
console
.Output
.WriteLine(
"│ Thank you for supporting Ukraine <3 │"
);
console
.Output
.WriteLine(
"│ │"
);
console
.Output
.WriteLine(
"│ As Russia wages a genocidal war against my country, │"
);
console
.Output
.WriteLine(
"│ I'm grateful to everyone who continues to │"
);
console
.Output
.WriteLine(
"│ stand with Ukraine in our fight for freedom. │"
);
console
.Output
.WriteLine(
"│ │"
);
console
.Output
.WriteLine(
"│ Learn more: https://tyrrrz.me/ukraine │"
);
console
.Output
.WriteLine(
"└────────────────────────────────────────────────────────────────────┘"
);
console.Output.WriteLine("");
}
@@ -54,9 +54,9 @@ public class ExportAllCommand : ExportCommandBase
await foreach (var guild in Discord.GetUserGuildsAsync(cancellationToken))
{
// Regular channels
await console.Output.WriteLineAsync(
$"Fetching channels for guild '{guild.Name}'..."
);
await console
.Output
.WriteLineAsync($"Fetching channels for guild '{guild.Name}'...");
var fetchedChannelsCount = 0;
await console
@@ -80,7 +80,10 @@ public class ExportAllCommand : ExportCommandBase
channels.Add(channel);
ctx.Status($"Fetched '{channel.GetHierarchicalName()}'.");
ctx.Status(
Markup.Escape($"Fetched '{channel.GetHierarchicalName()}'.")
);
fetchedChannelsCount++;
}
}
@@ -91,9 +94,9 @@ public class ExportAllCommand : ExportCommandBase
// Threads
if (ThreadInclusionMode != ThreadInclusionMode.None)
{
await console.Output.WriteLineAsync(
$"Fetching threads for guild '{guild.Name}'..."
);
await console
.Output
.WriteLineAsync($"Fetching threads for guild '{guild.Name}'...");
var fetchedThreadsCount = 0;
await console
@@ -114,15 +117,18 @@ public class ExportAllCommand : ExportCommandBase
{
channels.Add(thread);
ctx.Status($"Fetched '{thread.GetHierarchicalName()}'.");
ctx.Status(
Markup.Escape($"Fetched '{thread.GetHierarchicalName()}'.")
);
fetchedThreadsCount++;
}
}
);
await console.Output.WriteLineAsync(
$"Fetched {fetchedThreadsCount} thread(s)."
);
await console
.Output
.WriteLineAsync($"Fetched {fetchedThreadsCount} thread(s).");
}
}
}
@@ -142,7 +148,11 @@ public class ExportAllCommand : ExportCommandBase
{
foreach (var dumpChannel in dump.Channels)
{
ctx.Status($"Fetching '{dumpChannel.Name}' ({dumpChannel.Id})...");
ctx.Status(
Markup.Escape(
$"Fetching '{dumpChannel.Name}' ({dumpChannel.Id})..."
)
);
try
{
@@ -170,9 +180,9 @@ public class ExportAllCommand : ExportCommandBase
using (console.WithForegroundColor(ConsoleColor.Red))
{
await console.Error.WriteLineAsync(
"Failed to access the following channel(s):"
);
await console
.Error
.WriteLineAsync("Failed to access the following channel(s):");
}
foreach (var dumpChannel in inaccessibleChannels)
@@ -58,7 +58,8 @@ public class ExportGuildCommand : ExportCommandBase
channels.Add(channel);
ctx.Status($"Fetched '{channel.GetHierarchicalName()}'.");
ctx.Status(Markup.Escape($"Fetched '{channel.GetHierarchicalName()}'."));
fetchedChannelsCount++;
}
}
@@ -90,7 +91,8 @@ public class ExportGuildCommand : ExportCommandBase
{
channels.Add(thread);
ctx.Status($"Fetched '{thread.GetHierarchicalName()}'.");
ctx.Status(Markup.Escape($"Fetched '{thread.GetHierarchicalName()}'."));
fetchedThreadsCount++;
}
}
@@ -64,9 +64,9 @@ public class GetChannelsCommand : DiscordCommandBase
foreach (var channel in channels)
{
// Channel ID
await console.Output.WriteAsync(
channel.Id.ToString().PadRight(channelIdMaxLength, ' ')
);
await console
.Output
.WriteAsync(channel.Id.ToString().PadRight(channelIdMaxLength, ' '));
// Separator
using (console.WithForegroundColor(ConsoleColor.DarkGray))
@@ -88,9 +88,11 @@ public class GetChannelsCommand : DiscordCommandBase
await console.Output.WriteAsync(" * ");
// Thread ID
await console.Output.WriteAsync(
channelThread.Id.ToString().PadRight(channelThreadIdMaxLength, ' ')
);
await console
.Output
.WriteAsync(
channelThread.Id.ToString().PadRight(channelThreadIdMaxLength, ' ')
);
// Separator
using (console.WithForegroundColor(ConsoleColor.DarkGray))
@@ -106,9 +108,9 @@ public class GetChannelsCommand : DiscordCommandBase
// Thread status
using (console.WithForegroundColor(ConsoleColor.White))
await console.Output.WriteLineAsync(
channelThread.IsArchived ? "Archived" : "Active"
);
await console
.Output
.WriteLineAsync(channelThread.IsArchived ? "Archived" : "Active");
}
}
}
@@ -33,9 +33,9 @@ public class GetDirectChannelsCommand : DiscordCommandBase
foreach (var channel in channels)
{
// Channel ID
await console.Output.WriteAsync(
channel.Id.ToString().PadRight(channelIdMaxLength, ' ')
);
await console
.Output
.WriteAsync(channel.Id.ToString().PadRight(channelIdMaxLength, ' '));
// Separator
using (console.WithForegroundColor(ConsoleColor.DarkGray))
@@ -15,18 +15,20 @@ public class GuideCommand : ICommand
using (console.WithForegroundColor(ConsoleColor.White))
console.Output.WriteLine("To get user token:");
console.Output.WriteLine(
" * Automating user accounts is technically against TOS — USE AT YOUR OWN RISK!"
);
console
.Output
.WriteLine(
" * Automating user accounts is technically against TOS — USE AT YOUR OWN RISK!"
);
console.Output.WriteLine(" 1. Open Discord in your web browser and login");
console.Output.WriteLine(" 2. Open any server or direct message channel");
console.Output.WriteLine(" 3. Press Ctrl+Shift+I to show developer tools");
console.Output.WriteLine(" 4. Navigate to the Network tab");
console.Output.WriteLine(" 5. Press Ctrl+R to reload");
console.Output.WriteLine(" 6. Switch between random channels to trigger network requests");
console.Output.WriteLine(
" 7. Search for a request containing \"messages?limit=50\" or similar"
);
console
.Output
.WriteLine(" 7. Search for a request containing \"messages?limit=50\" or similar");
console.Output.WriteLine(" 8. Select the Headers tab on the right");
console.Output.WriteLine(" 9. Scroll down to the Request Headers section");
console.Output.WriteLine(" 10. Copy the value of the \"authorization\" header");
@@ -40,9 +42,11 @@ public class GuideCommand : ICommand
console.Output.WriteLine(" 2. Open your application's settings");
console.Output.WriteLine(" 3. Navigate to the Bot section on the left");
console.Output.WriteLine(" 4. Under Token click Copy");
console.Output.WriteLine(
" * Your bot needs to have Message Content Intent enabled to read messages"
);
console
.Output
.WriteLine(
" * Your bot needs to have Message Content Intent enabled to read messages"
);
console.Output.WriteLine();
// Guild or channel ID
@@ -53,20 +57,22 @@ public class GuideCommand : ICommand
console.Output.WriteLine(" 2. Open Settings");
console.Output.WriteLine(" 3. Go to Advanced section");
console.Output.WriteLine(" 4. Enable Developer Mode");
console.Output.WriteLine(
" 5. Right-click on the desired guild or channel and click Copy Server ID or Copy Channel ID"
);
console
.Output
.WriteLine(
" 5. Right-click on the desired guild or channel and click Copy Server ID or Copy Channel ID"
);
console.Output.WriteLine();
// Docs link
using (console.WithForegroundColor(ConsoleColor.White))
console.Output.WriteLine(
"If you have questions or issues, please refer to the documentation:"
);
console
.Output
.WriteLine("If you have questions or issues, please refer to the documentation:");
using (console.WithForegroundColor(ConsoleColor.DarkCyan))
console.Output.WriteLine(
"https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs"
);
console
.Output
.WriteLine("https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs");
return default;
}
@@ -7,7 +7,7 @@
<ItemGroup>
<PackageReference Include="CliFx" Version="2.3.4" />
<PackageReference Include="CSharpier.MsBuild" Version="0.25.0" PrivateAssets="all" />
<PackageReference Include="CSharpier.MsBuild" Version="0.26.1" PrivateAssets="all" />
<PackageReference Include="Deorcify" Version="1.0.2" PrivateAssets="all" />
<PackageReference Include="DotnetRuntimeBootstrapper" Version="2.5.1" PrivateAssets="all" />
<PackageReference Include="Gress" Version="2.1.1" />
@@ -44,8 +44,7 @@ internal static class ConsoleExtensions
var actualDescription = !string.IsNullOrWhiteSpace(description) ? description : "...";
var progressTask = context.AddTask(
// Don't recognize random square brackets as style tags
Markup.Escape(actualDescription),
actualDescription,
new ProgressTaskSettings { MaxValue = 1 }
);
@@ -1,5 +1,5 @@
using JsonExtensions.Reading;
using System.Text.Json;
using JsonExtensions.Reading;
namespace DiscordChatExporter.Core.Discord.Data.Embeds;
@@ -12,9 +12,11 @@ public partial record SpotifyTrackEmbedProjection
private static string? TryParseTrackId(string embedUrl)
{
// https://open.spotify.com/track/1LHZMWefF9502NPfArRfvP?si=3efac6ce9be04f0a
var trackId = Regex.Match(embedUrl, @"spotify\.com/track/(.*?)(?:\?|&|/|$)").Groups[
1
].Value;
var trackId = Regex
.Match(embedUrl, @"spotify\.com/track/(.*?)(?:\?|&|/|$)")
.Groups[1]
.Value;
if (!string.IsNullOrWhiteSpace(trackId))
return trackId;
@@ -10,7 +10,7 @@ public record Interaction(Snowflake Id, string Name, User User)
public static Interaction Parse(JsonElement json)
{
var id = json.GetProperty("id").GetNonWhiteSpaceString().Pipe(Snowflake.Parse);
var name = json.GetProperty("name").GetNonWhiteSpaceString();
var name = json.GetProperty("name").GetNonNullString(); // may be empty, but not null
var user = json.GetProperty("user").Pipe(User.Parse);
return new Interaction(id, name, user);
@@ -99,7 +99,8 @@ public partial record Message
if (trailingEmbeds.Any())
{
// Concatenate all images into one embed
var images = embed.Images
var images = embed
.Images
.Concat(trailingEmbeds.SelectMany(e => e.Images))
.ToArray();
@@ -40,10 +40,12 @@ public class DiscordClient
// Don't validate because the token can have special characters
// https://github.com/Tyrrrz/DiscordChatExporter/issues/828
request.Headers.TryAddWithoutValidation(
"Authorization",
tokenKind == TokenKind.Bot ? $"Bot {_token}" : _token
);
request
.Headers
.TryAddWithoutValidation(
"Authorization",
tokenKind == TokenKind.Bot ? $"Bot {_token}" : _token
);
var response = await Http.Client.SendAsync(
request,
@@ -58,11 +60,13 @@ public class DiscordClient
// require properly keeping track of Discord's global/per-route/per-resource
// rate limits and that's just way too much effort.
// https://discord.com/developers/docs/topics/rate-limits
var remainingRequestCount = response.Headers
var remainingRequestCount = response
.Headers
.TryGetValue("X-RateLimit-Remaining")
?.Pipe(s => int.Parse(s, CultureInfo.InvariantCulture));
var resetAfterDelay = response.Headers
var resetAfterDelay = response
.Headers
.TryGetValue("X-RateLimit-Reset-After")
?.Pipe(s => double.Parse(s, CultureInfo.InvariantCulture))
.Pipe(TimeSpan.FromSeconds);
@@ -1,15 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<PackageReference Include="AsyncKeyedLock" Version="6.2.1" />
<PackageReference Include="CSharpier.MsBuild" Version="0.25.0" PrivateAssets="all" />
<PackageReference Include="AsyncKeyedLock" Version="6.2.2" />
<PackageReference Include="CSharpier.MsBuild" Version="0.26.1" PrivateAssets="all" />
<PackageReference Include="Gress" Version="2.1.1" />
<PackageReference Include="JsonExtensions" Version="1.2.0" />
<PackageReference Include="Polly" Version="8.0.0" />
<PackageReference Include="Polly" Version="8.1.0" />
<PackageReference Include="RazorBlade" Version="0.4.4" />
<PackageReference Include="Superpower" Version="3.0.0" />
<PackageReference Include="WebMarkupMin.Core" Version="2.14.0" />
<PackageReference Include="YoutubeExplode" Version="6.3.4" />
<PackageReference Include="YoutubeExplode" Version="6.3.7" />
</ItemGroup>
</Project>
@@ -8,11 +8,9 @@ using Gress;
namespace DiscordChatExporter.Core.Exporting;
public class ChannelExporter
public class ChannelExporter(DiscordClient discord)
{
private readonly DiscordClient _discord;
public ChannelExporter(DiscordClient discord) => _discord = discord;
private readonly DiscordClient _discord = discord;
public async ValueTask ExportChannelAsync(
ExportRequest request,
@@ -22,11 +20,18 @@ public class ChannelExporter
{
// Forum channels don't have messages, they are just a list of threads
if (request.Channel.Kind == ChannelKind.GuildForum)
throw new DiscordChatExporterException("Channel is a forum.");
{
throw new DiscordChatExporterException(
"Channel is a forum and cannot be exported directly. "
+ "You need to pull its threads and export them individually."
);
}
// Check if the channel is empty
if (request.Channel.IsEmpty)
{
throw new DiscordChatExporterException("Channel does not contain any messages.");
}
// Check if the 'after' boundary is valid
if (request.After is not null && !request.Channel.MayHaveMessagesAfter(request.After.Value))
@@ -8,15 +8,10 @@ using DiscordChatExporter.Core.Utils.Extensions;
namespace DiscordChatExporter.Core.Exporting;
internal partial class CsvMessageWriter : MessageWriter
internal partial class CsvMessageWriter(Stream stream, ExportContext context)
: MessageWriter(stream, context)
{
private readonly TextWriter _writer;
public CsvMessageWriter(Stream stream, ExportContext context)
: base(stream, context)
{
_writer = new StreamWriter(stream);
}
private readonly TextWriter _writer = new StreamWriter(stream);
private async ValueTask<string> FormatMarkdownAsync(
string markdown,
@@ -88,7 +83,7 @@ internal partial class CsvMessageWriter : MessageWriter
await _writer.WriteAsync(',');
// Message timestamp
await _writer.WriteAsync(CsvEncode(Context.FormatDate(message.Timestamp)));
await _writer.WriteAsync(CsvEncode(Context.FormatDate(message.Timestamp, "o")));
await _writer.WriteAsync(',');
// Message content
@@ -14,7 +14,7 @@ using DiscordChatExporter.Core.Utils.Extensions;
namespace DiscordChatExporter.Core.Exporting;
internal partial class ExportAssetDownloader
internal partial class ExportAssetDownloader(string workingDirPath, bool reuse)
{
private static readonly AsyncKeyedLocker<string> Locker =
new(o =>
@@ -23,18 +23,12 @@ internal partial class ExportAssetDownloader
o.PoolInitialFill = 1;
});
private readonly string _workingDirPath;
private readonly bool _reuse;
private readonly string _workingDirPath = workingDirPath;
private readonly bool _reuse = reuse;
// File paths of the previously downloaded assets
private readonly Dictionary<string, string> _previousPathsByUrl = new(StringComparer.Ordinal);
public ExportAssetDownloader(string workingDirPath, bool reuse)
{
_workingDirPath = workingDirPath;
_reuse = reuse;
}
public async ValueTask<string> DownloadAsync(
string url,
CancellationToken cancellationToken = default
@@ -65,7 +59,9 @@ internal partial class ExportAssetDownloader
// Try to set the file date according to the last-modified header
try
{
var lastModified = response.Content.Headers
var lastModified = response
.Content
.Headers
.TryGetValue("Last-Modified")
?.Pipe(
s =>
@@ -12,27 +12,18 @@ using DiscordChatExporter.Core.Utils.Extensions;
namespace DiscordChatExporter.Core.Exporting;
internal class ExportContext
internal class ExportContext(DiscordClient discord, ExportRequest request)
{
private readonly Dictionary<Snowflake, Member?> _membersById = new();
private readonly Dictionary<Snowflake, Channel> _channelsById = new();
private readonly Dictionary<Snowflake, Role> _rolesById = new();
private readonly ExportAssetDownloader _assetDownloader;
public DiscordClient Discord { get; }
private readonly ExportAssetDownloader _assetDownloader =
new(request.AssetsDirPath, request.ShouldReuseAssets);
public ExportRequest Request { get; }
public DiscordClient Discord { get; } = discord;
public ExportContext(DiscordClient discord, ExportRequest request)
{
Discord = discord;
Request = request;
_assetDownloader = new ExportAssetDownloader(
request.AssetsDirPath,
request.ShouldReuseAssets
);
}
public ExportRequest Request { get; } = request;
public DateTimeOffset NormalizeDate(DateTimeOffset instant) =>
Request.IsUtcNormalizationEnabled ? instant.ToUniversalTime() : instant.ToLocalTime();
@@ -101,7 +92,8 @@ internal class ExportContext
public Role? TryGetRole(Snowflake id) => _rolesById.GetValueOrDefault(id);
public IReadOnlyList<Role> GetUserRoles(Snowflake id) =>
TryGetMember(id)?.RoleIds
TryGetMember(id)
?.RoleIds
.Select(TryGetRole)
.WhereNotNull()
.OrderByDescending(r => r.Position)
@@ -123,9 +115,9 @@ internal class ExportContext
var filePath = await _assetDownloader.DownloadAsync(url, cancellationToken);
var relativeFilePath = Path.GetRelativePath(Request.OutputDirPath, filePath);
// Prefer relative paths so that the output files can be copied around without breaking references.
// If the asset directory is outside of the export directory, use an absolute path instead.
var optimalFilePath =
// Prefer the relative path so that the export package can be copied around without breaking references.
// However, if the assets directory lies outside of the export directory, use the absolute path instead.
var shouldUseAbsoluteFilePath =
relativeFilePath.StartsWith(
".." + Path.DirectorySeparatorChar,
StringComparison.Ordinal
@@ -133,15 +125,19 @@ internal class ExportContext
|| relativeFilePath.StartsWith(
".." + Path.AltDirectorySeparatorChar,
StringComparison.Ordinal
)
? filePath
: relativeFilePath;
);
var optimalFilePath = shouldUseAbsoluteFilePath ? filePath : relativeFilePath;
// For HTML, the path needs to be properly formatted
if (Request.Format is ExportFormat.HtmlDark or ExportFormat.HtmlLight)
{
// Create a 'file:///' URI and then strip the 'file:///' prefix to allow for relative paths
return new Uri(new Uri("file:///"), optimalFilePath).ToString()[8..];
// Format the path into a valid file URI
var href = new Uri(new Uri("file:///"), optimalFilePath).ToString();
// File schema does not support relative paths, so strip it if that's the case
// https://github.com/Tyrrrz/DiscordChatExporter/issues/1155
return shouldUseAbsoluteFilePath ? href : href[8..];
}
return optimalFilePath;
@@ -182,10 +182,9 @@ public partial class ExportRequest
=> before?.ToDate().ToString("yyyy-MM-dd", CultureInfo.InvariantCulture)
?? "",
"%d"
=> DateTimeOffset.Now.ToString(
"yyyy-MM-dd",
CultureInfo.InvariantCulture
),
=> DateTimeOffset
.Now
.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture),
"%%" => "%",
_ => m.Value
@@ -26,12 +26,14 @@ internal class ContainsMessageFilter : MessageFilter
public override bool IsMatch(Message message) =>
IsMatch(message.Content)
|| message.Embeds.Any(
e =>
IsMatch(e.Title)
|| IsMatch(e.Author?.Name)
|| IsMatch(e.Description)
|| IsMatch(e.Footer?.Text)
|| e.Fields.Any(f => IsMatch(f.Name) || IsMatch(f.Value))
);
|| message
.Embeds
.Any(
e =>
IsMatch(e.Title)
|| IsMatch(e.Author?.Name)
|| IsMatch(e.Description)
|| IsMatch(e.Footer?.Text)
|| e.Fields.Any(f => IsMatch(f.Name) || IsMatch(f.Value))
);
}
@@ -11,11 +11,13 @@ internal class MentionsMessageFilter : MessageFilter
public MentionsMessageFilter(string value) => _value = value;
public override bool IsMatch(Message message) =>
message.MentionedUsers.Any(
user =>
string.Equals(_value, user.Name, StringComparison.OrdinalIgnoreCase)
|| string.Equals(_value, user.DisplayName, StringComparison.OrdinalIgnoreCase)
|| string.Equals(_value, user.FullName, StringComparison.OrdinalIgnoreCase)
|| string.Equals(_value, user.Id.ToString(), StringComparison.OrdinalIgnoreCase)
);
message
.MentionedUsers
.Any(
user =>
string.Equals(_value, user.Name, StringComparison.OrdinalIgnoreCase)
|| string.Equals(_value, user.DisplayName, StringComparison.OrdinalIgnoreCase)
|| string.Equals(_value, user.FullName, StringComparison.OrdinalIgnoreCase)
|| string.Equals(_value, user.Id.ToString(), StringComparison.OrdinalIgnoreCase)
);
}
@@ -11,10 +11,16 @@ internal class ReactionMessageFilter : MessageFilter
public ReactionMessageFilter(string value) => _value = value;
public override bool IsMatch(Message message) =>
message.Reactions.Any(
r =>
string.Equals(_value, r.Emoji.Id?.ToString(), StringComparison.OrdinalIgnoreCase)
|| string.Equals(_value, r.Emoji.Name, StringComparison.OrdinalIgnoreCase)
|| string.Equals(_value, r.Emoji.Code, StringComparison.OrdinalIgnoreCase)
);
message
.Reactions
.Any(
r =>
string.Equals(
_value,
r.Emoji.Id?.ToString(),
StringComparison.OrdinalIgnoreCase
)
|| string.Equals(_value, r.Emoji.Name, StringComparison.OrdinalIgnoreCase)
|| string.Equals(_value, r.Emoji.Code, StringComparison.OrdinalIgnoreCase)
);
}
@@ -12,18 +12,15 @@ using DiscordChatExporter.Core.Utils.Extensions;
namespace DiscordChatExporter.Core.Exporting;
internal partial class HtmlMarkdownVisitor : MarkdownVisitor
internal partial class HtmlMarkdownVisitor(
ExportContext context,
StringBuilder buffer,
bool isJumbo
) : MarkdownVisitor
{
private readonly ExportContext _context;
private readonly StringBuilder _buffer;
private readonly bool _isJumbo;
public HtmlMarkdownVisitor(ExportContext context, StringBuilder buffer, bool isJumbo)
{
_context = context;
_buffer = buffer;
_isJumbo = isJumbo;
}
private readonly ExportContext _context = context;
private readonly StringBuilder _buffer = buffer;
private readonly bool _isJumbo = isJumbo;
protected override ValueTask VisitTextAsync(
TextNode text,
@@ -196,7 +193,8 @@ internal partial class HtmlMarkdownVisitor : MarkdownVisitor
// Try to extract the message ID if the link points to a Discord message
var linkedMessageId = Regex
.Match(link.Url, @"^https?://(?:discord|discordapp)\.com/channels/.*?/(\d+)/?$")
.Groups[1].Value;
.Groups[1]
.Value;
_buffer.Append(
!string.IsNullOrWhiteSpace(linkedMessageId)
@@ -9,21 +9,15 @@ using WebMarkupMin.Core;
namespace DiscordChatExporter.Core.Exporting;
internal class HtmlMessageWriter : MessageWriter
internal class HtmlMessageWriter(Stream stream, ExportContext context, string themeName)
: MessageWriter(stream, context)
{
private readonly TextWriter _writer;
private readonly string _themeName;
private readonly TextWriter _writer = new StreamWriter(stream);
private readonly string _themeName = themeName;
private readonly HtmlMinifier _minifier = new();
private readonly List<Message> _messageGroup = new();
public HtmlMessageWriter(Stream stream, ExportContext context, string themeName)
: base(stream, context)
{
_writer = new StreamWriter(stream);
_themeName = themeName;
}
private bool CanJoinGroup(Message message)
{
// If the group is empty, any message can join it
@@ -12,26 +12,21 @@ using JsonExtensions.Writing;
namespace DiscordChatExporter.Core.Exporting;
internal class JsonMessageWriter : MessageWriter
internal class JsonMessageWriter(Stream stream, ExportContext context)
: MessageWriter(stream, context)
{
private readonly Utf8JsonWriter _writer;
public JsonMessageWriter(Stream stream, ExportContext context)
: base(stream, context)
{
_writer = new Utf8JsonWriter(
stream,
new JsonWriterOptions
{
// https://github.com/Tyrrrz/DiscordChatExporter/issues/450
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
Indented = true,
// Validation errors may mask actual failures
// https://github.com/Tyrrrz/DiscordChatExporter/issues/413
SkipValidation = true
}
);
}
private readonly Utf8JsonWriter _writer = new Utf8JsonWriter(
stream,
new JsonWriterOptions
{
// https://github.com/Tyrrrz/DiscordChatExporter/issues/450
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
Indented = true,
// Validation errors may mask actual failures
// https://github.com/Tyrrrz/DiscordChatExporter/issues/413
SkipValidation = true
}
);
private async ValueTask<string> FormatMarkdownAsync(
string markdown,
@@ -143,6 +138,31 @@ internal class JsonMessageWriter : MessageWriter
await _writer.FlushAsync(cancellationToken);
}
private async ValueTask WriteEmbedVideoAsync(
EmbedVideo embedVideo,
CancellationToken cancellationToken = default
)
{
_writer.WriteStartObject();
if (!string.IsNullOrWhiteSpace(embedVideo.Url))
{
_writer.WriteString(
"url",
await Context.ResolveAssetUrlAsync(
embedVideo.ProxyUrl ?? embedVideo.Url,
cancellationToken
)
);
}
_writer.WriteNumber("width", embedVideo.Width);
_writer.WriteNumber("height", embedVideo.Height);
_writer.WriteEndObject();
await _writer.FlushAsync(cancellationToken);
}
private async ValueTask WriteEmbedFooterAsync(
EmbedFooter embedFooter,
CancellationToken cancellationToken = default
@@ -224,6 +244,12 @@ internal class JsonMessageWriter : MessageWriter
await WriteEmbedImageAsync(embed.Image, cancellationToken);
}
if (embed.Video is not null)
{
_writer.WritePropertyName("video");
await WriteEmbedVideoAsync(embed.Video, cancellationToken);
}
if (embed.Footer is not null)
{
_writer.WritePropertyName("footer");
@@ -419,12 +445,14 @@ internal class JsonMessageWriter : MessageWriter
_writer.WriteStartArray("users");
await foreach (
var user in Context.Discord.GetMessageReactionsAsync(
Context.Request.Channel.Id,
message.Id,
reaction.Emoji,
cancellationToken
)
var user in Context
.Discord
.GetMessageReactionsAsync(
Context.Request.Channel.Id,
message.Id,
reaction.Emoji,
cancellationToken
)
)
{
_writer.WriteStartObject();
@@ -6,20 +6,15 @@ using DiscordChatExporter.Core.Discord.Data;
namespace DiscordChatExporter.Core.Exporting;
internal partial class MessageExporter : IAsyncDisposable
internal partial class MessageExporter(ExportContext context) : IAsyncDisposable
{
private readonly ExportContext _context;
private readonly ExportContext _context = context;
private int _partitionIndex;
private MessageWriter? _writer;
public long MessagesExported { get; private set; }
public MessageExporter(ExportContext context)
{
_context = context;
}
private async ValueTask ResetWriterAsync(CancellationToken cancellationToken = default)
{
if (_writer is not null)
@@ -44,10 +39,10 @@ internal partial class MessageExporter : IAsyncDisposable
// Ensure that the partition limit has not been reached
if (
_writer is not null
&& _context.Request.PartitionLimit.IsReached(
_writer.MessagesWritten,
_writer.BytesWritten
)
&& _context
.Request
.PartitionLimit
.IsReached(_writer.MessagesWritten, _writer.BytesWritten)
)
{
await ResetWriterAsync(cancellationToken);
@@ -6,22 +6,16 @@ using DiscordChatExporter.Core.Discord.Data;
namespace DiscordChatExporter.Core.Exporting;
internal abstract class MessageWriter : IAsyncDisposable
internal abstract class MessageWriter(Stream stream, ExportContext context) : IAsyncDisposable
{
protected Stream Stream { get; }
protected Stream Stream { get; } = stream;
protected ExportContext Context { get; }
protected ExportContext Context { get; } = context;
public long MessagesWritten { get; private set; }
public long BytesWritten => Stream.Length;
protected MessageWriter(Stream stream, ExportContext context)
{
Stream = stream;
Context = context;
}
public virtual ValueTask WritePreambleAsync(CancellationToken cancellationToken = default) =>
default;
@@ -8,16 +8,11 @@ using DiscordChatExporter.Core.Utils.Extensions;
namespace DiscordChatExporter.Core.Exporting;
internal partial class PlainTextMarkdownVisitor : MarkdownVisitor
internal partial class PlainTextMarkdownVisitor(ExportContext context, StringBuilder buffer)
: MarkdownVisitor
{
private readonly ExportContext _context;
private readonly StringBuilder _buffer;
public PlainTextMarkdownVisitor(ExportContext context, StringBuilder buffer)
{
_context = context;
_buffer = buffer;
}
private readonly ExportContext _context = context;
private readonly StringBuilder _buffer = buffer;
protected override ValueTask VisitTextAsync(
TextNode text,
@@ -8,15 +8,10 @@ using DiscordChatExporter.Core.Discord.Data.Embeds;
namespace DiscordChatExporter.Core.Exporting;
internal class PlainTextMessageWriter : MessageWriter
internal class PlainTextMessageWriter(Stream stream, ExportContext context)
: MessageWriter(stream, context)
{
private readonly TextWriter _writer;
public PlainTextMessageWriter(Stream stream, ExportContext context)
: base(stream, context)
{
_writer = new StreamWriter(stream);
}
private readonly TextWriter _writer = new StreamWriter(stream);
private async ValueTask<string> FormatMarkdownAsync(
string markdown,
@@ -30,7 +30,8 @@ internal class AggregateMatcher<T> : IMatcher<T>
// If this match is earlier than previous earliest - replace
if (
earliestMatch is null || match.Segment.StartIndex < earliestMatch.Segment.StartIndex
earliestMatch is null
|| match.Segment.StartIndex < earliestMatch.Segment.StartIndex
)
earliestMatch = match;
@@ -24,12 +24,10 @@ internal class StringMatcher<T> : IMatcher<T>
public ParsedMatch<T>? TryMatch(StringSegment segment)
{
var index = segment.Source.IndexOf(
_needle,
segment.StartIndex,
segment.Length,
_comparison
);
var index = segment
.Source
.IndexOf(_needle, segment.StartIndex, segment.Length, _comparison);
if (index < 0)
return null;
-3
View File
@@ -27,7 +27,4 @@ public static class PathEx
return buffer.ToString();
}
public static bool IsDirectoryPath(string path) =>
path.EndsWith(Path.DirectorySeparatorChar) || path.EndsWith(Path.AltDirectorySeparatorChar);
}
-1
View File
@@ -3,7 +3,6 @@ using DiscordChatExporter.Gui.ViewModels;
using DiscordChatExporter.Gui.ViewModels.Framework;
using Stylet;
using StyletIoC;
#if !DEBUG
using System.Windows;
using System.Windows.Threading;
@@ -13,15 +13,14 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Cogwheel" Version="2.0.3" />
<PackageReference Include="CSharpier.MsBuild" Version="0.25.0" PrivateAssets="all" />
<PackageReference Include="Cogwheel" Version="2.0.4" />
<PackageReference Include="CSharpier.MsBuild" Version="0.26.1" PrivateAssets="all" />
<PackageReference Include="Deorcify" Version="1.0.2" PrivateAssets="all" />
<PackageReference Include="DotnetRuntimeBootstrapper" Version="2.5.1" PrivateAssets="all" />
<PackageReference Include="Gress" Version="2.1.1" />
<PackageReference Include="MaterialDesignColors" Version="2.1.4" />
<PackageReference Include="MaterialDesignThemes" Version="4.9.0" />
<PackageReference Include="Microsoft.Xaml.Behaviors.Wpf" Version="1.1.39" />
<PackageReference Include="Ookii.Dialogs.Wpf" Version="5.0.1" />
<PackageReference Include="Microsoft.Xaml.Behaviors.Wpf" Version="1.1.77" />
<PackageReference Include="Onova" Version="2.6.10" />
<PackageReference Include="PropertyChanged.Fody" Version="4.1.0" PrivateAssets="all" />
<PackageReference Include="Stylet" Version="1.3.6" />
@@ -66,7 +66,8 @@ public partial class SettingsService
{
try
{
return Registry.CurrentUser
return Registry
.CurrentUser
.OpenSubKey(
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize",
false
@@ -12,8 +12,8 @@ using DiscordChatExporter.Gui.Models;
using DiscordChatExporter.Gui.Services;
using DiscordChatExporter.Gui.Utils;
using DiscordChatExporter.Gui.ViewModels.Dialogs;
using DiscordChatExporter.Gui.ViewModels.Messages;
using DiscordChatExporter.Gui.ViewModels.Framework;
using DiscordChatExporter.Gui.ViewModels.Messages;
using Gress;
using Gress.Completable;
using Stylet;
@@ -234,7 +234,8 @@ public class DashboardViewModel : PropertyChangedBase
var exporter = new ChannelExporter(_discord);
var channelProgressPairs = dialog.Channels!
var channelProgressPairs = dialog
.Channels!
.Select(c => new { Channel = c, Progress = _progressMuxer.CreateInput() })
.ToArray();
@@ -4,7 +4,6 @@ using System.Threading;
using System.Threading.Tasks;
using MaterialDesignThemes.Wpf;
using Microsoft.Win32;
using Ookii.Dialogs.Wpf;
using Stylet;
namespace DiscordChatExporter.Gui.ViewModels.Framework;
@@ -68,9 +67,8 @@ public class DialogManager : IDisposable
public string? PromptDirectoryPath(string defaultDirPath = "")
{
var dialog = new VistaFolderBrowserDialog { SelectedPath = defaultDirPath };
return dialog.ShowDialog() == true ? dialog.SelectedPath : null;
var dialog = new OpenFolderDialog { InitialDirectory = defaultDirPath };
return dialog.ShowDialog() == true ? dialog.FolderName : null;
}
public void Dispose()
@@ -4,8 +4,8 @@ using DiscordChatExporter.Gui.Services;
using DiscordChatExporter.Gui.Utils;
using DiscordChatExporter.Gui.ViewModels.Components;
using DiscordChatExporter.Gui.ViewModels.Dialogs;
using DiscordChatExporter.Gui.ViewModels.Messages;
using DiscordChatExporter.Gui.ViewModels.Framework;
using DiscordChatExporter.Gui.ViewModels.Messages;
using MaterialDesignThemes.Wpf;
using Stylet;
@@ -271,7 +271,10 @@
</Grid>
<!-- Reuse assets -->
<Grid Margin="16,8" ToolTip="Reuse previously downloaded assets to avoid redundant requests">
<Grid
Margin="16,8"
IsEnabled="{Binding ShouldDownloadAssets}"
ToolTip="Reuse previously downloaded assets to avoid redundant requests">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
@@ -289,14 +292,16 @@
</Grid>
<!-- Assets path -->
<Grid Margin="16,8">
<Grid
Margin="16,8"
IsEnabled="{Binding ShouldDownloadAssets}"
ToolTip="Download assets to this directory. If not specified, the asset directory path will be derived from the output path.">
<TextBox
Padding="16,16,42,16"
materialDesign:HintAssist.Hint="Assets directory path"
materialDesign:HintAssist.IsFloating="True"
Style="{DynamicResource MaterialDesignOutlinedTextBox}"
Text="{Binding AssetsDirPath}"
ToolTip="Download assets to this directory. If not specified, the asset directory path will be derived from the output path." />
Text="{Binding AssetsDirPath}" />
<Button
Width="24"
Height="24"
+9 -8
View File
@@ -23,7 +23,8 @@
**DiscordChatExporter** is an application that can be used to export message history from any [Discord](https://discord.com) channel to a file.
It works with direct messages, group messages, and server channels, and supports Discord's dialect of markdown as well as most other rich media features.
> ❔ If you have questions or issues, **please refer to the [docs](.docs)**.
<!-- Can't use a relative link here due to a bug in markdown parsing -->
> ❔ If you have questions or issues, **please refer to the [docs](https://github.com/Tyrrrz/DiscordChatExporter/tree/master/.docs)**.
> 💬 If you want to chat, **join my [Discord server](https://discord.gg/2SUWKFnHSm)**.
@@ -57,7 +58,7 @@ The following table lists all available download options:
<td>
<ul>
<li>🟢 <b><a href="https://github.com/Tyrrrz/DiscordChatExporter/releases/latest">Stable release</a></b> (<code>DiscordChatExporter.zip</code>)</li>
<li>🟠 <a href="https://github.com/Tyrrrz/DiscordChatExporter/actions/workflows/main.yml">CI build</a> (<code>DiscordChatExporter.zip</code>)</li>
<li>🟠 <a href="https://github.com/Tyrrrz/DiscordChatExporter/actions/workflows/main.yml">CI build</a> (<code>DiscordChatExporter.Gui.zip</code>)</li>
</ul>
</td>
<td>
@@ -70,8 +71,8 @@ The following table lists all available download options:
<td><b>CLI</b></td>
<td>
<ul>
<li>🟢 <b><a href="https://github.com/Tyrrrz/DiscordChatExporter/releases/latest">Stable release</a></b> (<code>DiscordChatExporter.CLI.zip</code></li>
<li>🟠 <a href="https://github.com/Tyrrrz/DiscordChatExporter/actions/workflows/main.yml">CI build</a> (<code>DiscordChatExporter.CLI.zip</code>)</li>
<li>🟢 <b><a href="https://github.com/Tyrrrz/DiscordChatExporter/releases/latest">Stable release</a></b> (<code>DiscordChatExporter.Cli.zip</code></li>
<li>🟠 <a href="https://github.com/Tyrrrz/DiscordChatExporter/actions/workflows/main.yml">CI build</a> (<code>DiscordChatExporter.Cli.zip</code>)</li>
<li>🐋 <a href="https://hub.docker.com/r/tyrrrz/discordchatexporter">Docker</a> (<code>tyrrrz/discordchatexporter</code>)</li>
<li>📦 <a href="https://aur.archlinux.org/packages/discord-chat-exporter-cli">AUR</a> (<code>discord-chat-exporter-cli</code>)</li>
<li>📦 <a href="https://search.nixos.org/packages?query=discordchatexporter-cli">Nix</a> (<code>discordchatexporter-cli</code>)</li>
@@ -93,12 +94,12 @@ The following table lists all available download options:
> If you have any issues with them, please contact the corresponding maintainers.
> **Warning**:
> To run **DiscordChatExporter** on macOS and Linux, you need to make sure that **.NET 7.0 Runtime** is installed.
> To run **DiscordChatExporter** on macOS and Linux, you need to make sure that **.NET 8.0 Runtime** is installed.
> You can download it here:
>
> - [.NET 7.0 Runtime for **macOS x64**](https://dotnet.microsoft.com/en-us/download/dotnet/thank-you/sdk-7.0.101-macos-x64-installer)
> - [.NET 7.0 Runtime for **macOS Arm64**](https://dotnet.microsoft.com/en-us/download/dotnet/thank-you/sdk-7.0.101-macos-arm64-installer)
> - [.NET 7.0 Runtime for **Linux**](https://docs.microsoft.com/en-us/dotnet/core/install/linux) (find the correct download for your distro)
> - [.NET 8.0 Runtime for **macOS x64**](https://dotnet.microsoft.com/en-us/download/dotnet/thank-you/runtime-8.0.0-macos-x64-installer)
> - [.NET 8.0 Runtime for **macOS arm64**](https://dotnet.microsoft.com/en-us/download/dotnet/thank-you/runtime-8.0.0-macos-arm64-installer)
> - [.NET 8.0 Runtime for **Linux**](https://learn.microsoft.com/dotnet/core/install/linux) (find the correct download for your distro)
>
> This should not be necessary if you install **DiscordChatExporter** using a package manager, as it should take care of the dependencies for you.
> This is also not necessary if you are running **DiscordChatExporter** via Docker, because the image already contains the runtime.