mirror of
https://github.com/Tyrrrz/DiscordChatExporter.git
synced 2025-06-08 10:22:25 -04:00
Formatting output paths (#472)
This commit is contained in:
parent
915f4c8d9f
commit
77b7977324
9 changed files with 152 additions and 31 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -4,6 +4,7 @@
|
||||||
*.userosscache
|
*.userosscache
|
||||||
*.sln.docstates
|
*.sln.docstates
|
||||||
.idea/
|
.idea/
|
||||||
|
.vs/
|
||||||
|
|
||||||
# Build results
|
# Build results
|
||||||
[Dd]ebug/
|
[Dd]ebug/
|
||||||
|
|
|
@ -117,26 +117,33 @@ namespace DiscordChatExporter.Domain.Discord
|
||||||
{
|
{
|
||||||
var response = await GetJsonResponseAsync($"guilds/{guildId}/channels");
|
var response = await GetJsonResponseAsync($"guilds/{guildId}/channels");
|
||||||
|
|
||||||
var categories = response
|
var orderedResponse = response
|
||||||
.EnumerateArray()
|
.EnumerateArray()
|
||||||
.ToDictionary(
|
.OrderBy(j => j.GetProperty("position").GetInt32())
|
||||||
j => j.GetProperty("id").GetString(),
|
.ThenBy(j => ulong.Parse(j.GetProperty("id").GetString()));
|
||||||
j => j.GetProperty("name").GetString()
|
|
||||||
);
|
|
||||||
|
|
||||||
foreach (var channelJson in response.EnumerateArray())
|
var categories = orderedResponse
|
||||||
|
.Where(j => j.GetProperty("type").GetInt32() == (int)ChannelType.GuildCategory)
|
||||||
|
.Select((j, index) => ChannelCategory.Parse(j, index + 1))
|
||||||
|
.ToDictionary(j => j.Id.ToString());
|
||||||
|
|
||||||
|
var position = 0;
|
||||||
|
|
||||||
|
foreach (var channelJson in orderedResponse)
|
||||||
{
|
{
|
||||||
var parentId = channelJson.GetPropertyOrNull("parent_id")?.GetString();
|
var parentId = channelJson.GetPropertyOrNull("parent_id")?.GetString();
|
||||||
var category = !string.IsNullOrWhiteSpace(parentId)
|
var category = !string.IsNullOrWhiteSpace(parentId)
|
||||||
? categories.GetValueOrDefault(parentId)
|
? categories.GetValueOrDefault(parentId)
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
var channel = Channel.Parse(channelJson, category);
|
var channel = Channel.Parse(channelJson, category, position);
|
||||||
|
|
||||||
// Skip non-text channels
|
// Skip non-text channels
|
||||||
if (!channel.IsTextChannel)
|
if (!channel.IsTextChannel)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
position++;
|
||||||
|
|
||||||
yield return channel;
|
yield return channel;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -162,10 +169,11 @@ namespace DiscordChatExporter.Domain.Discord
|
||||||
return response?.Pipe(Member.Parse);
|
return response?.Pipe(Member.Parse);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async ValueTask<string> GetChannelCategoryAsync(Snowflake channelParentId)
|
public async ValueTask<ChannelCategory> GetChannelCategoryAsync(Snowflake channelId)
|
||||||
{
|
{
|
||||||
var response = await GetJsonResponseAsync($"channels/{channelParentId}");
|
var response = await GetJsonResponseAsync($"channels/{channelId}");
|
||||||
return response.GetProperty("name").GetString();
|
|
||||||
|
return ChannelCategory.Parse(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async ValueTask<Channel> GetChannelAsync(Snowflake channelId)
|
public async ValueTask<Channel> GetChannelAsync(Snowflake channelId)
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using System.Linq;
|
using System;
|
||||||
|
using System.Linq;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using DiscordChatExporter.Domain.Discord.Models.Common;
|
using DiscordChatExporter.Domain.Discord.Models.Common;
|
||||||
using DiscordChatExporter.Domain.Utilities;
|
using DiscordChatExporter.Domain.Utilities;
|
||||||
|
@ -36,28 +37,34 @@ namespace DiscordChatExporter.Domain.Discord.Models
|
||||||
|
|
||||||
public Snowflake GuildId { get; }
|
public Snowflake GuildId { get; }
|
||||||
|
|
||||||
public string Category { get; }
|
public ChannelCategory Category { get; }
|
||||||
|
|
||||||
public string Name { get; }
|
public string Name { get; }
|
||||||
|
|
||||||
|
public int Position { get; }
|
||||||
|
|
||||||
public string? Topic { get; }
|
public string? Topic { get; }
|
||||||
|
|
||||||
public Channel(Snowflake id, ChannelType type, Snowflake guildId, string category, string name, string? topic)
|
public Channel(Snowflake id, ChannelType type, Snowflake guildId, ChannelCategory? category, string name, int position, string? topic)
|
||||||
{
|
{
|
||||||
Id = id;
|
Id = id;
|
||||||
Type = type;
|
Type = type;
|
||||||
GuildId = guildId;
|
GuildId = guildId;
|
||||||
Category = category;
|
Category = category ?? GetDefaultCategory(type);
|
||||||
Name = name;
|
Name = name;
|
||||||
|
Position = position;
|
||||||
Topic = topic;
|
Topic = topic;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string ToString() => Name;
|
public override string ToString() => Name;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public partial class Channel
|
public partial class Channel
|
||||||
{
|
{
|
||||||
private static string GetDefaultCategory(ChannelType channelType) => channelType switch
|
private static ChannelCategory GetDefaultCategory(ChannelType channelType) => new(
|
||||||
|
Snowflake.Zero,
|
||||||
|
channelType switch
|
||||||
{
|
{
|
||||||
ChannelType.GuildTextChat => "Text",
|
ChannelType.GuildTextChat => "Text",
|
||||||
ChannelType.DirectTextChat => "Private",
|
ChannelType.DirectTextChat => "Private",
|
||||||
|
@ -65,9 +72,11 @@ namespace DiscordChatExporter.Domain.Discord.Models
|
||||||
ChannelType.GuildNews => "News",
|
ChannelType.GuildNews => "News",
|
||||||
ChannelType.GuildStore => "Store",
|
ChannelType.GuildStore => "Store",
|
||||||
_ => "Default"
|
_ => "Default"
|
||||||
};
|
},
|
||||||
|
0
|
||||||
|
);
|
||||||
|
|
||||||
public static Channel Parse(JsonElement json, string? category = null)
|
public static Channel Parse(JsonElement json, ChannelCategory? category = null, int? position = null)
|
||||||
{
|
{
|
||||||
var id = json.GetProperty("id").GetString().Pipe(Snowflake.Parse);
|
var id = json.GetProperty("id").GetString().Pipe(Snowflake.Parse);
|
||||||
var guildId = json.GetPropertyOrNull("guild_id")?.GetString().Pipe(Snowflake.Parse);
|
var guildId = json.GetPropertyOrNull("guild_id")?.GetString().Pipe(Snowflake.Parse);
|
||||||
|
@ -80,12 +89,15 @@ namespace DiscordChatExporter.Domain.Discord.Models
|
||||||
json.GetPropertyOrNull("recipients")?.EnumerateArray().Select(User.Parse).Select(u => u.Name).JoinToString(", ") ??
|
json.GetPropertyOrNull("recipients")?.EnumerateArray().Select(User.Parse).Select(u => u.Name).JoinToString(", ") ??
|
||||||
id.ToString();
|
id.ToString();
|
||||||
|
|
||||||
|
position ??= json.GetProperty("position").GetInt32();
|
||||||
|
|
||||||
return new Channel(
|
return new Channel(
|
||||||
id,
|
id,
|
||||||
type,
|
type,
|
||||||
guildId ?? Guild.DirectMessages.Id,
|
guildId ?? Guild.DirectMessages.Id,
|
||||||
category ?? GetDefaultCategory(type),
|
category ?? GetDefaultCategory(type),
|
||||||
name,
|
name,
|
||||||
|
position.Value,
|
||||||
topic
|
topic
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
48
DiscordChatExporter.Domain/Discord/Models/ChannelCategory.cs
Normal file
48
DiscordChatExporter.Domain/Discord/Models/ChannelCategory.cs
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text.Json;
|
||||||
|
using DiscordChatExporter.Domain.Discord.Models.Common;
|
||||||
|
using DiscordChatExporter.Domain.Utilities;
|
||||||
|
using JsonExtensions.Reading;
|
||||||
|
using Tyrrrz.Extensions;
|
||||||
|
|
||||||
|
namespace DiscordChatExporter.Domain.Discord.Models
|
||||||
|
{
|
||||||
|
public partial class ChannelCategory : IHasId
|
||||||
|
{
|
||||||
|
public Snowflake Id { get; }
|
||||||
|
|
||||||
|
public string Name { get; }
|
||||||
|
|
||||||
|
public int Position { get; }
|
||||||
|
|
||||||
|
public ChannelCategory(Snowflake id, string name, int position)
|
||||||
|
{
|
||||||
|
Id = id;
|
||||||
|
Name = name;
|
||||||
|
Position = position;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString() => Name;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public partial class ChannelCategory
|
||||||
|
{
|
||||||
|
public static ChannelCategory Parse(JsonElement json, int? position = null)
|
||||||
|
{
|
||||||
|
var id = json.GetProperty("id").GetString().Pipe(Snowflake.Parse);
|
||||||
|
position ??= json.GetProperty("position").GetInt32();
|
||||||
|
|
||||||
|
var name = json.GetPropertyOrNull("name")?.GetString() ??
|
||||||
|
json.GetPropertyOrNull("recipients")?.EnumerateArray().Select(User.Parse).Select(u => u.Name).JoinToString(", ") ??
|
||||||
|
id.ToString();
|
||||||
|
|
||||||
|
return new ChannelCategory(
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
position.Value
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace DiscordChatExporter.Domain.Discord.Models.Common
|
||||||
|
{
|
||||||
|
public partial class ChannelPositionBasedComparer : IComparer<Channel>
|
||||||
|
{
|
||||||
|
public int Compare(Channel? x, Channel? y)
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
if (x != null)
|
||||||
|
{
|
||||||
|
result = x.Position.CompareTo(y?.Position);
|
||||||
|
}
|
||||||
|
else if (y != null)
|
||||||
|
{
|
||||||
|
result = -y.Position.CompareTo(x?.Position);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = 0;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public partial class ChannelPositionBasedComparer
|
||||||
|
{
|
||||||
|
public static ChannelPositionBasedComparer Instance { get; } = new();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,7 @@
|
||||||
using System.IO;
|
using System;
|
||||||
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
using DiscordChatExporter.Domain.Discord;
|
using DiscordChatExporter.Domain.Discord;
|
||||||
using DiscordChatExporter.Domain.Discord.Models;
|
using DiscordChatExporter.Domain.Discord.Models;
|
||||||
using DiscordChatExporter.Domain.Internal;
|
using DiscordChatExporter.Domain.Internal;
|
||||||
|
@ -81,6 +83,26 @@ namespace DiscordChatExporter.Domain.Exporting
|
||||||
Snowflake? after = null,
|
Snowflake? after = null,
|
||||||
Snowflake? before = null)
|
Snowflake? before = null)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
// Formats path
|
||||||
|
outputPath = Regex.Replace(outputPath, "%.", m =>
|
||||||
|
PathEx.EscapePath(m.Value switch
|
||||||
|
{
|
||||||
|
"%g" => guild.Id.ToString(),
|
||||||
|
"%G" => guild.Name,
|
||||||
|
"%t" => channel.Category.Id.ToString(),
|
||||||
|
"%T" => channel.Category.Name,
|
||||||
|
"%c" => channel.Id.ToString(),
|
||||||
|
"%C" => channel.Name,
|
||||||
|
"%p" => channel.Position.ToString(),
|
||||||
|
"%P" => channel.Category.Position.ToString(),
|
||||||
|
"%a" => (after ?? Snowflake.Zero).ToDate().ToString("yyyy-MM-dd"),
|
||||||
|
"%b" => (before?.ToDate() ?? DateTime.Now).ToString("yyyy-MM-dd"),
|
||||||
|
"%%" => "%",
|
||||||
|
_ => m.Value
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
// Output is a directory
|
// Output is a directory
|
||||||
if (Directory.Exists(outputPath) || string.IsNullOrWhiteSpace(Path.GetExtension(outputPath)))
|
if (Directory.Exists(outputPath) || string.IsNullOrWhiteSpace(Path.GetExtension(outputPath)))
|
||||||
{
|
{
|
||||||
|
@ -102,7 +124,7 @@ namespace DiscordChatExporter.Domain.Exporting
|
||||||
var buffer = new StringBuilder();
|
var buffer = new StringBuilder();
|
||||||
|
|
||||||
// Guild and channel names
|
// Guild and channel names
|
||||||
buffer.Append($"{guild.Name} - {channel.Category} - {channel.Name} [{channel.Id}]");
|
buffer.Append($"{guild.Name} - {channel.Category.Name} - {channel.Name} [{channel.Id}]");
|
||||||
|
|
||||||
// Date range
|
// Date range
|
||||||
if (after != null || before != null)
|
if (after != null || before != null)
|
||||||
|
|
|
@ -192,7 +192,7 @@ namespace DiscordChatExporter.Domain.Exporting.Writers
|
||||||
_writer.WriteStartObject("channel");
|
_writer.WriteStartObject("channel");
|
||||||
_writer.WriteString("id", Context.Request.Channel.Id.ToString());
|
_writer.WriteString("id", Context.Request.Channel.Id.ToString());
|
||||||
_writer.WriteString("type", Context.Request.Channel.Type.ToString());
|
_writer.WriteString("type", Context.Request.Channel.Type.ToString());
|
||||||
_writer.WriteString("category", Context.Request.Channel.Category);
|
_writer.WriteString("category", Context.Request.Channel.Category.Name);
|
||||||
_writer.WriteString("name", Context.Request.Channel.Name);
|
_writer.WriteString("name", Context.Request.Channel.Name);
|
||||||
_writer.WriteString("topic", Context.Request.Channel.Topic);
|
_writer.WriteString("topic", Context.Request.Channel.Topic);
|
||||||
_writer.WriteEndObject();
|
_writer.WriteEndObject();
|
||||||
|
|
|
@ -113,7 +113,7 @@ namespace DiscordChatExporter.Domain.Exporting.Writers
|
||||||
{
|
{
|
||||||
await _writer.WriteLineAsync('='.Repeat(62));
|
await _writer.WriteLineAsync('='.Repeat(62));
|
||||||
await _writer.WriteLineAsync($"Guild: {Context.Request.Guild.Name}");
|
await _writer.WriteLineAsync($"Guild: {Context.Request.Guild.Name}");
|
||||||
await _writer.WriteLineAsync($"Channel: {Context.Request.Channel.Category} / {Context.Request.Channel.Name}");
|
await _writer.WriteLineAsync($"Channel: {Context.Request.Channel.Category.Name} / {Context.Request.Channel.Name}");
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(Context.Request.Channel.Topic))
|
if (!string.IsNullOrWhiteSpace(Context.Request.Channel.Topic))
|
||||||
await _writer.WriteLineAsync($"Topic: {Context.Request.Channel.Topic}");
|
await _writer.WriteLineAsync($"Topic: {Context.Request.Channel.Topic}");
|
||||||
|
|
|
@ -30,7 +30,7 @@
|
||||||
<PropertyGroupDescription PropertyName="Category" />
|
<PropertyGroupDescription PropertyName="Category" />
|
||||||
</CollectionViewSource.GroupDescriptions>
|
</CollectionViewSource.GroupDescriptions>
|
||||||
<CollectionViewSource.SortDescriptions>
|
<CollectionViewSource.SortDescriptions>
|
||||||
<componentModel:SortDescription Direction="Ascending" PropertyName="Category" />
|
<componentModel:SortDescription Direction="Ascending" PropertyName="Position" />
|
||||||
<componentModel:SortDescription Direction="Ascending" PropertyName="Name" />
|
<componentModel:SortDescription Direction="Ascending" PropertyName="Name" />
|
||||||
</CollectionViewSource.SortDescriptions>
|
</CollectionViewSource.SortDescriptions>
|
||||||
</CollectionViewSource>
|
</CollectionViewSource>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue