diff --git a/package.json b/package.json index fcf73bab..5f53d9e8 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "@msgpack/msgpack": "3.0.0-beta2", "@tauri-apps/api": "1.3.0", "@xenova/transformers": "^2.1.1", + "blueimp-md5": "^2.19.0", "body-parser": "^1.20.2", "buffer": "^6.0.3", "core-js": "^3.30.2", @@ -43,6 +44,7 @@ "showdown": "^2.1.0", "sweetalert2": "^11.7.3", "uuid": "^9.0.0", + "wasmoon": "^1.15.0", "web-streams-polyfill": "^3.2.1" }, "devDependencies": { @@ -50,6 +52,7 @@ "@tailwindcss/typography": "^0.5.9", "@tauri-apps/cli": "1.3.1", "@tsconfig/svelte": "^3.0.0", + "@types/blueimp-md5": "^2.18.0", "@types/dompurify": "^3.0.1", "@types/lodash": "^4.14.194", "@types/lodash.clonedeep": "^4.5.7", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 670f657c..5e0793d9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -16,6 +16,9 @@ dependencies: '@xenova/transformers': specifier: ^2.1.1 version: 2.1.1 + blueimp-md5: + specifier: ^2.19.0 + version: 2.19.0 body-parser: specifier: ^1.20.2 version: 1.20.2 @@ -88,6 +91,9 @@ dependencies: uuid: specifier: ^9.0.0 version: 9.0.0 + wasmoon: + specifier: ^1.15.0 + version: 1.15.0 web-streams-polyfill: specifier: ^3.2.1 version: 3.2.1 @@ -105,6 +111,9 @@ devDependencies: '@tsconfig/svelte': specifier: ^3.0.0 version: 3.0.0 + '@types/blueimp-md5': + specifier: ^2.18.0 + version: 2.18.0 '@types/dompurify': specifier: ^3.0.1 version: 3.0.1 @@ -731,12 +740,20 @@ packages: resolution: {integrity: sha512-pYrtLtOwku/7r1i9AMONsJMVYAtk3hzOfiGNekhtq5tYBGA7unMve8RvUclKLMT3PrihvJqUmzsRGh0RP84hKg==} dev: true + /@types/blueimp-md5@2.18.0: + resolution: {integrity: sha512-f4A+++lGZGJvVSgeyMkqA7BEf2BVQli6F+qEykKb49c5ieWQBkfpn6CP5c1IZr2Yi2Ofl6Fj+v0e1fN18Z8Cnw==} + dev: true + /@types/dompurify@3.0.1: resolution: {integrity: sha512-ubq8VKmf8W+U48jUOiZO4BoSGS7NnbITPMvrF+7HgMN4L+eezCKv8QBPB8p3o4YPicLMmNeTyDkE5X4c2ViHJQ==} dependencies: '@types/jsdom': 21.1.1 '@types/trusted-types': 2.0.3 + /@types/emscripten@1.39.5: + resolution: {integrity: sha512-DIOOg+POSrYl+OlNRHQuIEqCd8DCtynG57H862UCce16nXJX7J8eWxNGgOcf8Eyge8zXeSs27mz1UcFu8L/L7g==} + dev: false + /@types/jsdom@21.1.1: resolution: {integrity: sha512-cZFuoVLtzKP3gmq9eNosUL1R50U+USkbLtUQ1bYVgl/lKp0FZM7Cq4aIHAL8oIvQ17uSHi7jXPtfDOdjPwBE7A==} dependencies: @@ -912,6 +929,10 @@ packages: readable-stream: 3.6.2 dev: false + /blueimp-md5@2.19.0: + resolution: {integrity: sha512-DRQrD6gJyy8FbiE4s+bDoXS9hiW3Vbx5uCdwvcCf3zLHL+Iv7LtGHLpr+GZV8rHG8tK766FGYBwRbu8pELTt+w==} + dev: false + /body-parser@1.20.1: resolution: {integrity: sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} @@ -3190,6 +3211,13 @@ packages: xml-name-validator: 4.0.0 dev: false + /wasmoon@1.15.0: + resolution: {integrity: sha512-QU33AnnMTgbcGOJLzcqM2UBcSksmLvwkvB/Bcgkf5hS+EFoHxB6nyiQiDG+ZnALN8mn/ezeeMSm6eY1zs0cKTg==} + hasBin: true + dependencies: + '@types/emscripten': 1.39.5 + dev: false + /web-streams-polyfill@3.2.1: resolution: {integrity: sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==} engines: {node: '>= 8'} diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index e74c7145..cee0bae6 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -11,6 +11,8 @@ use serde_json::Value; use reqwest::header::{HeaderMap, HeaderName, HeaderValue}; use base64::{engine::general_purpose, Engine as _}; use std::time::Duration; +use serde_json::json; +use std::collections::HashMap; #[tauri::command] @@ -61,13 +63,15 @@ async fn native_request(url: String, body: String, header: String, method:String match response { Ok(resp) => { + let headers = resp.headers(); + let header_json = header_map_to_json(headers); let bytes = match resp.bytes().await { Ok(b) => b, Err(e) => return format!(r#"{{"success":false,"body":"{}"}}"#, e.to_string()), }; let encoded = general_purpose::STANDARD.encode(&bytes); - format!(r#"{{"success":true,"body":"{}"}}"#, encoded) + format!(r#"{{"success":true,"body":"{}","headers":{}}}"#, encoded, header_json) } Err(e) => format!(r#"{{"success":false,"body":"{}"}}"#, e.to_string()), } @@ -79,3 +83,12 @@ fn main() { .run(tauri::generate_context!()) .expect("error while running tauri application"); } + + +fn header_map_to_json(header_map: &HeaderMap) -> serde_json::Value { + let mut map = HashMap::new(); + for (key, value) in header_map { + map.insert(key.as_str().to_string(), value.to_str().unwrap().to_string()); + } + json!(map) +} \ No newline at end of file diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 1b499f4e..6a142c80 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -8,7 +8,7 @@ }, "package": { "productName": "RisuAI", - "version": "1.26.0" + "version": "1.27.1" }, "tauri": { "allowlist": { diff --git a/src/lib/UI/ModelList.svelte b/src/lib/UI/ModelList.svelte index 9feecb1a..f2a71248 100644 --- a/src/lib/UI/ModelList.svelte +++ b/src/lib/UI/ModelList.svelte @@ -3,7 +3,7 @@ import { getHordeModels } from "src/ts/horde/getModels"; import Arcodion from "./Arcodion.svelte"; import { language } from "src/lang"; - import { isNodeServer, isTauri } from "src/ts/storage/globalApi"; + import { isNodeServer, isTauri } from "src/ts/storage/globalApi"; export let value = "" export let onChange: (v:string) => void = (v) => {} @@ -63,10 +63,11 @@ - - + + + @@ -80,6 +81,7 @@ {#if isTauri ||isNodeServer} + {/if} {#await getHordeModels()} diff --git a/src/ts/drive/drive.ts b/src/ts/drive/drive.ts index e1278d7a..88ce9f33 100644 --- a/src/ts/drive/drive.ts +++ b/src/ts/drive/drive.ts @@ -2,7 +2,6 @@ import { get } from "svelte/store"; import { alertError, alertInput, alertNormal, alertSelect, alertStore } from "../alert"; import { DataBase, setDatabase, type Database } from "../storage/database"; import { forageStorage, getUnpargeables, isNodeServer, isTauri, openURL } from "../storage/globalApi"; -import pako from "pako"; import { BaseDirectory, exists, readBinaryFile, readDir, writeBinaryFile } from "@tauri-apps/api/fs"; import { language } from "../../lang"; import { relaunch } from '@tauri-apps/api/process'; @@ -10,6 +9,7 @@ import { open } from '@tauri-apps/api/shell'; import { cloneDeep, isEqual, last } from "lodash"; import { sleep } from "../util"; import { hubURL } from "../characterCards"; +import { decodeRisuSave, encodeRisuSave } from "../storage/risuSave"; export async function checkDriver(type:'save'|'load'|'loadtauri'|'savetauri'|'reftoken'){ const CLIENT_ID = '580075990041-l26k2d3c0nemmqiu3d3aag01npfrkn76.apps.googleusercontent.com'; @@ -245,10 +245,7 @@ async function backupDrive(ACCESS_TOKEN:string) { } } - const dbjson = JSON.stringify(get(DataBase)) - const dbData = pako.deflate( - Buffer.from(dbjson, 'utf-8') - ) + const dbData = encodeRisuSave(get(DataBase)) alertStore.set({ type: "wait", @@ -355,7 +352,7 @@ async function loadDrive(ACCESS_TOKEN:string, mode: 'backup'|'sync'):Promise { } }) -export async function ParseMarkdown(data:string, char:(character | groupChat) = null, mode:'normal'|'back' = 'normal') { - if(char && char.type !== 'group'){ - if(char.additionalAssets){ - for(const asset of char.additionalAssets){ - const assetPath = await getFileSrc(asset[1]) - data = data.replaceAll(`{{raw::${asset[0]}}}`, assetPath). - replaceAll(`{{img::${asset[0]}}}`,`${asset[0]}`) - .replaceAll(`{{video::${asset[0]}}}`,``) - .replaceAll(`{{audio::${asset[0]}}}`,``) - if(mode === 'back'){ - data = data.replaceAll(`{{bg::${asset[0]}}}`, `
`) - } +async function parseAdditionalAssets(data:string, char:character, mode:'normal'|'back'){ + if(char.additionalAssets){ + for(const asset of char.additionalAssets){ + const assetPath = await getFileSrc(asset[1]) + data = data.replaceAll(`{{raw::${asset[0]}}}`, assetPath). + replaceAll(`{{img::${asset[0]}}}`,`${asset[0]}`) + .replaceAll(`{{video::${asset[0]}}}`,``) + .replaceAll(`{{audio::${asset[0]}}}`,``) + if(mode === 'back'){ + data = data.replaceAll(`{{bg::${asset[0]}}}`, `
`) } } } + + return data +} + +export async function ParseMarkdown(data:string, char:(character | groupChat) = null, mode:'normal'|'back' = 'normal') { + let firstParsed = '' + if(char && char.type !== 'group'){ + data = await parseAdditionalAssets(data, char, mode) + firstParsed = data + } if(char){ data = processScript(char, data, 'editdisplay') } + if(firstParsed !== data && char && char.type !== 'group'){ + data = await parseAdditionalAssets(data, char, mode) + } return DOMPurify.sanitize(convertor.makeHtml(data), { ADD_TAGS: ["iframe"], ADD_ATTR: ["allow", "allowfullscreen", "frameborder", "scrolling"], diff --git a/src/ts/process/deepai.ts b/src/ts/process/deepai.ts new file mode 100644 index 00000000..8d117df1 --- /dev/null +++ b/src/ts/process/deepai.ts @@ -0,0 +1,40 @@ +import md5 from "blueimp-md5"; +import { globalFetch } from "../storage/globalApi"; +import type { OpenAIChat } from "."; + +function randomBytes(size: number): Uint8Array { + const array = new Uint8Array(size); + return crypto.getRandomValues(array); +} +export async function createDeep(messages: OpenAIChat[]) { + const userAgent = navigator.userAgent; + + const part1 = Math.floor(Math.random() * Math.pow(10, 11)).toString(); + + const md5Text = (text: string): string => { + return md5(text).split('').reverse().join(''); + } + + const part2 = md5Text(userAgent + md5Text(userAgent + md5Text(userAgent + part1 + "x"))); + + const apiKey = `tryit-${part1}-${part2}`; + + const headers = { + "api-key": apiKey, + "user-agent": userAgent + }; + + const body = new URLSearchParams(); + body.append("chat_style", "chat"); + console.log(messages); + body.append("chatHistory", JSON.stringify(messages)); + + const response = await globalFetch("https://api.deepai.org/chat_response", { + method: 'POST', + headers: headers, + body: body, + rawResponse: true + }); + + return response; +} \ No newline at end of file diff --git a/src/ts/process/index.ts b/src/ts/process/index.ts index 83e483cd..e634fe72 100644 --- a/src/ts/process/index.ts +++ b/src/ts/process/index.ts @@ -139,6 +139,11 @@ export async function sendChat(chatProcessIndex = -1,arg:{chatAdditonalTokens?:n maxContextTokens = 8000 } } + if(db.aiModel === 'deepai'){ + if(maxContextTokens > 3000){ + maxContextTokens = 3000 + } + } let unformated = { @@ -154,7 +159,7 @@ export async function sendChat(chatProcessIndex = -1,arg:{chatAdditonalTokens?:n } if(!currentChar.utilityBot){ - const mainp = currentChar.systemPrompt || db.mainPrompt + const mainp = currentChar.systemPrompt?.replaceAll('{{original}}', db.mainPrompt) || db.mainPrompt function formatPrompt(data:string){ @@ -183,7 +188,7 @@ export async function sendChat(chatProcessIndex = -1,arg:{chatAdditonalTokens?:n unformated.jailbreak.push(...formatPrompt(replacePlaceholders(db.jailbreak, currentChar.name))) } - unformated.globalNote.push(...formatPrompt(replacePlaceholders(currentChar.replaceGlobalNote || db.globalNote, currentChar.name))) + unformated.globalNote.push(...formatPrompt(replacePlaceholders(currentChar.replaceGlobalNote?.replaceAll('{{original}}', db.globalNote) || db.globalNote, currentChar.name))) } if(currentChat.note){ diff --git a/src/ts/process/lua.ts b/src/ts/process/lua.ts new file mode 100644 index 00000000..296580e0 --- /dev/null +++ b/src/ts/process/lua.ts @@ -0,0 +1,33 @@ +import { get } from "svelte/store"; +import { DataBase, type character } from "../storage/database"; +import type {LuaEngine} from 'wasmoon' +import { selectedCharID } from "../stores"; + +let lua: LuaEngine = null + + +export class CharacterLua{ + char:character + constructor(char:character){ + this.char = char + } + async init(){ + if(!lua){ + const factory = new (await import("wasmoon")).LuaFactory + lua = await factory.createEngine() + lua.global.set('getChat', () => { + const cha = get(DataBase).characters[get(selectedCharID)] + return cha.chats[cha.chatPage].message + }) + lua.global.set('setChat', () => { + const cha = get(DataBase).characters[get(selectedCharID)] + return cha.chats[cha.chatPage].message + }) + lua.global.set('doSend', (a:string) => { + console.log(a) + }) + } + } + + +} \ No newline at end of file diff --git a/src/ts/process/request.ts b/src/ts/process/request.ts index 3fe36380..b81eeadd 100644 --- a/src/ts/process/request.ts +++ b/src/ts/process/request.ts @@ -6,6 +6,7 @@ import { language } from "../../lang"; import { stringlizeChat, unstringlizeChat } from "./stringlize"; import { globalFetch, isTauri } from "../storage/globalApi"; import { sleep } from "../util"; +import { createDeep } from "./deepai"; interface requestDataArgument{ formated: OpenAIChat[] @@ -183,6 +184,13 @@ export async function requestChatDataMain(arg:requestDataArgument, model:'model' } } + if(da.headers.get('Content-Type') !== 'text/event-stream'){ + return { + type: "fail", + result: await da.text() + } + } + let dataUint = new Uint8Array([]) const transtream = new TransformStream( { @@ -572,6 +580,40 @@ export async function requestChatDataMain(arg:requestDataArgument, model:'model' 'result': unstringlizeChat(result, formated, currentChar?.name ?? '') } } + case "deepai":{ + + for(let i=0;i { + changed = true + }) while(true){ - const dbjson = JSON.stringify(get(DataBase)) - if(dbjson !== lastSave){ - lastSave = dbjson - const dbData = pako.deflate( - Buffer.from(dbjson, 'utf-8') - ) + if(changed){ + const dbData = encodeRisuSave(get(DataBase)) if(isTauri){ await writeBinaryFile('database/database.bin', dbData, {dir: BaseDirectory.AppData}) await writeBinaryFile(`database/dbbackup-${(Date.now()/100).toFixed()}.bin`, dbData, {dir: BaseDirectory.AppData}) @@ -279,13 +279,13 @@ export async function loadData() { } if(!await exists('database/database.bin', {dir: BaseDirectory.AppData})){ await writeBinaryFile('database/database.bin', - pako.deflate(Buffer.from(JSON.stringify({}), 'utf-8')) + encodeRisuSave({}) ,{dir: BaseDirectory.AppData}) } try { setDatabase( - JSON.parse(Buffer.from(pako.inflate(Buffer.from(await readBinaryFile('database/database.bin',{dir: BaseDirectory.AppData})))).toString('utf-8')) - ) + decodeRisuSave(await readBinaryFile('database/database.bin',{dir: BaseDirectory.AppData})) + ) } catch (error) { const backups = await getDbBackups() let backupLoaded = false @@ -293,7 +293,7 @@ export async function loadData() { try { const backupData = await readBinaryFile(`database/dbbackup-${backup}.bin`,{dir: BaseDirectory.AppData}) setDatabase( - JSON.parse(Buffer.from(pako.inflate(Buffer.from(backupData))).toString('utf-8')) + decodeRisuSave(backupData) ) backupLoaded = true } catch (error) {} @@ -309,12 +309,11 @@ export async function loadData() { else{ let gotStorage:Uint8Array = await forageStorage.getItem('database/database.bin') if(checkNullish(gotStorage)){ - gotStorage = pako.deflate(Buffer.from(JSON.stringify({}), 'utf-8')) - await forageStorage.setItem('database/database.bin', gotStorage) + await forageStorage.setItem('database/database.bin', encodeRisuSave({})) } try { setDatabase( - JSON.parse(Buffer.from(pako.inflate(Buffer.from(gotStorage))).toString('utf-8')) + decodeRisuSave(gotStorage) ) } catch (error) { const backups = await getDbBackups() @@ -323,7 +322,7 @@ export async function loadData() { try { const backupData:Uint8Array = await forageStorage.getItem(`database/dbbackup-${backup}.bin`) setDatabase( - JSON.parse(Buffer.from(pako.inflate(Buffer.from(backupData))).toString('utf-8')) + decodeRisuSave(backupData) ) backupLoaded = true } catch (error) {} @@ -380,7 +379,11 @@ export async function loadData() { const knownHostes = ["localhost","127.0.0.1","api.openai.com"] -export async function globalFetch(url:string, arg:{body?:any,headers?:{[key:string]:string}, rawResponse?:boolean, method?:"POST"|"GET", abortSignal?:AbortSignal} = {}) { +export async function globalFetch(url:string, arg:{body?:any,headers?:{[key:string]:string}, rawResponse?:boolean, method?:"POST"|"GET", abortSignal?:AbortSignal} = {}): Promise<{ + ok: boolean; + data: any; + headers:{[key:string]:string} +}> { try { const db = get(DataBase) const method = arg.method ?? "POST" @@ -430,7 +433,8 @@ export async function globalFetch(url:string, arg:{body?:any,headers?:{[key:stri addFetchLog("Uint8Array Response", da.ok && da.status >= 200 && da.status < 300) return { ok: da.ok && da.status >= 200 && da.status < 300, - data: new Uint8Array(await da.arrayBuffer()) + data: new Uint8Array(await da.arrayBuffer()), + headers: Object.fromEntries(da.headers) } } else{ @@ -438,7 +442,8 @@ export async function globalFetch(url:string, arg:{body?:any,headers?:{[key:stri addFetchLog(dat, da.ok && da.status >= 200 && da.status < 300) return { ok: da.ok && da.status >= 200 && da.status < 300, - data: dat + data: dat, + headers: Object.fromEntries(da.headers) } } @@ -446,6 +451,7 @@ export async function globalFetch(url:string, arg:{body?:any,headers?:{[key:stri return { ok: false, data: `${error}`, + headers: {} } } } @@ -469,7 +475,8 @@ export async function globalFetch(url:string, arg:{body?:any,headers?:{[key:stri addFetchLog("Uint8Array Response", da.ok && da.status >= 200 && da.status < 300) return { ok: da.ok && da.status >= 200 && da.status < 300, - data: new Uint8Array(await da.arrayBuffer()) + data: new Uint8Array(await da.arrayBuffer()), + headers: Object.fromEntries(da.headers) } } else{ @@ -477,7 +484,8 @@ export async function globalFetch(url:string, arg:{body?:any,headers?:{[key:stri addFetchLog(dat, da.ok && da.status >= 200 && da.status < 300) return { ok: da.ok && da.status >= 200 && da.status < 300, - data: dat + data: dat, + headers: Object.fromEntries(da.headers) } } @@ -485,6 +493,7 @@ export async function globalFetch(url:string, arg:{body?:any,headers?:{[key:stri return { ok: false, data: `${error}`, + headers: {} } } } @@ -492,20 +501,33 @@ export async function globalFetch(url:string, arg:{body?:any,headers?:{[key:stri if(db.requester === 'new'){ try { let preHeader = arg.headers ?? {} - preHeader["Content-Type"] = `application/json` - const body = JSON.stringify(arg.body) + let body:any + if(arg.body instanceof URLSearchParams){ + const argBody = arg.body as URLSearchParams + body = argBody.toString() + preHeader["Content-Type"] = `application/x-www-form-urlencoded` + } + else{ + body = JSON.stringify(arg.body) + preHeader["Content-Type"] = `application/json` + } + console.log(body) const header = JSON.stringify(preHeader) const res:string = await invoke('native_request', {url:url, body:body, header:header, method: method}) const d:{ success: boolean - body:string + body:string, + headers: {[key:string]:string} } = JSON.parse(res) - + + const resHeader = d.headers ?? {} + if(!d.success){ addFetchLog(Buffer.from(d.body, 'base64').toString('utf-8'), false) return { ok:false, - data: Buffer.from(d.body, 'base64').toString('utf-8') + data: Buffer.from(d.body, 'base64').toString('utf-8'), + headers: resHeader } } else{ @@ -513,14 +535,16 @@ export async function globalFetch(url:string, arg:{body?:any,headers?:{[key:stri addFetchLog("Uint8Array Response", true) return { ok:true, - data: new Uint8Array(Buffer.from(d.body, 'base64')) + data: new Uint8Array(Buffer.from(d.body, 'base64')), + headers: resHeader } } else{ addFetchLog(JSON.parse(Buffer.from(d.body, 'base64').toString('utf-8')), true) return { ok:true, - data: JSON.parse(Buffer.from(d.body, 'base64').toString('utf-8')) + data: JSON.parse(Buffer.from(d.body, 'base64').toString('utf-8')), + headers: resHeader } } } @@ -528,6 +552,7 @@ export async function globalFetch(url:string, arg:{body?:any,headers?:{[key:stri return { ok: false, data: `${error}`, + headers: {} } } } @@ -549,6 +574,7 @@ export async function globalFetch(url:string, arg:{body?:any,headers?:{[key:stri return { ok: d.ok, data: new Uint8Array(d.data as number[]), + headers: d.headers } } else{ @@ -556,20 +582,33 @@ export async function globalFetch(url:string, arg:{body?:any,headers?:{[key:stri return { ok: d.ok, data: d.data, + headers: d.headers } } } else{ try { - let headers = arg.headers ?? {} - if(!headers["Content-Type"]){ - headers["Content-Type"] = `application/json` + let body:any + if(arg.body instanceof URLSearchParams){ + const argBody = arg.body as URLSearchParams + body = argBody.toString() + let headers = arg.headers ?? {} + if(!headers["Content-Type"]){ + headers["Content-Type"] = `application/x-www-form-urlencoded` + } + } + else{ + body = JSON.stringify(arg.body) + let headers = arg.headers ?? {} + if(!headers["Content-Type"]){ + headers["Content-Type"] = `application/json` + } } if(arg.rawResponse){ const furl = `/proxy?url=${encodeURIComponent(url)}` const da = await fetch(furl, { - body: JSON.stringify(arg.body), + body: body, headers: { "risu-header": encodeURIComponent(JSON.stringify(arg.headers)), "Content-Type": "application/json" @@ -581,40 +620,53 @@ export async function globalFetch(url:string, arg:{body?:any,headers?:{[key:stri addFetchLog("Uint8Array Response", da.ok && da.status >= 200 && da.status < 300) return { ok: da.ok && da.status >= 200 && da.status < 300, - data: new Uint8Array(await da.arrayBuffer()) + data: new Uint8Array(await da.arrayBuffer()), + headers: Object.fromEntries(da.headers) } } else{ const furl = `/proxy?url=${encodeURIComponent(url)}` const da = await fetch(furl, { - body: JSON.stringify(arg.body), + body: body, headers: { "risu-header": encodeURIComponent(JSON.stringify(arg.headers)), "Content-Type": "application/json" }, method: method }) - - const dat = await da.json() - addFetchLog(dat, da.ok && da.status >= 200 && da.status < 300) - return { - ok: da.ok && da.status >= 200 && da.status < 300, - data: dat + const daText = await da.text() + try { + const dat = JSON.parse(daText) + addFetchLog(dat, da.ok && da.status >= 200 && da.status < 300) + return { + ok: da.ok && da.status >= 200 && da.status < 300, + data: dat, + headers: Object.fromEntries(da.headers) + } + } catch (error) { + addFetchLog(daText, false) + return { + ok:false, + data: daText, + headers: Object.fromEntries(da.headers) + } } } } catch (error) { console.log(error) return { ok:false, - data: `${error}` + data: `${error}`, + headers: {} } } } } catch (error) { return { ok:false, - data: `${error}` + data: `${error}`, + headers: {} } } } @@ -834,4 +886,14 @@ export function openURL(url:string){ else{ window.open(url, "_blank") } -} \ No newline at end of file +} + +function formDataToString(formData: FormData): string { + const params: string[] = []; + + for (const [name, value] of formData.entries()) { + params.push(`${encodeURIComponent(name)}=${encodeURIComponent(value.toString())}`); + } + + return params.join('&'); + } \ No newline at end of file diff --git a/src/ts/storage/risuSave.ts b/src/ts/storage/risuSave.ts new file mode 100644 index 00000000..4c56d8d8 --- /dev/null +++ b/src/ts/storage/risuSave.ts @@ -0,0 +1,31 @@ +import { decode, encode } from "@msgpack/msgpack"; +import { isEqual } from "lodash"; +import pako from "pako"; + + +export function encodeRisuSave(data:any){ + const risuSaveHeader = new Uint8Array(Buffer.from("\u0000\u0000RISU",'utf-8')) + const encoded = encode(data) + const mergedArray = new Uint8Array(risuSaveHeader.length + encoded.length); + mergedArray.set(risuSaveHeader); + mergedArray.set(encoded, risuSaveHeader.length); + return mergedArray +} + +export function decodeRisuSave(data:Uint8Array){ + const risuSaveHeader = new Uint8Array(Buffer.from("\u0000\u0000RISU",'utf-8')) + const sub = data.subarray(0, risuSaveHeader.length) + if(isEqual(sub, risuSaveHeader)){ + try { + const realData = data.subarray(risuSaveHeader.length) + const dec = decode(realData) + return dec + } catch (error) { + console.error(error) + throw error + } + } + else{ + return JSON.parse(Buffer.from(pako.inflate(Buffer.from(data))).toString('utf-8')) + } +} \ No newline at end of file diff --git a/version.json b/version.json index dbba4485..08b4235c 100644 --- a/version.json +++ b/version.json @@ -1 +1 @@ -{"version":"1.26.0"} \ No newline at end of file +{"version":"1.27.1"} \ No newline at end of file