mirror of
				https://github.com/chylex/Discord-History-Tracker.git
				synced 2025-11-03 18:40:12 +01:00 
			
		
		
		
	Compare commits
	
		
			1 Commits
		
	
	
		
			c3d4fa5532
			...
			app-raw-me
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						
						
							
						
						dc5cd83da9
	
				 | 
					
					
						
@@ -304,6 +304,7 @@ const STATE = (function() {
 | 
				
			|||||||
					});
 | 
										});
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				
 | 
									
 | 
				
			||||||
 | 
									obj["raw"] = JSON.stringify(msg);
 | 
				
			||||||
				return obj;
 | 
									return obj;
 | 
				
			||||||
			}));
 | 
								}));
 | 
				
			||||||
			
 | 
								
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,5 +12,6 @@ namespace DHT.Server.Data {
 | 
				
			|||||||
		public ImmutableArray<Attachment> Attachments { get; internal init; }
 | 
							public ImmutableArray<Attachment> Attachments { get; internal init; }
 | 
				
			||||||
		public ImmutableArray<Embed> Embeds { get; internal init; }
 | 
							public ImmutableArray<Embed> Embeds { get; internal init; }
 | 
				
			||||||
		public ImmutableArray<Reaction> Reactions { get; internal init; }
 | 
							public ImmutableArray<Reaction> Reactions { get; internal init; }
 | 
				
			||||||
 | 
							public string? RawJson { get; internal init; }
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,7 +5,7 @@ using Microsoft.Data.Sqlite;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace DHT.Server.Database.Sqlite {
 | 
					namespace DHT.Server.Database.Sqlite {
 | 
				
			||||||
	sealed class Schema {
 | 
						sealed class Schema {
 | 
				
			||||||
		internal const int Version = 2;
 | 
							internal const int Version = 3;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		private readonly SqliteConnection conn;
 | 
							private readonly SqliteConnection conn;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -97,6 +97,8 @@ namespace DHT.Server.Database.Sqlite {
 | 
				
			|||||||
					emoji_flags INTEGER NOT NULL,
 | 
										emoji_flags INTEGER NOT NULL,
 | 
				
			||||||
					count INTEGER NOT NULL)");
 | 
										count INTEGER NOT NULL)");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								CreateMessagesRawTable();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			Execute("CREATE INDEX attachments_message_ix ON attachments(message_id)");
 | 
								Execute("CREATE INDEX attachments_message_ix ON attachments(message_id)");
 | 
				
			||||||
			Execute("CREATE INDEX embeds_message_ix ON embeds(message_id)");
 | 
								Execute("CREATE INDEX embeds_message_ix ON embeds(message_id)");
 | 
				
			||||||
			Execute("CREATE INDEX reactions_message_ix ON reactions(message_id)");
 | 
								Execute("CREATE INDEX reactions_message_ix ON reactions(message_id)");
 | 
				
			||||||
@@ -110,6 +112,16 @@ namespace DHT.Server.Database.Sqlite {
 | 
				
			|||||||
			if (dbVersion <= 1) {
 | 
								if (dbVersion <= 1) {
 | 
				
			||||||
				Execute("ALTER TABLE channels ADD parent_id INTEGER");
 | 
									Execute("ALTER TABLE channels ADD parent_id INTEGER");
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (dbVersion <= 2) {
 | 
				
			||||||
 | 
									CreateMessagesRawTable();
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							private void CreateMessagesRawTable() {
 | 
				
			||||||
 | 
								Execute(@"CREATE TABLE messages_raw (
 | 
				
			||||||
 | 
								        message_id INTEGER PRIMARY KEY NOT NULL,
 | 
				
			||||||
 | 
								        json BLOB)");
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,6 +6,7 @@ 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.Collections;
 | 
					using DHT.Utils.Collections;
 | 
				
			||||||
 | 
					using DHT.Utils.Compression;
 | 
				
			||||||
using Microsoft.Data.Sqlite;
 | 
					using Microsoft.Data.Sqlite;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace DHT.Server.Database.Sqlite {
 | 
					namespace DHT.Server.Database.Sqlite {
 | 
				
			||||||
@@ -157,6 +158,10 @@ namespace DHT.Server.Database.Sqlite {
 | 
				
			|||||||
				"message_id", "sender_id", "channel_id", "text", "timestamp", "edit_timestamp", "replied_to_id"
 | 
									"message_id", "sender_id", "channel_id", "text", "timestamp", "edit_timestamp", "replied_to_id"
 | 
				
			||||||
			});
 | 
								});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								using var messageRawCmd = conn.Upsert("messages_raw", new[] {
 | 
				
			||||||
 | 
									"message_id", "json"
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			using var deleteAttachmentsCmd = conn.Command("DELETE FROM attachments WHERE message_id = :message_id");
 | 
								using var deleteAttachmentsCmd = conn.Command("DELETE FROM attachments WHERE message_id = :message_id");
 | 
				
			||||||
			using var attachmentCmd = conn.Insert("attachments", new[] {
 | 
								using var attachmentCmd = conn.Insert("attachments", new[] {
 | 
				
			||||||
				"message_id", "attachment_id", "name", "type", "url", "size"
 | 
									"message_id", "attachment_id", "name", "type", "url", "size"
 | 
				
			||||||
@@ -181,6 +186,10 @@ namespace DHT.Server.Database.Sqlite {
 | 
				
			|||||||
			messageParams.Add(":edit_timestamp", SqliteType.Integer);
 | 
								messageParams.Add(":edit_timestamp", SqliteType.Integer);
 | 
				
			||||||
			messageParams.Add(":replied_to_id", SqliteType.Integer);
 | 
								messageParams.Add(":replied_to_id", SqliteType.Integer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								var messageRawParams = messageRawCmd.Parameters;
 | 
				
			||||||
 | 
								messageRawParams.Add(":message_id", SqliteType.Integer);
 | 
				
			||||||
 | 
								messageRawParams.Add(":json", SqliteType.Blob);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			var deleteAttachmentsParams = deleteAttachmentsCmd.Parameters;
 | 
								var deleteAttachmentsParams = deleteAttachmentsCmd.Parameters;
 | 
				
			||||||
			deleteAttachmentsParams.Add(":message_id", SqliteType.Integer);
 | 
								deleteAttachmentsParams.Add(":message_id", SqliteType.Integer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -209,6 +218,8 @@ namespace DHT.Server.Database.Sqlite {
 | 
				
			|||||||
			reactionParams.Add(":emoji_flags", SqliteType.Integer);
 | 
								reactionParams.Add(":emoji_flags", SqliteType.Integer);
 | 
				
			||||||
			reactionParams.Add(":count", SqliteType.Integer);
 | 
								reactionParams.Add(":count", SqliteType.Integer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								var brotli = new Brotli(4096);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			foreach (var message in messages) {
 | 
								foreach (var message in messages) {
 | 
				
			||||||
				object messageId = message.Id;
 | 
									object messageId = message.Id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -221,6 +232,12 @@ namespace DHT.Server.Database.Sqlite {
 | 
				
			|||||||
				messageParams.Set(":replied_to_id", message.RepliedToId);
 | 
									messageParams.Set(":replied_to_id", message.RepliedToId);
 | 
				
			||||||
				messageCmd.ExecuteNonQuery();
 | 
									messageCmd.ExecuteNonQuery();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if (message.RawJson is {} json) {
 | 
				
			||||||
 | 
										messageRawParams.Set(":message_id", messageId);
 | 
				
			||||||
 | 
										messageRawParams.Set(":json", brotli.Compress(Encoding.UTF8.GetBytes(json)));
 | 
				
			||||||
 | 
										messageRawCmd.ExecuteNonQuery();
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				deleteAttachmentsParams.Set(":message_id", messageId);
 | 
									deleteAttachmentsParams.Set(":message_id", messageId);
 | 
				
			||||||
				deleteAttachmentsCmd.ExecuteNonQuery();
 | 
									deleteAttachmentsCmd.ExecuteNonQuery();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -50,7 +50,8 @@ namespace DHT.Server.Endpoints {
 | 
				
			|||||||
			RepliedToId = json.HasKey("repliedToId") ? json.RequireSnowflake("repliedToId", path) : null,
 | 
								RepliedToId = json.HasKey("repliedToId") ? json.RequireSnowflake("repliedToId", path) : null,
 | 
				
			||||||
			Attachments = json.HasKey("attachments") ? ReadAttachments(json.RequireArray("attachments", path + ".attachments"), path + ".attachments[]").ToImmutableArray() : ImmutableArray<Attachment>.Empty,
 | 
								Attachments = json.HasKey("attachments") ? ReadAttachments(json.RequireArray("attachments", path + ".attachments"), path + ".attachments[]").ToImmutableArray() : ImmutableArray<Attachment>.Empty,
 | 
				
			||||||
			Embeds = json.HasKey("embeds") ? ReadEmbeds(json.RequireArray("embeds", path + ".embeds"), path + ".embeds[]").ToImmutableArray() : ImmutableArray<Embed>.Empty,
 | 
								Embeds = json.HasKey("embeds") ? ReadEmbeds(json.RequireArray("embeds", path + ".embeds"), path + ".embeds[]").ToImmutableArray() : ImmutableArray<Embed>.Empty,
 | 
				
			||||||
			Reactions = json.HasKey("reactions") ? ReadReactions(json.RequireArray("reactions", path + ".reactions"), path + ".reactions[]").ToImmutableArray() : ImmutableArray<Reaction>.Empty
 | 
								Reactions = json.HasKey("reactions") ? ReadReactions(json.RequireArray("reactions", path + ".reactions"), path + ".reactions[]").ToImmutableArray() : ImmutableArray<Reaction>.Empty,
 | 
				
			||||||
 | 
								RawJson = json.HasKey("raw") ? json.RequireString("raw", path) : null
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		private static IEnumerable<Attachment> ReadAttachments(JsonElement.ArrayEnumerator array, string path) => array.Select(ele => new Attachment {
 | 
							private static IEnumerable<Attachment> ReadAttachments(JsonElement.ArrayEnumerator array, string path) => array.Select(ele => new Attachment {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										58
									
								
								app/Utils/Compression/Brotli.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								app/Utils/Compression/Brotli.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,58 @@
 | 
				
			|||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Buffers;
 | 
				
			||||||
 | 
					using System.IO;
 | 
				
			||||||
 | 
					using System.IO.Compression;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace DHT.Utils.Compression {
 | 
				
			||||||
 | 
						public sealed class Brotli {
 | 
				
			||||||
 | 
							private readonly byte[] tempBuffer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public Brotli(int bufferSize) {
 | 
				
			||||||
 | 
								tempBuffer = new byte[bufferSize];
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public byte[] Compress(byte[] input) {
 | 
				
			||||||
 | 
								var inputBuffer = new ReadOnlySpan<byte>(input);
 | 
				
			||||||
 | 
								var outputBuffer = new Span<byte>(tempBuffer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								using var outputStream = new MemoryStream();
 | 
				
			||||||
 | 
								using var brotliEncoder = new BrotliEncoder(10, 11);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								var result = OperationStatus.DestinationTooSmall;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								while (result == OperationStatus.DestinationTooSmall) {
 | 
				
			||||||
 | 
									result = brotliEncoder.Compress(inputBuffer, outputBuffer, out int bytesConsumed, out int bytesWritten, isFinalBlock: false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if (result == OperationStatus.InvalidData) {
 | 
				
			||||||
 | 
										throw new InvalidDataException();
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									Write(bytesWritten, outputBuffer, outputStream);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if (bytesConsumed > 0) {
 | 
				
			||||||
 | 
										inputBuffer = inputBuffer[bytesConsumed..];
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								result = OperationStatus.DestinationTooSmall;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								while (result == OperationStatus.DestinationTooSmall) {
 | 
				
			||||||
 | 
									result = brotliEncoder.Flush(outputBuffer, out var bytesWritten);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if (result == OperationStatus.InvalidData) {
 | 
				
			||||||
 | 
										throw new InvalidDataException();
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									Write(bytesWritten, outputBuffer, outputStream);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								return outputStream.ToArray();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							private static void Write(int bytes, Span<byte> buffer, MemoryStream outputStream) {
 | 
				
			||||||
 | 
								if (bytes > 0) {
 | 
				
			||||||
 | 
									outputStream.Write(buffer[..bytes]);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user