mirror of
				https://github.com/chylex/Discord-History-Tracker.git
				synced 2025-11-04 12:40:11 +01:00 
			
		
		
		
	Compare commits
	
		
			5 Commits
		
	
	
		
			38f79dee7d
			...
			b2276600c7
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						
						
							
						
						b2276600c7
	
				 | 
					
					
						|||
| 
						
						
							
						
						40269f591b
	
				 | 
					
					
						|||
| 
						
						
							
						
						dea3b272c0
	
				 | 
					
					
						|||
| 
						
						
							
						
						e66206b4a8
	
				 | 
					
					
						|||
| 
						
						
							
						
						780d5ae421
	
				 | 
					
					
						
@@ -3,12 +3,12 @@
 | 
				
			|||||||
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
 | 
					        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
 | 
				
			||||||
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
 | 
					        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
 | 
				
			||||||
        xmlns:main="clr-namespace:DHT.Desktop.Main"
 | 
					        xmlns:main="clr-namespace:DHT.Desktop.Main"
 | 
				
			||||||
        mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="360"
 | 
					        mc:Ignorable="d" d:DesignWidth="510" d:DesignHeight="375"
 | 
				
			||||||
        x:Class="DHT.Desktop.Main.AboutWindow"
 | 
					        x:Class="DHT.Desktop.Main.AboutWindow"
 | 
				
			||||||
        x:DataType="main:AboutWindowModel"
 | 
					        x:DataType="main:AboutWindowModel"
 | 
				
			||||||
        Title="About Discord History Tracker"
 | 
					        Title="About Discord History Tracker"
 | 
				
			||||||
        Icon="avares://DiscordHistoryTracker/Resources/icon.ico"
 | 
					        Icon="avares://DiscordHistoryTracker/Resources/icon.ico"
 | 
				
			||||||
        Width="480" Height="360" CanResize="False"
 | 
					        Width="510" Height="375" CanResize="False"
 | 
				
			||||||
        WindowStartupLocation="CenterOwner">
 | 
					        WindowStartupLocation="CenterOwner">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <Design.DataContext>
 | 
					    <Design.DataContext>
 | 
				
			||||||
@@ -16,10 +16,6 @@
 | 
				
			|||||||
    </Design.DataContext>
 | 
					    </Design.DataContext>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <Window.Styles>
 | 
					    <Window.Styles>
 | 
				
			||||||
        <Style Selector="StackPanel">
 | 
					 | 
				
			||||||
            <Setter Property="Orientation" Value="Horizontal" />
 | 
					 | 
				
			||||||
            <Setter Property="Spacing" Value="5" />
 | 
					 | 
				
			||||||
        </Style>
 | 
					 | 
				
			||||||
        <Style Selector="TextBlock">
 | 
					        <Style Selector="TextBlock">
 | 
				
			||||||
            <Setter Property="TextWrapping" Value="Wrap" />
 | 
					            <Setter Property="TextWrapping" Value="Wrap" />
 | 
				
			||||||
            <Setter Property="VerticalAlignment" Value="Center" />
 | 
					            <Setter Property="VerticalAlignment" Value="Center" />
 | 
				
			||||||
