Merge branch 'main' into chatsticker

This commit is contained in:
ASDF9999
2023-06-22 23:21:36 +09:00
committed by GitHub
15 changed files with 337 additions and 73 deletions

View File

@@ -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 @@
<Arcodion name="OpenAI GPT">
<button class="p-2 hover:text-green-500" on:click={() => {changeModel('gpt35')}}>GPT-3.5 Turbo</button>
<button class="p-2 hover:text-green-500" on:click={() => {changeModel('gpt35_16k')}}>GPT-3.5 Turbo 16K</button>
<button class="p-2 hover:text-green-500" on:click={() => {changeModel('gpt35_16k_0613')}}>GPT-3.5 Turbo 16K 0613</button>
<button class="p-2 hover:text-green-500" on:click={() => {changeModel('gpt4')}}>GPT-4</button>
<button class="p-2 hover:text-green-500" on:click={() => {changeModel('gpt4_0613')}}>GPT-4 0613</button>
<button class="p-2 hover:text-green-500" on:click={() => {changeModel('gpt4_32k')}}>GPT-4 32K</button>
<button class="p-2 hover:text-green-500" on:click={() => {changeModel('gpt35_0613')}}>GPT-3.5 Turbo 0613</button>
<button class="p-2 hover:text-green-500" on:click={() => {changeModel('gpt35_16k_0613')}}>GPT-3.5 Turbo 16K 0613</button>
<button class="p-2 hover:text-green-500" on:click={() => {changeModel('gpt4_0613')}}>GPT-4 0613</button>
<button class="p-2 hover:text-green-500" on:click={() => {changeModel('gpt4_32k_0613')}}>GPT-4 32K 0613</button>
</Arcodion>
<Arcodion name="Anthropic Claude">
@@ -80,6 +81,7 @@
<button class="hover:bg-selected px-6 py-2 text-lg" on:click={() => {changeModel('kobold')}}>Kobold</button>
{#if isTauri ||isNodeServer}
<button class="hover:bg-selected px-6 py-2 text-lg" on:click={() => {changeModel('novelai')}}>NovelAI Clio</button>
<button class="hover:bg-selected px-6 py-2 text-lg" on:click={() => {changeModel('deepai')}}>DeepAI</button>
{/if}
<Arcodion name="Horde">
{#await getHordeModels()}

View File

@@ -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<voi
}
const selectedIndex = (await alertSelect([language.loadLatest, language.loadOthers]) === '0') ? 0 : parseInt(await alertSelect(selectables))
const selectedDb = dbs[selectedIndex][0]
const decompressedDb:Database = JSON.parse(Buffer.from(pako.inflate(await getFileData(ACCESS_TOKEN, selectedDb.id))).toString('utf-8'))
const decompressedDb:Database = decodeRisuSave(await getFileData(ACCESS_TOKEN, selectedDb.id))
return decompressedDb
}
@@ -405,10 +402,7 @@ async function loadDrive(ACCESS_TOKEN:string, mode: 'backup'|'sync'):Promise<voi
}
}
db.didFirstSetup = true
const dbjson = JSON.stringify(db)
const dbData = pako.deflate(
Buffer.from(dbjson, 'utf-8')
)
const dbData = encodeRisuSave(db)
if(isTauri){
await writeBinaryFile('database/database.bin', dbData, {dir: BaseDirectory.AppData})

View File

@@ -58,24 +58,35 @@ DOMPurify.addHook("uponSanitizeAttribute", (node, data) => {
}
})
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]}}}`,`<img src="${assetPath}" alt="${asset[0]}"/>`)
.replaceAll(`{{video::${asset[0]}}}`,`<video controls autoplay loop><source src="${assetPath}" type="video/mp4"></video>`)
.replaceAll(`{{audio::${asset[0]}}}`,`<audio controls autoplay loop><source src="${assetPath}" type="audio/mpeg"></audio>`)
if(mode === 'back'){
data = data.replaceAll(`{{bg::${asset[0]}}}`, `<div style="width:100%;height:100%;background: linear-gradient(rgba(0, 0, 0, 0.8), rgba(0, 0, 0, 0.8)),url(${assetPath}); background-size: cover;"></div>`)
}
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]}}}`,`<img src="${assetPath}" alt="${asset[0]}"/>`)
.replaceAll(`{{video::${asset[0]}}}`,`<video controls autoplay loop><source src="${assetPath}" type="video/mp4"></video>`)
.replaceAll(`{{audio::${asset[0]}}}`,`<audio controls autoplay loop><source src="${assetPath}" type="audio/mpeg"></audio>`)
if(mode === 'back'){
data = data.replaceAll(`{{bg::${asset[0]}}}`, `<div style="width:100%;height:100%;background: linear-gradient(rgba(0, 0, 0, 0.8), rgba(0, 0, 0, 0.8)),url(${assetPath}); background-size: cover;"></div>`)
}
}
}
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"],

40
src/ts/process/deepai.ts Normal file
View File

@@ -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;
}

View File

@@ -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){

33
src/ts/process/lua.ts Normal file
View File

@@ -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)
})
}
}
}

View File

@@ -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<Uint8Array, string>( {
@@ -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<formated.length;i++){
delete formated[i].memo
delete formated[i].name
if(arg.isGroupChat && formated[i].name && formated[i].role === 'assistant'){
formated[i].content = formated[i].name + ": " + formated[i].content
}
if(formated[i].role !== 'assistant' && formated[i].role !== 'user'){
formated[i].content = formated[i].role + ": " + formated[i].content
formated[i].role = 'assistant'
}
formated[i].name = undefined
}
const response = await createDeep([{
role: 'user',
content: stringlizeChat(formated, currentChar?.name ?? '')
}])
if(!response.ok){
return {
type: 'fail',
result: response.data
}
}
const result = Buffer.from(response.data).toString('utf-8')
return {
'type': 'success',
'result': result
}
}
default:{
if(aiModel.startsWith('claude')){
for(let i=0;i<formated.length;i++){

View File

@@ -8,7 +8,7 @@ import { defaultAutoSuggestPrompt, defaultJailbreak, defaultMainPrompt } from '.
export const DataBase = writable({} as any as Database)
export const loadedStore = writable(false)
export let appVer = '1.26.0'
export let appVer = '1.27.1'
export function setDatabase(data:Database){
if(checkNullish(data.characters)){

View File

@@ -7,7 +7,6 @@ import { appDataDir, join } from "@tauri-apps/api/path";
import { get } from "svelte/store";
import {open} from '@tauri-apps/api/shell'
import { DataBase, loadedStore, setDatabase, type Database, updateTextTheme, defaultSdDataFunc } from "./database";
import pako from "pako";
import { appWindow } from "@tauri-apps/api/window";
import { checkOldDomain, checkUpdate } from "../update";
import { selectedCharID } from "../stores";
@@ -21,6 +20,7 @@ import { cloneDeep } from "lodash";
import { NodeStorage } from "./nodeStorage";
import { defaultJailbreak, defaultMainPrompt, oldJailbreak, oldMainPrompt } from "./defaultPrompts";
import { loadRisuAccountData } from "../drive/accounter";
import { decodeRisuSave, encodeRisuSave } from "./risuSave";
//@ts-ignore
export const isTauri = !!window.__TAURI__
@@ -200,14 +200,14 @@ let lastSave = ''
export async function saveDb(){
lastSave =JSON.stringify(get(DataBase))
let changed = false
syncDrive()
DataBase.subscribe(() => {
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")
}
}
}
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('&');
}

View File

@@ -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'))
}
}