Discord bot to open dong files

add dong-io.ts from https://github.com/dong-files/web/blob/master/src/dong-io.ts

this will be replaced with a package etc once i get around to extracting it out lol

+120
+120
src/lib/dong-io.ts
··· 1 + declare global { 2 + interface Uint8Array { 3 + toBase64(): string; 4 + } 5 + } 6 + 7 + const blobBytes = async (blob: Blob) => { 8 + if ("bytes" in blob) return blob.bytes(); 9 + return new Response(blob).arrayBuffer().then((buffer) => { 10 + const uint = new Uint8Array(buffer); 11 + return uint; 12 + }); 13 + }; 14 + 15 + export const createDong = async ( 16 + image: File, 17 + audio: File 18 + ): Promise<Blob | string> => { 19 + if ( 20 + image.type === "" || 21 + !image.type.startsWith("image/") || 22 + audio.type === "" || 23 + !audio.type.startsWith("audio/") 24 + ) 25 + return "Mime types invalid"; 26 + return new Blob([ 27 + // version 28 + (() => { 29 + const version = new Int8Array(new ArrayBuffer(2)); 30 + version[0] = 0xd0; 31 + version[1] = 2; 32 + return version; 33 + })(), 34 + // image type 35 + image.type, 36 + // 00 padding 37 + new ArrayBuffer(256 - image.type.length), 38 + // image size 39 + (() => { 40 + const value = new Uint32Array(1); 41 + value[0] = image.size; 42 + return value; 43 + })(), 44 + // audio type 45 + audio.type, 46 + // 00 padding 47 + new ArrayBuffer(256 - audio.type.length), 48 + // audio size 49 + (() => { 50 + const value = new Uint32Array(1); 51 + value[0] = audio.size; 52 + return value; 53 + })(), 54 + // image data 55 + await blobBytes(image), 56 + // audio data 57 + await blobBytes(audio), 58 + ]); 59 + }; 60 + 61 + export async function readDong(dongFile: File): Promise< 62 + | { 63 + image: { data: Uint8Array; mime: string }; 64 + audio: { data: Uint8Array; mime: string }; 65 + } 66 + | string 67 + > { 68 + // get first 2 bytes and verify 69 + const version = new Uint8Array(await blobBytes(dongFile.slice(0, 2))); 70 + if (version[0] !== 0xd0 || version[1] !== 2) return "Invalid file"; 71 + 72 + // get next 256 bytes and get mime type 73 + const imgMimeType: string | undefined = ( 74 + await dongFile.slice(2, 258).text() 75 + ).match(/[a-zA-Z0-9.]+\/[a-zA-Z0-9.]+/gm)?.[0]; 76 + if (!imgMimeType) return "Image mime type parse failed"; 77 + 78 + // get next 4 bytes and get image size 79 + const imgSize = new Uint32Array( 80 + (await blobBytes(dongFile.slice(258, 262))).buffer 81 + )[0]; 82 + 83 + // get next 256 bytes and get mime type 84 + const audMimeType: string | undefined = ( 85 + await dongFile.slice(262, 518).text() 86 + ).match(/[a-zA-Z0-9.]+\/[a-zA-Z0-9.]+/gm)?.[0]; 87 + if (!audMimeType) return "Audio mime type parse failed"; 88 + 89 + // get next 4 bytes and get image size 90 + const audSize = new Uint32Array( 91 + (await blobBytes(dongFile.slice(518, 522))).buffer 92 + )[0]; 93 + 94 + const imageBytes = await blobBytes(dongFile.slice(522, 522 + imgSize)); 95 + const audioBytes = await blobBytes( 96 + dongFile.slice(522 + imgSize, 522 + imgSize + audSize) 97 + ); 98 + 99 + return { 100 + image: { 101 + mime: imgMimeType, 102 + data: imageBytes, 103 + }, 104 + audio: { 105 + mime: audMimeType, 106 + data: audioBytes, 107 + }, 108 + }; 109 + } 110 + 111 + export const download = (file: File) => { 112 + const url = URL.createObjectURL(file); 113 + const a = document.createElement("a"); 114 + a.href = url; 115 + a.download = file.name; 116 + document.body.appendChild(a); 117 + a.click(); 118 + document.body.removeChild(a); 119 + URL.revokeObjectURL(url); 120 + };