mirror of
https://github.com/Tyrrrz/DiscordChatExporter.git
synced 2026-07-08 07:04:40 +02:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 921f348769 | |||
| a25e809671 | |||
| fa80c82468 | |||
| 91f4f02a35 | |||
| 6d2880ce26 | |||
| 355b8cb8cf | |||
| 82945ac3cf | |||
| 5009b90a3e |
Binary file not shown.
|
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 21 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 105 KiB After Width: | Height: | Size: 27 KiB |
@@ -1,3 +1,9 @@
|
|||||||
|
### v2.23 (14-Sep-2020)
|
||||||
|
|
||||||
|
- [CLI] Added a command to export all channels across all servers. Use `exportall` to do it. (Thanks [@wyattscarpenter](https://github.com/wyattscarpenter))
|
||||||
|
- [HTML] Fixed an issue where Whitney fonts were not being loaded properly, causing the browser to fall back to Helvetica.
|
||||||
|
- Fixed an issue where self-contained export crashed occasionally. This usually happened when the server hosting the file did not serve the stream properly. Such files are now ignored after the first failed attempt.
|
||||||
|
|
||||||
### v2.22 (12-Aug-2020)
|
### v2.22 (12-Aug-2020)
|
||||||
|
|
||||||
- [GUI] Improved the channel list by adding collapsible category groups. (Thanks [@CarJem Generations](https://github.com/CarJem))
|
- [GUI] Improved the channel list by adding collapsible category groups. (Thanks [@CarJem Generations](https://github.com/CarJem))
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using CliFx;
|
||||||
|
using CliFx.Attributes;
|
||||||
|
using DiscordChatExporter.Cli.Commands.Base;
|
||||||
|
using DiscordChatExporter.Domain.Discord.Models;
|
||||||
|
|
||||||
|
namespace DiscordChatExporter.Cli.Commands
|
||||||
|
{
|
||||||
|
[Command("exportall", Description = "Export all accessible channels.")]
|
||||||
|
public class ExportAllCommand : ExportMultipleCommandBase
|
||||||
|
{
|
||||||
|
[CommandOption("include-dm", Description = "Whether to also export direct message channels.")]
|
||||||
|
public bool IncludeDirectMessages { get; set; } = true;
|
||||||
|
|
||||||
|
public override async ValueTask ExecuteAsync(IConsole console)
|
||||||
|
{
|
||||||
|
var channels = new List<Channel>();
|
||||||
|
|
||||||
|
// Aggregate channels from all guilds
|
||||||
|
await foreach (var guild in GetDiscordClient().GetUserGuildsAsync())
|
||||||
|
{
|
||||||
|
// Skip DMs if instructed to
|
||||||
|
if (!IncludeDirectMessages && guild.Id == Guild.DirectMessages.Id)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
await foreach (var channel in GetDiscordClient().GetGuildChannelsAsync(guild.Id))
|
||||||
|
{
|
||||||
|
channels.Add(channel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await ExportMultipleAsync(console, channels);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,8 +12,8 @@ namespace DiscordChatExporter.Cli.Commands
|
|||||||
{
|
{
|
||||||
public override async ValueTask ExecuteAsync(IConsole console)
|
public override async ValueTask ExecuteAsync(IConsole console)
|
||||||
{
|
{
|
||||||
var dmChannels = await GetDiscordClient().GetGuildChannelsAsync(Guild.DirectMessages.Id);
|
var channels = await GetDiscordClient().GetGuildChannelsAsync(Guild.DirectMessages.Id);
|
||||||
await ExportMultipleAsync(console, dmChannels);
|
await ExportMultipleAsync(console, channels);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -14,8 +14,8 @@ namespace DiscordChatExporter.Cli.Commands
|
|||||||
|
|
||||||
public override async ValueTask ExecuteAsync(IConsole console)
|
public override async ValueTask ExecuteAsync(IConsole console)
|
||||||
{
|
{
|
||||||
var guildChannels = await GetDiscordClient().GetGuildChannelsAsync(GuildId);
|
var channels = await GetDiscordClient().GetGuildChannelsAsync(GuildId);
|
||||||
await ExportMultipleAsync(console, guildChannels);
|
await ExportMultipleAsync(console, channels);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -7,7 +7,7 @@ using DiscordChatExporter.Domain.Utilities;
|
|||||||
|
|
||||||
namespace DiscordChatExporter.Cli.Commands
|
namespace DiscordChatExporter.Cli.Commands
|
||||||
{
|
{
|
||||||
[Command("channels", Description = "Get the list of channels in specified guild.")]
|
[Command("channels", Description = "Get the list of channels in a guild.")]
|
||||||
public class GetChannelsCommand : TokenCommandBase
|
public class GetChannelsCommand : TokenCommandBase
|
||||||
{
|
{
|
||||||
[CommandOption("guild", 'g', IsRequired = true, Description = "Guild ID.")]
|
[CommandOption("guild", 'g', IsRequired = true, Description = "Guild ID.")]
|
||||||
@@ -15,9 +15,9 @@ namespace DiscordChatExporter.Cli.Commands
|
|||||||
|
|
||||||
public override async ValueTask ExecuteAsync(IConsole console)
|
public override async ValueTask ExecuteAsync(IConsole console)
|
||||||
{
|
{
|
||||||
var guildChannels = await GetDiscordClient().GetGuildChannelsAsync(GuildId);
|
var channels = await GetDiscordClient().GetGuildChannelsAsync(GuildId);
|
||||||
|
|
||||||
foreach (var channel in guildChannels.OrderBy(c => c.Category).ThenBy(c => c.Name))
|
foreach (var channel in channels.OrderBy(c => c.Category).ThenBy(c => c.Name))
|
||||||
console.Output.WriteLine($"{channel.Id} | {channel.Category} / {channel.Name}");
|
console.Output.WriteLine($"{channel.Id} | {channel.Category} / {channel.Name}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,9 +13,9 @@ namespace DiscordChatExporter.Cli.Commands
|
|||||||
{
|
{
|
||||||
public override async ValueTask ExecuteAsync(IConsole console)
|
public override async ValueTask ExecuteAsync(IConsole console)
|
||||||
{
|
{
|
||||||
var dmChannels = await GetDiscordClient().GetGuildChannelsAsync(Guild.DirectMessages.Id);
|
var channels = await GetDiscordClient().GetGuildChannelsAsync(Guild.DirectMessages.Id);
|
||||||
|
|
||||||
foreach (var channel in dmChannels.OrderBy(c => c.Category).ThenBy(c => c.Name))
|
foreach (var channel in channels.OrderBy(c => c.Category).ThenBy(c => c.Name))
|
||||||
console.Output.WriteLine($"{channel.Id} | {channel.Category} / {channel.Name}");
|
console.Output.WriteLine($"{channel.Id} | {channel.Category} / {channel.Name}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ namespace DiscordChatExporter.Domain.Discord.Models
|
|||||||
|
|
||||||
public DateTimeOffset? EditedTimestamp { get; }
|
public DateTimeOffset? EditedTimestamp { get; }
|
||||||
|
|
||||||
|
public DateTimeOffset? CallEndedTimestamp { get; }
|
||||||
|
|
||||||
public bool IsPinned { get; }
|
public bool IsPinned { get; }
|
||||||
|
|
||||||
public string Content { get; }
|
public string Content { get; }
|
||||||
@@ -51,6 +53,7 @@ namespace DiscordChatExporter.Domain.Discord.Models
|
|||||||
User author,
|
User author,
|
||||||
DateTimeOffset timestamp,
|
DateTimeOffset timestamp,
|
||||||
DateTimeOffset? editedTimestamp,
|
DateTimeOffset? editedTimestamp,
|
||||||
|
DateTimeOffset? callEndedTimestamp,
|
||||||
bool isPinned,
|
bool isPinned,
|
||||||
string content,
|
string content,
|
||||||
IReadOnlyList<Attachment> attachments,
|
IReadOnlyList<Attachment> attachments,
|
||||||
@@ -63,6 +66,7 @@ namespace DiscordChatExporter.Domain.Discord.Models
|
|||||||
Author = author;
|
Author = author;
|
||||||
Timestamp = timestamp;
|
Timestamp = timestamp;
|
||||||
EditedTimestamp = editedTimestamp;
|
EditedTimestamp = editedTimestamp;
|
||||||
|
CallEndedTimestamp = callEndedTimestamp;
|
||||||
IsPinned = isPinned;
|
IsPinned = isPinned;
|
||||||
Content = content;
|
Content = content;
|
||||||
Attachments = attachments;
|
Attachments = attachments;
|
||||||
@@ -82,6 +86,7 @@ namespace DiscordChatExporter.Domain.Discord.Models
|
|||||||
var author = json.GetProperty("author").Pipe(User.Parse);
|
var author = json.GetProperty("author").Pipe(User.Parse);
|
||||||
var timestamp = json.GetProperty("timestamp").GetDateTimeOffset();
|
var timestamp = json.GetProperty("timestamp").GetDateTimeOffset();
|
||||||
var editedTimestamp = json.GetPropertyOrNull("edited_timestamp")?.GetDateTimeOffset();
|
var editedTimestamp = json.GetPropertyOrNull("edited_timestamp")?.GetDateTimeOffset();
|
||||||
|
var callEndedTimestamp = json.GetPropertyOrNull("call")?.GetPropertyOrNull("ended_timestamp")?.GetDateTimeOffset();
|
||||||
var type = (MessageType) json.GetProperty("type").GetInt32();
|
var type = (MessageType) json.GetProperty("type").GetInt32();
|
||||||
var isPinned = json.GetPropertyOrNull("pinned")?.GetBoolean() ?? false;
|
var isPinned = json.GetPropertyOrNull("pinned")?.GetBoolean() ?? false;
|
||||||
|
|
||||||
@@ -89,7 +94,8 @@ namespace DiscordChatExporter.Domain.Discord.Models
|
|||||||
{
|
{
|
||||||
MessageType.RecipientAdd => "Added a recipient.",
|
MessageType.RecipientAdd => "Added a recipient.",
|
||||||
MessageType.RecipientRemove => "Removed a recipient.",
|
MessageType.RecipientRemove => "Removed a recipient.",
|
||||||
MessageType.Call => "Started a call.",
|
MessageType.Call =>
|
||||||
|
$"Started a call that lasted {callEndedTimestamp?.Pipe(t => t - timestamp).Pipe(t => (int) t.TotalMinutes) ?? 0} minutes.",
|
||||||
MessageType.ChannelNameChange => "Changed the channel name.",
|
MessageType.ChannelNameChange => "Changed the channel name.",
|
||||||
MessageType.ChannelIconChange => "Changed the channel icon.",
|
MessageType.ChannelIconChange => "Changed the channel icon.",
|
||||||
MessageType.ChannelPinnedMessage => "Pinned a message.",
|
MessageType.ChannelPinnedMessage => "Pinned a message.",
|
||||||
@@ -119,6 +125,7 @@ namespace DiscordChatExporter.Domain.Discord.Models
|
|||||||
author,
|
author,
|
||||||
timestamp,
|
timestamp,
|
||||||
editedTimestamp,
|
editedTimestamp,
|
||||||
|
callEndedTimestamp,
|
||||||
isPinned,
|
isPinned,
|
||||||
content,
|
content,
|
||||||
attachments,
|
attachments,
|
||||||
|
|||||||
@@ -84,7 +84,10 @@ namespace DiscordChatExporter.Domain.Exporting
|
|||||||
|
|
||||||
return relativeFilePath;
|
return relativeFilePath;
|
||||||
}
|
}
|
||||||
catch (HttpRequestException)
|
// Try to catch only exceptions related to failed HTTP requests
|
||||||
|
// https://github.com/Tyrrrz/DiscordChatExporter/issues/332
|
||||||
|
// https://github.com/Tyrrrz/DiscordChatExporter/issues/372
|
||||||
|
catch (Exception ex) when (ex is HttpRequestException || ex is OperationCanceledException)
|
||||||
{
|
{
|
||||||
// We don't want this to crash the exporting process in case of failure
|
// We don't want this to crash the exporting process in case of failure
|
||||||
return url;
|
return url;
|
||||||
|
|||||||
@@ -2,31 +2,31 @@
|
|||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: Whitney;
|
font-family: Whitney;
|
||||||
src: url(https://discord.com/assets/6c6374bad0b0b6d204d8d6dc4a18d820.woff);
|
src: url(https://cdn.jsdelivr.net/gh/Tyrrrz/DiscordFonts@master/whitney-300.woff);
|
||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: Whitney;
|
font-family: Whitney;
|
||||||
src: url(https://discord.com/assets/e8acd7d9bf6207f99350ca9f9e23b168.woff);
|
src: url(https://cdn.jsdelivr.net/gh/Tyrrrz/DiscordFonts@master/whitney-400.woff);
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: Whitney;
|
font-family: Whitney;
|
||||||
src: url(https://discord.com/assets/3bdef1251a424500c1b3a78dea9b7e57.woff);
|
src: url(https://cdn.jsdelivr.net/gh/Tyrrrz/DiscordFonts@master/whitney-500.woff);
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: Whitney;
|
font-family: Whitney;
|
||||||
src: url(https://discord.com/assets/be0060dafb7a0e31d2a1ca17c0708636.woff);
|
src: url(https://cdn.jsdelivr.net/gh/Tyrrrz/DiscordFonts@master/whitney-600.woff);
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: Whitney;
|
font-family: Whitney;
|
||||||
src: url(https://discord.com/assets/8e12fb4f14d9c4592eb8ec9f22337b04.woff);
|
src: url(https://cdn.jsdelivr.net/gh/Tyrrrz/DiscordFonts@master/whitney-700.woff);
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -197,6 +197,7 @@ namespace DiscordChatExporter.Domain.Exporting.Writers
|
|||||||
_writer.WriteString("type", message.Type.ToString());
|
_writer.WriteString("type", message.Type.ToString());
|
||||||
_writer.WriteString("timestamp", message.Timestamp);
|
_writer.WriteString("timestamp", message.Timestamp);
|
||||||
_writer.WriteString("timestampEdited", message.EditedTimestamp);
|
_writer.WriteString("timestampEdited", message.EditedTimestamp);
|
||||||
|
_writer.WriteString("callEndedTimestamp", message.CallEndedTimestamp);
|
||||||
_writer.WriteBoolean("isPinned", message.IsPinned);
|
_writer.WriteBoolean("isPinned", message.IsPinned);
|
||||||
|
|
||||||
// Content
|
// Content
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||||
<Version>2.22</Version>
|
<Version>2.23</Version>
|
||||||
<Company>Tyrrrz</Company>
|
<Company>Tyrrrz</Company>
|
||||||
<Copyright>Copyright (c) Alexey Golub</Copyright>
|
<Copyright>Copyright (c) Alexey Golub</Copyright>
|
||||||
<LangVersion>latest</LangVersion>
|
<LangVersion>latest</LangVersion>
|
||||||
|
|||||||
Reference in New Issue
Block a user