mirror of
				https://github.com/chylex/Discord-History-Tracker.git
				synced 2025-10-31 02:17:15 +01:00 
			
		
		
		
	Compare commits
	
		
			1 Commits
		
	
	
		
			c3bf7d5dc3
			...
			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