196 lines
6.0 KiB
TypeScript
196 lines
6.0 KiB
TypeScript
import { runTrigger } from "./process/triggers";
|
|
import { sleep } from "./util";
|
|
import { getCurrentCharacter, getCurrentChat, setCurrentChat } from "./storage/database.svelte";
|
|
import { runLuaButtonTrigger } from "./process/lua";
|
|
import { globalFetch } from "./globalApi.svelte";
|
|
|
|
let bgmElement:HTMLAudioElement|null = null;
|
|
|
|
function nodeObserve(node:HTMLElement){
|
|
const triggerName = node.getAttribute('risu-trigger');
|
|
const btnEvent = node.getAttribute('risu-btn');
|
|
const observerAdded = node.getAttribute('risu-observer');
|
|
const hlLang = node.getAttribute('x-hl-lang');
|
|
const ctrlName = node.getAttribute('risu-ctrl');
|
|
|
|
if(observerAdded){
|
|
return
|
|
}
|
|
if(triggerName){
|
|
node.addEventListener('click', async () => {
|
|
const currentChar = getCurrentCharacter()
|
|
if(currentChar.type === 'group'){
|
|
return;
|
|
}
|
|
const triggerResult = await runTrigger(currentChar, 'manual', {
|
|
chat: getCurrentChat(),
|
|
manualName: triggerName,
|
|
});
|
|
|
|
if(triggerResult){
|
|
setCurrentChat(triggerResult.chat);
|
|
}
|
|
|
|
}, {
|
|
passive: true,
|
|
});
|
|
node.setAttribute('risu-observer', 'true');
|
|
return
|
|
}
|
|
|
|
if(btnEvent){
|
|
node.addEventListener('click', async () => {
|
|
const currentChar = getCurrentCharacter()
|
|
if(currentChar.type === 'group'){
|
|
return;
|
|
}
|
|
const triggerResult = await runLuaButtonTrigger(currentChar, btnEvent);
|
|
|
|
if(triggerResult){
|
|
setCurrentChat(triggerResult.chat);
|
|
}
|
|
|
|
}, {
|
|
passive: true,
|
|
});
|
|
node.setAttribute('risu-observer', 'true');
|
|
return
|
|
}
|
|
|
|
if(hlLang){
|
|
node.addEventListener('contextmenu', (e)=>{
|
|
e.preventDefault();
|
|
const menu = document.createElement('div');
|
|
menu.setAttribute('class', 'fixed z-50 min-w-[160px] py-2 bg-gray-800 rounded-lg border border-gray-700')
|
|
|
|
const copyOption = document.createElement('div');
|
|
copyOption.textContent = 'Copy';
|
|
copyOption.setAttribute('class', 'px-4 py-2 text-sm text-gray-300 hover:bg-gray-700 cursor-pointer')
|
|
copyOption.addEventListener('click', ()=>{
|
|
navigator.clipboard.writeText(node.getAttribute('x-hl-text'));
|
|
menu.remove();
|
|
})
|
|
|
|
const downloadOption = document.createElement('div');
|
|
downloadOption.textContent = 'Download';
|
|
downloadOption.setAttribute('class', 'px-4 py-2 text-sm text-gray-300 hover:bg-gray-700 cursor-pointer')
|
|
downloadOption.addEventListener('click', ()=>{
|
|
const a = document.createElement('a');
|
|
a.href = URL.createObjectURL(new Blob([node.getAttribute('x-hl-text')], {type: 'text/plain'}));
|
|
a.download = 'code.' + hlLang;
|
|
a.click();
|
|
menu.remove();
|
|
})
|
|
|
|
menu.appendChild(copyOption);
|
|
menu.appendChild(downloadOption);
|
|
|
|
menu.style.left = e.clientX + 'px';
|
|
menu.style.top = e.clientY + 'px';
|
|
|
|
document.body.appendChild(menu);
|
|
|
|
document.addEventListener('click', ()=>{
|
|
menu.remove();
|
|
}, {once: true})
|
|
})
|
|
}
|
|
|
|
if(ctrlName){
|
|
const split = ctrlName.split('___');
|
|
|
|
switch(split[0]){
|
|
case 'bgm':{
|
|
const volume = split[1] === 'auto' ? 0.5 : parseFloat(split[1]);
|
|
if(!bgmElement){
|
|
bgmElement = new Audio(split[2]);
|
|
bgmElement.volume = volume
|
|
bgmElement.addEventListener('ended', ()=>{
|
|
bgmElement.remove();
|
|
bgmElement = null;
|
|
})
|
|
bgmElement.play();
|
|
}
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
export async function startObserveDom(){
|
|
//For codeblock we are using MutationObserver since it doesn't appear well
|
|
|
|
const observer = new MutationObserver((mutations) => {
|
|
mutations.forEach((mutation) => {
|
|
mutation.addedNodes.forEach((node) => {
|
|
if(node instanceof HTMLElement){
|
|
nodeObserve(node);
|
|
}
|
|
})
|
|
})
|
|
})
|
|
|
|
while(true){
|
|
document.querySelectorAll('[risu-trigger], [risu-btn], [x-hl-lang], [risu-ctrl]').forEach(nodeObserve);
|
|
await sleep(100);
|
|
}
|
|
}
|
|
|
|
|
|
let claudeObserverRunning = false;
|
|
let lastClaudeObserverLoad = 0;
|
|
let lastClaudeRequestTimes = 0;
|
|
let lastClaudeObserverPayload:any = null;
|
|
let lastClaudeObserverHeaders:any = null;
|
|
let lastClaudeObserverURL:any = null;
|
|
|
|
export async function registerClaudeObserver(arg:{
|
|
url:string,
|
|
body:any,
|
|
headers:any,
|
|
}) {
|
|
lastClaudeRequestTimes = 0;
|
|
lastClaudeObserverLoad = Date.now();
|
|
lastClaudeObserverPayload = safeStructuredClone(arg.body)
|
|
lastClaudeObserverHeaders = arg.headers;
|
|
lastClaudeObserverURL = arg.url;
|
|
lastClaudeObserverPayload.max_tokens = 10;
|
|
claudeObserver()
|
|
}
|
|
|
|
async function claudeObserver(){
|
|
if(claudeObserverRunning){
|
|
return
|
|
}
|
|
claudeObserverRunning = true;
|
|
|
|
const fetchIt = async (tries = 0)=>{
|
|
const res = await globalFetch(lastClaudeObserverURL, {
|
|
body: lastClaudeObserverPayload,
|
|
headers: lastClaudeObserverHeaders,
|
|
method: "POST"
|
|
})
|
|
if(res.status >= 400){
|
|
if(tries < 3){
|
|
fetchIt(tries + 1)
|
|
}
|
|
}
|
|
}
|
|
|
|
const func = async ()=>{
|
|
//request every 4 minutes and 30 seconds
|
|
if(lastClaudeObserverLoad > Date.now() - 1000 * 60 * 4.5){
|
|
return
|
|
}
|
|
|
|
if(lastClaudeRequestTimes > 4){
|
|
return
|
|
}
|
|
fetchIt()
|
|
lastClaudeObserverLoad = Date.now();
|
|
lastClaudeRequestTimes += 1;
|
|
}
|
|
|
|
setInterval(func, 20000)
|
|
} |