mirror of
https://github.com/Tyrrrz/DiscordChatExporter.git
synced 2026-03-31 17:43:04 -06:00
refactor: pt 1 of redesign for generic components structure
This commit is contained in:
parent
1a671b31e6
commit
0414da2fb7
|
|
@ -1,32 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text.Json;
|
|
||||||
using JsonExtensions.Reading;
|
|
||||||
|
|
||||||
namespace DiscordChatExporter.Core.Discord.Data.Components;
|
|
||||||
|
|
||||||
// https://discord.com/developers/docs/components/reference#action-row
|
|
||||||
public partial record ActionRowComponent(IReadOnlyList<ButtonComponent> Components)
|
|
||||||
{
|
|
||||||
public bool HasButtons => Components.Any();
|
|
||||||
}
|
|
||||||
|
|
||||||
public partial record ActionRowComponent
|
|
||||||
{
|
|
||||||
public static ActionRowComponent? Parse(JsonElement json)
|
|
||||||
{
|
|
||||||
var type = json.GetPropertyOrNull("type")?.GetInt32OrNull();
|
|
||||||
if (type != 1)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
var components =
|
|
||||||
json.GetPropertyOrNull("components")
|
|
||||||
?.EnumerateArrayOrNull()
|
|
||||||
?.Where(c => c.GetPropertyOrNull("type")?.GetInt32OrNull() == 2) // TODO: support other component types (selects)
|
|
||||||
?.Select(ButtonComponent.Parse)
|
|
||||||
.ToArray()
|
|
||||||
?? [];
|
|
||||||
|
|
||||||
return new ActionRowComponent(components);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text.Json;
|
||||||
|
using DiscordChatExporter.Core.Utils.Extensions;
|
||||||
|
using JsonExtensions.Reading;
|
||||||
|
|
||||||
|
namespace DiscordChatExporter.Core.Discord.Data.Components;
|
||||||
|
|
||||||
|
// https://docs.discord.com/developers/components/reference#what-is-a-component
|
||||||
|
public partial record MessageComponent(
|
||||||
|
MessageComponentKind Kind,
|
||||||
|
IReadOnlyList<MessageComponent> Components,
|
||||||
|
ButtonComponent? Button
|
||||||
|
)
|
||||||
|
{
|
||||||
|
public bool HasButtons => Button is not null || Components.Any(c => c.HasButtons);
|
||||||
|
|
||||||
|
public IReadOnlyList<ButtonComponent> Buttons =>
|
||||||
|
Components.Select(c => c.Button).WhereNotNull().ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public partial record MessageComponent
|
||||||
|
{
|
||||||
|
public static MessageComponent? Parse(JsonElement json)
|
||||||
|
{
|
||||||
|
var rawType = json.GetPropertyOrNull("type")?.GetInt32OrNull();
|
||||||
|
if (rawType is null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var type = rawType.Value;
|
||||||
|
if (!Enum.IsDefined(typeof(MessageComponentKind), type))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var kind = (MessageComponentKind)type;
|
||||||
|
|
||||||
|
var components =
|
||||||
|
json.GetPropertyOrNull("components")
|
||||||
|
?.EnumerateArrayOrNull()
|
||||||
|
?.Select(Parse)
|
||||||
|
.WhereNotNull()
|
||||||
|
.ToArray()
|
||||||
|
?? [];
|
||||||
|
|
||||||
|
var button = kind == MessageComponentKind.Button ? ButtonComponent.Parse(json) : null;
|
||||||
|
|
||||||
|
return new MessageComponent(kind, components, button);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
namespace DiscordChatExporter.Core.Discord.Data.Components;
|
||||||
|
|
||||||
|
// https://discord.com/developers/docs/components/reference#component-object-component-types
|
||||||
|
public enum MessageComponentKind
|
||||||
|
{
|
||||||
|
ActionRow = 1,
|
||||||
|
Button = 2,
|
||||||
|
StringSelect = 3,
|
||||||
|
TextInput = 4,
|
||||||
|
UserSelect = 5,
|
||||||
|
RoleSelect = 6,
|
||||||
|
MentionableSelect = 7,
|
||||||
|
ChannelSelect = 8,
|
||||||
|
Section = 9,
|
||||||
|
TextDisplay = 10,
|
||||||
|
Thumbnail = 11,
|
||||||
|
MediaGallery = 12,
|
||||||
|
File = 13,
|
||||||
|
Separator = 14,
|
||||||
|
Container = 17,
|
||||||
|
Label = 18,
|
||||||
|
}
|
||||||
|
|
@ -22,9 +22,9 @@ public partial record Message(
|
||||||
bool IsPinned,
|
bool IsPinned,
|
||||||
string Content,
|
string Content,
|
||||||
IReadOnlyList<Attachment> Attachments,
|
IReadOnlyList<Attachment> Attachments,
|
||||||
IReadOnlyList<ActionRowComponent> Components,
|
|
||||||
IReadOnlyList<Embed> Embeds,
|
IReadOnlyList<Embed> Embeds,
|
||||||
IReadOnlyList<Sticker> Stickers,
|
IReadOnlyList<Sticker> Stickers,
|
||||||
|
IReadOnlyList<MessageComponent> Components,
|
||||||
IReadOnlyList<Reaction> Reactions,
|
IReadOnlyList<Reaction> Reactions,
|
||||||
IReadOnlyList<User> MentionedUsers,
|
IReadOnlyList<User> MentionedUsers,
|
||||||
MessageReference? Reference,
|
MessageReference? Reference,
|
||||||
|
|
@ -152,14 +152,6 @@ public partial record Message
|
||||||
.ToArray()
|
.ToArray()
|
||||||
?? [];
|
?? [];
|
||||||
|
|
||||||
var components =
|
|
||||||
json.GetPropertyOrNull("components")
|
|
||||||
?.EnumerateArrayOrNull()
|
|
||||||
?.Select(ActionRowComponent.Parse)
|
|
||||||
.WhereNotNull()
|
|
||||||
.ToArray()
|
|
||||||
?? [];
|
|
||||||
|
|
||||||
var embeds = NormalizeEmbeds(
|
var embeds = NormalizeEmbeds(
|
||||||
json.GetPropertyOrNull("embeds")?.EnumerateArrayOrNull()?.Select(Embed.Parse).ToArray()
|
json.GetPropertyOrNull("embeds")?.EnumerateArrayOrNull()?.Select(Embed.Parse).ToArray()
|
||||||
?? []
|
?? []
|
||||||
|
|
@ -172,6 +164,14 @@ public partial record Message
|
||||||
.ToArray()
|
.ToArray()
|
||||||
?? [];
|
?? [];
|
||||||
|
|
||||||
|
var components =
|
||||||
|
json.GetPropertyOrNull("components")
|
||||||
|
?.EnumerateArrayOrNull()
|
||||||
|
?.Select(MessageComponent.Parse)
|
||||||
|
.WhereNotNull()
|
||||||
|
.ToArray()
|
||||||
|
?? [];
|
||||||
|
|
||||||
var reactions =
|
var reactions =
|
||||||
json.GetPropertyOrNull("reactions")
|
json.GetPropertyOrNull("reactions")
|
||||||
?.EnumerateArrayOrNull()
|
?.EnumerateArrayOrNull()
|
||||||
|
|
@ -209,9 +209,9 @@ public partial record Message
|
||||||
isPinned,
|
isPinned,
|
||||||
content,
|
content,
|
||||||
attachments,
|
attachments,
|
||||||
components,
|
|
||||||
embeds,
|
embeds,
|
||||||
stickers,
|
stickers,
|
||||||
|
components,
|
||||||
reactions,
|
reactions,
|
||||||
mentionedUsers,
|
mentionedUsers,
|
||||||
messageReference,
|
messageReference,
|
||||||
|
|
|
||||||
|
|
@ -373,42 +373,40 @@ internal class JsonMessageWriter(Stream stream, ExportContext context)
|
||||||
await _writer.FlushAsync(cancellationToken);
|
await _writer.FlushAsync(cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async ValueTask WriteButtonComponentAsync(
|
private async ValueTask WriteComponentAsync(
|
||||||
ButtonComponent button,
|
MessageComponent component,
|
||||||
{
|
|
||||||
_writer.WriteStartObject();
|
|
||||||
|
|
||||||
_writer.WriteString("type", "Button");
|
|
||||||
_writer.WriteString("style", button.Style.ToString());
|
|
||||||
_writer.WriteString("label", button.Label);
|
|
||||||
_writer.WriteString("url", button.Url);
|
|
||||||
_writer.WriteString("customId", button.CustomId);
|
|
||||||
_writer.WriteString("skuId", button.SkuId?.ToString());
|
|
||||||
_writer.WriteBoolean("isDisabled", button.IsDisabled);
|
|
||||||
|
|
||||||
if (button.Emoji is not null)
|
|
||||||
{
|
|
||||||
_writer.WritePropertyName("emoji");
|
|
||||||
await WriteEmojiAsync(button.Emoji, cancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
_writer.WriteEndObject();
|
|
||||||
await _writer.FlushAsync(cancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async ValueTask WriteActionRowComponentAsync(
|
|
||||||
ActionRowComponent actionRow,
|
|
||||||
CancellationToken cancellationToken = default
|
CancellationToken cancellationToken = default
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
_writer.WriteStartObject();
|
_writer.WriteStartObject();
|
||||||
|
|
||||||
_writer.WriteString("type", "ActionRow");
|
_writer.WriteString("type", component.Kind.ToString());
|
||||||
|
|
||||||
_writer.WriteStartArray("components");
|
if (component.Button is not null)
|
||||||
foreach (var button in actionRow.Components)
|
{
|
||||||
await WriteButtonComponentAsync(button, cancellationToken);
|
_writer.WriteString("style", component.Button.Style.ToString());
|
||||||
_writer.WriteEndArray();
|
_writer.WriteString("label", component.Button.Label);
|
||||||
|
_writer.WriteString("url", component.Button.Url);
|
||||||
|
_writer.WriteString("customId", component.Button.CustomId);
|
||||||
|
_writer.WriteString("skuId", component.Button.SkuId?.ToString());
|
||||||
|
_writer.WriteBoolean("isDisabled", component.Button.IsDisabled);
|
||||||
|
|
||||||
|
if (component.Button.Emoji is not null)
|
||||||
|
{
|
||||||
|
_writer.WritePropertyName("emoji");
|
||||||
|
await WriteEmojiAsync(component.Button.Emoji, cancellationToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (component.Components.Any())
|
||||||
|
{
|
||||||
|
_writer.WriteStartArray("components");
|
||||||
|
|
||||||
|
foreach (var child in component.Components)
|
||||||
|
await WriteComponentAsync(child, cancellationToken);
|
||||||
|
|
||||||
|
_writer.WriteEndArray();
|
||||||
|
}
|
||||||
|
|
||||||
_writer.WriteEndObject();
|
_writer.WriteEndObject();
|
||||||
await _writer.FlushAsync(cancellationToken);
|
await _writer.FlushAsync(cancellationToken);
|
||||||
|
|
@ -524,7 +522,7 @@ internal class JsonMessageWriter(Stream stream, ExportContext context)
|
||||||
_writer.WriteStartArray("components");
|
_writer.WriteStartArray("components");
|
||||||
|
|
||||||
foreach (var component in message.Components)
|
foreach (var component in message.Components)
|
||||||
await WriteActionRowComponentAsync(component, cancellationToken);
|
await WriteComponentAsync(component, cancellationToken);
|
||||||
|
|
||||||
_writer.WriteEndArray();
|
_writer.WriteEndArray();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -733,13 +733,13 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
@* Components *@
|
@* Components *@
|
||||||
@if (message.Components.Any(c => c.HasButtons))
|
@if (message.Components.Any(c => c.Kind == MessageComponentKind.ActionRow && c.HasButtons))
|
||||||
{
|
{
|
||||||
<div class="chatlog__components">
|
<div class="chatlog__components">
|
||||||
@foreach (var actionRow in message.Components.Where(c => c.HasButtons))
|
@foreach (var actionRow in message.Components.Where(c => c.Kind == MessageComponentKind.ActionRow && c.HasButtons))
|
||||||
{
|
{
|
||||||
<div class="chatlog__action-row">
|
<div class="chatlog__action-row">
|
||||||
@foreach (var button in actionRow.Components)
|
@foreach (var button in actionRow.Buttons)
|
||||||
{
|
{
|
||||||
var styleClass = button.Style switch {
|
var styleClass = button.Style switch {
|
||||||
ButtonStyle.Primary => "chatlog__component-button--primary",
|
ButtonStyle.Primary => "chatlog__component-button--primary",
|
||||||
|
|
|
||||||
152
PKGBUILD
152
PKGBUILD
|
|
@ -1,152 +0,0 @@
|
||||||
# Maintainer: Vitalii Kuzhdin <vitaliikuzhdin@gmail.com>
|
|
||||||
|
|
||||||
_sdk=10.0
|
|
||||||
_Name="DiscordChatExporter"
|
|
||||||
pkgbase="discord-chat-exporter"
|
|
||||||
pkgname=(
|
|
||||||
"${pkgbase}-core"
|
|
||||||
"${pkgbase}-cli"
|
|
||||||
"${pkgbase}-gui"
|
|
||||||
)
|
|
||||||
pkgver=2.46.1
|
|
||||||
pkgrel=1
|
|
||||||
pkgdesc="Exports Discord chat logs to a file"
|
|
||||||
arch=(
|
|
||||||
'aarch64'
|
|
||||||
'armv7h'
|
|
||||||
'x86_64'
|
|
||||||
)
|
|
||||||
url="https://github.com/Tyrrrz/${_Name}"
|
|
||||||
license=(
|
|
||||||
'MIT'
|
|
||||||
)
|
|
||||||
depends=(
|
|
||||||
"dotnet-runtime-${_sdk}"
|
|
||||||
)
|
|
||||||
makedepends=(
|
|
||||||
"dotnet-sdk-${_sdk}"
|
|
||||||
'gendesk'
|
|
||||||
)
|
|
||||||
options=(
|
|
||||||
'!strip'
|
|
||||||
'!debug'
|
|
||||||
)
|
|
||||||
_pkgsrc="${_Name}-${pkgver}"
|
|
||||||
source=(
|
|
||||||
"${pkgbase}_xdg_settings.patch"
|
|
||||||
)
|
|
||||||
b2sums=('ec3740a7c60b0f5fc2773e991e6cde9b4116d77d50094b237e118f456d9273c18a8e3bc2da2ff8a86eb35fa7df4f81c94759467b415f53e4794fb7a4e0929a91')
|
|
||||||
|
|
||||||
if [ "${CARCH}" = 'aarch64' ]; then _msarch=arm64;
|
|
||||||
elif [ "${CARCH}" = 'armv7h' ]; then _msarch=arm;
|
|
||||||
elif [ "${CARCH}" = 'x86_64' ]; then _msarch=x64; fi
|
|
||||||
|
|
||||||
_source() {
|
|
||||||
export NUGET_PACKAGES="${srcdir}/.nuget"
|
|
||||||
export DOTNET_SKIP_FIRST_TIME_EXPERIENCE=true
|
|
||||||
export DOTNET_NOLOGO=true
|
|
||||||
export DOTNET_CLI_TELEMETRY_OPTOUT=true
|
|
||||||
}
|
|
||||||
|
|
||||||
prepare() {
|
|
||||||
_source
|
|
||||||
local dotnet_restore_options=(
|
|
||||||
--runtime "linux-${_msarch}"
|
|
||||||
--locked-mode
|
|
||||||
)
|
|
||||||
|
|
||||||
mkdir -p "${srcdir}/${_pkgsrc}"
|
|
||||||
rsync -a --delete \
|
|
||||||
--exclude='/.git' \
|
|
||||||
--exclude='/src' \
|
|
||||||
--exclude='/pkg' \
|
|
||||||
--exclude='/.nuget' \
|
|
||||||
"${startdir}/" "${srcdir}/${_pkgsrc}/"
|
|
||||||
|
|
||||||
cd "${srcdir}/${_pkgsrc}"
|
|
||||||
patch -Np1 -i "${srcdir}/${pkgbase}_xdg_settings.patch"
|
|
||||||
|
|
||||||
for dir in Core Cli Gui; do
|
|
||||||
dotnet restore "${dotnet_restore_options[@]}" "${_Name}.${dir}"
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
build() {
|
|
||||||
_source
|
|
||||||
local dotnet_publish_options=(
|
|
||||||
--configuration Release
|
|
||||||
--framework "net${_sdk}"
|
|
||||||
--no-restore
|
|
||||||
# --output build
|
|
||||||
--no-self-contained
|
|
||||||
--runtime "linux-${_msarch}"
|
|
||||||
-p:DebugType=None
|
|
||||||
-p:DebugSymbols=false
|
|
||||||
-p:Version="${pkgver%%.[A-Za-z]*}"
|
|
||||||
-p:PublishTrimmed=false
|
|
||||||
-p:PublishMacOSBundle=false
|
|
||||||
)
|
|
||||||
|
|
||||||
cd "${srcdir}"
|
|
||||||
gendesk -f -n \
|
|
||||||
--pkgname "${pkgbase}-gui" \
|
|
||||||
--pkgdesc "${pkgdesc}" \
|
|
||||||
--name "Discord Chat Exporter (GUI)" \
|
|
||||||
--exec "${pkgbase}-gui" \
|
|
||||||
--icon "${pkgbase}" \
|
|
||||||
--categories "Utility"
|
|
||||||
|
|
||||||
cd "${_pkgsrc}"
|
|
||||||
dotnet publish "${dotnet_publish_options[@]}" --output build-core "${_Name}.Core"
|
|
||||||
|
|
||||||
mkdir -p build-{cli,gui}
|
|
||||||
cp -aT build-core build-cli
|
|
||||||
cp -aT build-core build-gui
|
|
||||||
|
|
||||||
dotnet publish "${dotnet_publish_options[@]}" --output build-cli "${_Name}.Cli"
|
|
||||||
dotnet publish "${dotnet_publish_options[@]}" --output build-gui "${_Name}.Gui"
|
|
||||||
|
|
||||||
find build-core -type f | while read -r f; do
|
|
||||||
rel="${f#build-core/}"
|
|
||||||
rm -f "build-cli/$rel" "build-gui/$rel"
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
package_discord-chat-exporter-core() {
|
|
||||||
pkgdesc+=" - Core"
|
|
||||||
|
|
||||||
cd "${srcdir}/${_pkgsrc}"
|
|
||||||
install -vd "${pkgdir}/usr/lib/${pkgbase}"
|
|
||||||
cp -vaT --no-preserve=ownership "build-core" "${pkgdir}/usr/lib/${pkgbase}"
|
|
||||||
|
|
||||||
install -vDm644 "Readme.md" "${pkgdir}/usr/share/doc/${pkgbase}/README.md"
|
|
||||||
install -vDm644 "License.txt" "${pkgdir}/usr/share/licenses/${pkgbase}/LICENSE"
|
|
||||||
install -vDm644 "favicon.png" "${pkgdir}/usr/share/pixmaps/${pkgbase}.png"
|
|
||||||
}
|
|
||||||
|
|
||||||
package_discord-chat-exporter-cli() {
|
|
||||||
pkgdesc+=" - CLI"
|
|
||||||
depends+=(
|
|
||||||
"${pkgbase}-core>=${pkgver}-${pkgrel}"
|
|
||||||
)
|
|
||||||
|
|
||||||
cd "${srcdir}/${_pkgsrc}"
|
|
||||||
install -vd "${pkgdir}/usr/bin" "${pkgdir}/usr/lib/${pkgbase}"
|
|
||||||
cp -vaT --no-preserve=ownership "build-cli" "${pkgdir}/usr/lib/${pkgbase}"
|
|
||||||
ln -vsf "/usr/lib/${pkgbase}/${_Name}.Cli" "${pkgdir}/usr/bin/${pkgname}"
|
|
||||||
}
|
|
||||||
|
|
||||||
package_discord-chat-exporter-gui() {
|
|
||||||
pkgdesc+=" - GUI"
|
|
||||||
depends+=(
|
|
||||||
"${pkgbase}-core>=${pkgver}-${pkgrel}"
|
|
||||||
)
|
|
||||||
|
|
||||||
cd "${srcdir}"
|
|
||||||
install -vDm644 "${pkgname}.desktop" "${pkgdir}/usr/share/applications/${pkgname}.desktop"
|
|
||||||
|
|
||||||
cd "${_pkgsrc}"
|
|
||||||
install -vd "${pkgdir}/usr/bin" "${pkgdir}/usr/lib/${pkgbase}"
|
|
||||||
cp -vaT --no-preserve=ownership "build-gui" "${pkgdir}/usr/lib/${pkgbase}/"
|
|
||||||
ln -vsf "/usr/lib/${pkgbase}/${_Name}" "${pkgdir}/usr/bin/${pkgname}"
|
|
||||||
}
|
|
||||||
Loading…
Reference in a new issue