@@ -33,44 +29,45 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    <StackPanel Orientation="Vertical" Margin="20" Spacing="12">
 | 
					    <StackPanel Orientation="Vertical" Margin="20" Spacing="12">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <TextBlock VerticalAlignment="Center">
 | 
					        <StackPanel Orientation="Vertical" Spacing="3">
 | 
				
			||||||
            Discord History Tracker was created by chylex and released under the MIT license.
 | 
					            <TextBlock TextWrapping="Wrap">Discord History Tracker was created by chylex.</TextBlock>
 | 
				
			||||||
        </TextBlock>
 | 
					            <TextBlock>It is available under the MIT license.</TextBlock>
 | 
				
			||||||
 | 
					        </StackPanel>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <StackPanel>
 | 
					        <StackPanel Orientation="Horizontal" Spacing="8">
 | 
				
			||||||
            <Button Command="{Binding ShowOfficialWebsite}">Official Website</Button>
 | 
					            <Button Command="{Binding ShowOfficialWebsite}">Official Website</Button>
 | 
				
			||||||
            <Button Command="{Binding ShowIssueTracker}">Issue Tracker</Button>
 | 
					            <Button Command="{Binding ShowIssueTracker}">Issue Tracker</Button>
 | 
				
			||||||
            <Button Command="{Binding ShowSourceCode}">Source Code</Button>
 | 
					            <Button Command="{Binding ShowSourceCode}">Source Code</Button>
 | 
				
			||||||
        </StackPanel>
 | 
					        </StackPanel>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <Grid RowDefinitions="Auto,5,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto" ColumnDefinitions="175,125,*" Margin="0 10 0 0">
 | 
					        <Grid RowDefinitions="Auto,5,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto" ColumnDefinitions="*,115,95" Margin="0 10 0 0">
 | 
				
			||||||
            <TextBlock Grid.Row="0" Grid.Column="0" FontWeight="Bold">Third-Party Software</TextBlock>
 | 
					            <TextBlock Grid.Row="0" Grid.Column="0" FontWeight="Bold">Third-Party Software</TextBlock>
 | 
				
			||||||
            <TextBlock Grid.Row="0" Grid.Column="1" FontWeight="Bold">License</TextBlock>
 | 
					            <TextBlock Grid.Row="0" Grid.Column="1" FontWeight="Bold">License</TextBlock>
 | 
				
			||||||
            <TextBlock Grid.Row="0" Grid.Column="2" FontWeight="Bold">Link</TextBlock>
 | 
					            <TextBlock Grid.Row="0" Grid.Column="2" FontWeight="Bold">Link</TextBlock>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <TextBlock Grid.Row="2" Grid.Column="0">.NET 8</TextBlock>
 | 
					            <TextBlock Grid.Row="2" Grid.Column="0">.NET</TextBlock>
 | 
				
			||||||
            <TextBlock Grid.Row="2" Grid.Column="1">MIT</TextBlock>
 | 
					            <TextBlock Grid.Row="2" Grid.Column="1">MIT</TextBlock>
 | 
				
			||||||
            <Button Grid.Row="2" Grid.Column="2" Command="{Binding ShowLibraryNetCore}">GitHub</Button>
 | 
					            <Button Grid.Row="2" Grid.Column="2" Command="{Binding ShowLibraryNetCore}">GitHub</Button>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <TextBlock Grid.Row="3" Grid.Column="0">Avalonia</TextBlock>
 | 
					            <TextBlock Grid.Row="3" Grid.Column="0">Avalonia</TextBlock>
 | 
				
			||||||
            <TextBlock Grid.Row="3" Grid.Column="1">MIT</TextBlock>
 | 
					            <TextBlock Grid.Row="3" Grid.Column="1">MIT</TextBlock>
 | 
				
			||||||
            <Button Grid.Row="3" Grid.Column="2" Command="{Binding ShowLibraryAvalonia}">NuGet</Button>
 | 
					            <Button Grid.Row="3" Grid.Column="2" Command="{Binding ShowLibraryAvalonia}">GitHub</Button>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <TextBlock Grid.Row="4" Grid.Column="0">MVVM Toolkit</TextBlock>
 | 
					            <TextBlock Grid.Row="4" Grid.Column="0">Rx.NET</TextBlock>
 | 
				
			||||||
            <TextBlock Grid.Row="4" Grid.Column="1">MIT</TextBlock>
 | 
					            <TextBlock Grid.Row="4" Grid.Column="1">MIT</TextBlock>
 | 
				
			||||||
            <Button Grid.Row="4" Grid.Column="2" Command="{Binding ShowLibraryCommunityToolkit}">GitHub</Button>
 | 
					            <Button Grid.Row="4" Grid.Column="2" Command="{Binding ShowLibraryRxNet}">GitHub</Button>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <TextBlock Grid.Row="5" Grid.Column="0">SQLite</TextBlock>
 | 
					            <TextBlock Grid.Row="5" Grid.Column="0">SQLite</TextBlock>
 | 
				
			||||||
            <TextBlock Grid.Row="5" Grid.Column="1">Public Domain</TextBlock>
 | 
					            <TextBlock Grid.Row="5" Grid.Column="1">Public Domain</TextBlock>
 | 
				
			||||||
            <Button Grid.Row="5" Grid.Column="2" Command="{Binding ShowLibrarySqlite}">Official Website</Button>
 | 
					            <Button Grid.Row="5" Grid.Column="2" Command="{Binding ShowLibrarySqlite}">Website</Button>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <TextBlock Grid.Row="6" Grid.Column="0">Microsoft.Data.Sqlite</TextBlock>
 | 
					            <TextBlock Grid.Row="6" Grid.Column="0">Microsoft.Data.Sqlite</TextBlock>
 | 
				
			||||||
            <TextBlock Grid.Row="6" Grid.Column="1">Apache-2.0</TextBlock>
 | 
					            <TextBlock Grid.Row="6" Grid.Column="1">Apache-2.0</TextBlock>
 | 
				
			||||||
            <Button Grid.Row="6" Grid.Column="2" Command="{Binding ShowLibrarySqliteAdoNet}">NuGet</Button>
 | 
					            <Button Grid.Row="6" Grid.Column="2" Command="{Binding ShowLibrarySqliteAdoNet}">NuGet</Button>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <TextBlock Grid.Row="7" Grid.Column="0">Rx.NET</TextBlock>
 | 
					            <TextBlock Grid.Row="7" Grid.Column="0">PropertyChanged.SourceGenerator</TextBlock>
 | 
				
			||||||
            <TextBlock Grid.Row="7" Grid.Column="1">MIT</TextBlock>
 | 
					            <TextBlock Grid.Row="7" Grid.Column="1">MIT</TextBlock>
 | 
				
			||||||
            <Button Grid.Row="7" Grid.Column="2" Command="{Binding ShowLibraryRxNet}">GitHub</Button>
 | 
					            <Button Grid.Row="7" Grid.Column="2" Command="{Binding ShowLibraryPropertyChangedSourceGenerator}">GitHub</Button>
 | 
				
			||||||
        </Grid>
 | 
					        </Grid>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    </StackPanel>
 | 
					    </StackPanel>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -20,11 +20,11 @@ sealed class AboutWindowModel {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	public void ShowLibraryAvalonia() {
 | 
						public void ShowLibraryAvalonia() {
 | 
				
			||||||
		SystemUtils.OpenUrl("https://www.nuget.org/packages/Avalonia");
 | 
							SystemUtils.OpenUrl("https://github.com/AvaloniaUI/Avalonia");
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	public void ShowLibraryCommunityToolkit() {
 | 
						public void ShowLibraryPropertyChangedSourceGenerator() {
 | 
				
			||||||
		SystemUtils.OpenUrl("https://github.com/CommunityToolkit/dotnet");
 | 
							SystemUtils.OpenUrl("https://github.com/canton7/PropertyChanged.SourceGenerator");
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	public void ShowLibrarySqlite() {
 | 
						public void ShowLibrarySqlite() {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -35,7 +35,7 @@
 | 
				
			|||||||
            <Button Command="{Binding OnClickToggleDownload}" Content="{Binding ToggleDownloadButtonText}" IsEnabled="{Binding IsToggleDownloadButtonEnabled}" />
 | 
					            <Button Command="{Binding OnClickToggleDownload}" Content="{Binding ToggleDownloadButtonText}" IsEnabled="{Binding IsToggleDownloadButtonEnabled}" />
 | 
				
			||||||
            <Button Command="{Binding OnClickRetryFailed}" IsEnabled="{Binding IsRetryFailedOnDownloadsButtonEnabled}">Retry Failed</Button>
 | 
					            <Button Command="{Binding OnClickRetryFailed}" IsEnabled="{Binding IsRetryFailedOnDownloadsButtonEnabled}">Retry Failed</Button>
 | 
				
			||||||
            <Button Command="{Binding OnClickDeleteOrphaned}">Delete Orphaned</Button>
 | 
					            <Button Command="{Binding OnClickDeleteOrphaned}">Delete Orphaned</Button>
 | 
				
			||||||
            <Button Command="{Binding OnClickExportAll}" IsEnabled="{Binding HasSuccessfulDownloads}">Export All</Button>
 | 
					            <Button Command="{Binding OnClickExportAll}" IsEnabled="{Binding HasSuccessfulDownloads}">Export All...</Button>
 | 
				
			||||||
        </WrapPanel>
 | 
					        </WrapPanel>
 | 
				
			||||||
        <StackPanel Orientation="Vertical" Spacing="20" Margin="0 10 0 0">
 | 
					        <StackPanel Orientation="Vertical" Spacing="20" Margin="0 10 0 0">
 | 
				
			||||||
            <controls:DownloadItemFilterPanel DataContext="{Binding FilterModel}" IsEnabled="{Binding !$parent[UserControl].((pages:DownloadsPageModel)DataContext).IsDownloading}" />
 | 
					            <controls:DownloadItemFilterPanel DataContext="{Binding FilterModel}" IsEnabled="{Binding !$parent[UserControl].((pages:DownloadsPageModel)DataContext).IsDownloading}" />
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,12 +21,13 @@
 | 
				
			|||||||
  <PropertyGroup>
 | 
					  <PropertyGroup>
 | 
				
			||||||
    <SuppressTrimAnalysisWarnings>false</SuppressTrimAnalysisWarnings>
 | 
					    <SuppressTrimAnalysisWarnings>false</SuppressTrimAnalysisWarnings>
 | 
				
			||||||
    <PublishTrimmed>true</PublishTrimmed>
 | 
					    <PublishTrimmed>true</PublishTrimmed>
 | 
				
			||||||
    <TrimMode>partial</TrimMode>
 | 
					    <TrimMode>full</TrimMode>
 | 
				
			||||||
    <EnableUnsafeBinaryFormatterSerialization>false</EnableUnsafeBinaryFormatterSerialization>
 | 
					    <EnableUnsafeBinaryFormatterSerialization>false</EnableUnsafeBinaryFormatterSerialization>
 | 
				
			||||||
    <EnableUnsafeUTF7Encoding>false</EnableUnsafeUTF7Encoding>
 | 
					    <EnableUnsafeUTF7Encoding>false</EnableUnsafeUTF7Encoding>
 | 
				
			||||||
    <EventSourceSupport>false</EventSourceSupport>
 | 
					    <EventSourceSupport>false</EventSourceSupport>
 | 
				
			||||||
    <HttpActivityPropagationSupport>false</HttpActivityPropagationSupport>
 | 
					    <HttpActivityPropagationSupport>false</HttpActivityPropagationSupport>
 | 
				
			||||||
    <JsonSerializerIsReflectionEnabledByDefault>false</JsonSerializerIsReflectionEnabledByDefault>
 | 
					    <JsonSerializerIsReflectionEnabledByDefault>false</JsonSerializerIsReflectionEnabledByDefault>
 | 
				
			||||||
 | 
					    <XmlResolverIsNetworkingEnabledByDefault>false</XmlResolverIsNetworkingEnabledByDefault>
 | 
				
			||||||
  </PropertyGroup>
 | 
					  </PropertyGroup>
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
  <PropertyGroup>
 | 
					  <PropertyGroup>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,16 +1,20 @@
 | 
				
			|||||||
using System;
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Buffers.Text;
 | 
				
			||||||
 | 
					using System.Diagnostics;
 | 
				
			||||||
using System.Text.Json;
 | 
					using System.Text.Json;
 | 
				
			||||||
using System.Text.Json.Serialization;
 | 
					using System.Text.Json.Serialization;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace DHT.Server.Database.Export;
 | 
					namespace DHT.Server.Database.Export;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
sealed class SnowflakeJsonSerializer : JsonConverter<Snowflake> {
 | 
					sealed class SnowflakeJsonSerializer : JsonConverter<Snowflake> {
 | 
				
			||||||
 | 
						private const int MaxUlongStringLength = 20;
 | 
				
			||||||
 | 
						
 | 
				
			||||||
	public override Snowflake Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) {
 | 
						public override Snowflake Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) {
 | 
				
			||||||
		return new Snowflake(ulong.Parse(reader.GetString()!));
 | 
							return new Snowflake(ulong.Parse(reader.GetString()!));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	public override void Write(Utf8JsonWriter writer, Snowflake value, JsonSerializerOptions options) {
 | 
						public override void Write(Utf8JsonWriter writer, Snowflake value, JsonSerializerOptions options) {
 | 
				
			||||||
		writer.WriteStringValue(value.Id.ToString());
 | 
							writer.WriteStringValue(Format(value, stackalloc byte[MaxUlongStringLength]));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	public override Snowflake ReadAsPropertyName(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) {
 | 
						public override Snowflake ReadAsPropertyName(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) {
 | 
				
			||||||
@@ -18,6 +22,14 @@ sealed class SnowflakeJsonSerializer : JsonConverter<Snowflake> {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	public override void WriteAsPropertyName(Utf8JsonWriter writer, Snowflake value, JsonSerializerOptions options) {
 | 
						public override void WriteAsPropertyName(Utf8JsonWriter writer, Snowflake value, JsonSerializerOptions options) {
 | 
				
			||||||
		writer.WritePropertyName(value.Id.ToString());
 | 
							writer.WritePropertyName(Format(value, stackalloc byte[MaxUlongStringLength]));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						private static ReadOnlySpan<byte> Format(Snowflake value, Span<byte> destination) {
 | 
				
			||||||
 | 
							if (!Utf8Formatter.TryFormat(value.Id, destination, out int bytesWritten)) {
 | 
				
			||||||
 | 
								Debug.Fail("Failed to format Snowflake value.");
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							return destination[..bytesWritten];
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -30,7 +30,7 @@ static class ViewerJson {
 | 
				
			|||||||
		public required string Name { get; init; }
 | 
							public required string Name { get; init; }
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
		[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
 | 
							[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
 | 
				
			||||||
		public string? Parent { get; init; }
 | 
							public Snowflake? Parent { get; init; }
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
		[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
 | 
							[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
 | 
				
			||||||
		public int? Position { get; init; }
 | 
							public int? Position { get; init; }
 | 
				
			||||||
@@ -55,7 +55,7 @@ static class ViewerJson {
 | 
				
			|||||||
		public long? Te { get; init; }
 | 
							public long? Te { get; init; }
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
		[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
 | 
							[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
 | 
				
			||||||
		public string? R { get; init; }
 | 
							public Snowflake? R { get; init; }
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
		[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
 | 
							[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
 | 
				
			||||||
		public JsonMessageAttachment[]? A { get; init; }
 | 
							public JsonMessageAttachment[]? A { get; init; }
 | 
				
			||||||
@@ -80,7 +80,7 @@ static class ViewerJson {
 | 
				
			|||||||
	
 | 
						
 | 
				
			||||||
	public sealed class JsonMessageReaction {
 | 
						public sealed class JsonMessageReaction {
 | 
				
			||||||
		[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
 | 
							[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
 | 
				
			||||||
		public string? Id { get; init; }
 | 
							public Snowflake? Id { get; init; }
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
		[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
 | 
							[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
 | 
				
			||||||
		public string? N { get; init; }
 | 
							public string? N { get; init; }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,13 +2,15 @@ using System;
 | 
				
			|||||||
using System.Collections.Generic;
 | 
					using System.Collections.Generic;
 | 
				
			||||||
using System.IO;
 | 
					using System.IO;
 | 
				
			||||||
using System.Linq;
 | 
					using System.Linq;
 | 
				
			||||||
using System.Runtime.CompilerServices;
 | 
					 | 
				
			||||||
using System.Text.Json;
 | 
					using System.Text.Json;
 | 
				
			||||||
using System.Threading;
 | 
					using System.Threading;
 | 
				
			||||||
 | 
					using System.Threading.Channels;
 | 
				
			||||||
using System.Threading.Tasks;
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
using DHT.Server.Data;
 | 
					using DHT.Server.Data;
 | 
				
			||||||
using DHT.Server.Data.Filters;
 | 
					using DHT.Server.Data.Filters;
 | 
				
			||||||
using DHT.Utils.Logging;
 | 
					using DHT.Utils.Logging;
 | 
				
			||||||
 | 
					using Channel = System.Threading.Channels.Channel;
 | 
				
			||||||
 | 
					using DiscordChannel = DHT.Server.Data.Channel;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace DHT.Server.Database.Export;
 | 
					namespace DHT.Server.Database.Export;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -18,12 +20,12 @@ static class ViewerJsonExport {
 | 
				
			|||||||
	public static async Task GetMetadata(Stream stream, IDatabaseFile db, MessageFilter? filter = null, CancellationToken cancellationToken = default) {
 | 
						public static async Task GetMetadata(Stream stream, IDatabaseFile db, MessageFilter? filter = null, CancellationToken cancellationToken = default) {
 | 
				
			||||||
		Perf perf = Log.Start();
 | 
							Perf perf = Log.Start();
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
		var includedChannels = new List<Channel>();
 | 
							var includedChannels = new List<DiscordChannel>();
 | 
				
			||||||
		var includedServerIds = new HashSet<ulong>();
 | 
							var includedServerIds = new HashSet<ulong>();
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
		HashSet<ulong>? channelIdFilter = filter?.ChannelIds;
 | 
							HashSet<ulong>? channelIdFilter = filter?.ChannelIds;
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
		await foreach (Channel channel in db.Channels.Get(cancellationToken)) {
 | 
							await foreach (DiscordChannel channel in db.Channels.Get(cancellationToken)) {
 | 
				
			||||||
			if (channelIdFilter == null || channelIdFilter.Contains(channel.Id)) {
 | 
								if (channelIdFilter == null || channelIdFilter.Contains(channel.Id)) {
 | 
				
			||||||
				includedChannels.Add(channel);
 | 
									includedChannels.Add(channel);
 | 
				
			||||||
				includedServerIds.Add(channel.Server);
 | 
									includedServerIds.Add(channel.Server);
 | 
				
			||||||
@@ -53,11 +55,30 @@ static class ViewerJsonExport {
 | 
				
			|||||||
		
 | 
							
 | 
				
			||||||
		ReadOnlyMemory<byte> newLine = "\n"u8.ToArray();
 | 
							ReadOnlyMemory<byte> newLine = "\n"u8.ToArray();
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
		await foreach (ViewerJson.JsonMessage message in GenerateMessageList(db, filter, cancellationToken)) {
 | 
							Channel<Message> channel = Channel.CreateBounded<Message>(new BoundedChannelOptions(32) {
 | 
				
			||||||
			await JsonSerializer.SerializeAsync(stream, message, ViewerJsonMessageContext.Default.JsonMessage, cancellationToken);
 | 
								SingleWriter = true,
 | 
				
			||||||
 | 
								SingleReader = true,
 | 
				
			||||||
 | 
								AllowSynchronousContinuations = true,
 | 
				
			||||||
 | 
								FullMode = BoundedChannelFullMode.Wait,
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							Task writerTask = Task.Run(async () => {
 | 
				
			||||||
 | 
								try {
 | 
				
			||||||
 | 
									await foreach (Message message in db.Messages.Get(filter, cancellationToken)) {
 | 
				
			||||||
 | 
										await channel.Writer.WriteAsync(message, cancellationToken);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								} finally {
 | 
				
			||||||
 | 
									channel.Writer.Complete();
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}, cancellationToken);
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							await foreach (Message message in channel.Reader.ReadAllAsync(cancellationToken)) {
 | 
				
			||||||
 | 
								await JsonSerializer.SerializeAsync(stream, ToJsonMessage(message), ViewerJsonMessageContext.Default.JsonMessage, cancellationToken);
 | 
				
			||||||
			await stream.WriteAsync(newLine, cancellationToken);
 | 
								await stream.WriteAsync(newLine, cancellationToken);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
 | 
							await writerTask;
 | 
				
			||||||
 | 
							
 | 
				
			||||||
		perf.Step("Generate and serialize messages to JSON");
 | 
							perf.Step("Generate and serialize messages to JSON");
 | 
				
			||||||
		perf.End();
 | 
							perf.End();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -93,14 +114,14 @@ static class ViewerJsonExport {
 | 
				
			|||||||
		return servers;
 | 
							return servers;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	private static Dictionary<Snowflake, ViewerJson.JsonChannel> GenerateChannelList(List<Channel> includedChannels) {
 | 
						private static Dictionary<Snowflake, ViewerJson.JsonChannel> GenerateChannelList(List<DiscordChannel> includedChannels) {
 | 
				
			||||||
		var channels = new Dictionary<Snowflake, ViewerJson.JsonChannel>();
 | 
							var channels = new Dictionary<Snowflake, ViewerJson.JsonChannel>();
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
		foreach (Channel channel in includedChannels) {
 | 
							foreach (DiscordChannel channel in includedChannels) {
 | 
				
			||||||
			channels[channel.Id] = new ViewerJson.JsonChannel {
 | 
								channels[channel.Id] = new ViewerJson.JsonChannel {
 | 
				
			||||||
				Server = channel.Server,
 | 
									Server = channel.Server,
 | 
				
			||||||
				Name = channel.Name,
 | 
									Name = channel.Name,
 | 
				
			||||||
				Parent = channel.ParentId?.ToString(),
 | 
									Parent = channel.ParentId,
 | 
				
			||||||
				Position = channel.Position,
 | 
									Position = channel.Position,
 | 
				
			||||||
				Topic = channel.Topic,
 | 
									Topic = channel.Topic,
 | 
				
			||||||
				Nsfw = channel.Nsfw,
 | 
									Nsfw = channel.Nsfw,
 | 
				
			||||||
@@ -110,16 +131,15 @@ static class ViewerJsonExport {
 | 
				
			|||||||
		return channels;
 | 
							return channels;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	private static async IAsyncEnumerable<ViewerJson.JsonMessage> GenerateMessageList(IDatabaseFile db, MessageFilter? filter, [EnumeratorCancellation] CancellationToken cancellationToken) {
 | 
						private static ViewerJson.JsonMessage ToJsonMessage(Message message) {
 | 
				
			||||||
		await foreach (Message message in db.Messages.Get(filter, cancellationToken)) {
 | 
							return new ViewerJson.JsonMessage {
 | 
				
			||||||
			yield return new ViewerJson.JsonMessage {
 | 
					 | 
				
			||||||
			Id = message.Id,
 | 
								Id = message.Id,
 | 
				
			||||||
			C = message.Channel,
 | 
								C = message.Channel,
 | 
				
			||||||
			U = message.Sender,
 | 
								U = message.Sender,
 | 
				
			||||||
			T = message.Timestamp,
 | 
								T = message.Timestamp,
 | 
				
			||||||
			M = string.IsNullOrEmpty(message.Text) ? null : message.Text,
 | 
								M = string.IsNullOrEmpty(message.Text) ? null : message.Text,
 | 
				
			||||||
			Te = message.EditTimestamp,
 | 
								Te = message.EditTimestamp,
 | 
				
			||||||
				R = message.RepliedToId?.ToString(),
 | 
								R = message.RepliedToId,
 | 
				
			||||||
			
 | 
								
 | 
				
			||||||
			A = message.Attachments.IsEmpty ? null : message.Attachments.Select(static attachment => {
 | 
								A = message.Attachments.IsEmpty ? null : message.Attachments.Select(static attachment => {
 | 
				
			||||||
				var a = new ViewerJson.JsonMessageAttachment {
 | 
									var a = new ViewerJson.JsonMessageAttachment {
 | 
				
			||||||
@@ -138,7 +158,7 @@ static class ViewerJsonExport {
 | 
				
			|||||||
			E = message.Embeds.IsEmpty ? null : message.Embeds.Select(static embed => embed.Json).ToArray(),
 | 
								E = message.Embeds.IsEmpty ? null : message.Embeds.Select(static embed => embed.Json).ToArray(),
 | 
				
			||||||
			
 | 
								
 | 
				
			||||||
			Re = message.Reactions.IsEmpty ? null : message.Reactions.Select(static reaction => new ViewerJson.JsonMessageReaction {
 | 
								Re = message.Reactions.IsEmpty ? null : message.Reactions.Select(static reaction => new ViewerJson.JsonMessageReaction {
 | 
				
			||||||
					Id = reaction.EmojiId?.ToString(),
 | 
									Id = reaction.EmojiId,
 | 
				
			||||||
				N = reaction.EmojiName,
 | 
									N = reaction.EmojiName,
 | 
				
			||||||
				A = reaction.EmojiFlags.HasFlag(EmojiFlags.Animated),
 | 
									A = reaction.EmojiFlags.HasFlag(EmojiFlags.Animated),
 | 
				
			||||||
				C = reaction.Count,
 | 
									C = reaction.Count,
 | 
				
			||||||
@@ -146,4 +166,3 @@ static class ViewerJsonExport {
 | 
				
			|||||||
		};
 | 
							};
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,5 +8,5 @@ using DHT.Utils;
 | 
				
			|||||||
namespace DHT.Utils;
 | 
					namespace DHT.Utils;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static class Version {
 | 
					static class Version {
 | 
				
			||||||
	public const string Tag = "45.0.0.0";
 | 
						public const string Tag = "46.0.0.0";
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user