import type { RuntimeConfig } from './types'; export function parseGM(raw:string){ const command=raw.trim().replace(/^\./,''); if(/[\r\n\x00-\x1f]/.test(command))throw new Error('Use one command per request'); if(!/^(server info|announce [^<>]{1,240}|teleport name [A-Za-z]{2,12} [A-Za-z0-9 _-]{1,80}|pinfo [A-Za-z]{2,12}|server motd)$/.test(command))throw new Error('Supported remote commands: server info, announce , teleport name , pinfo , server motd.'); return command; } const escapeXML=(s:string)=>s.replace(/&/g,'&').replace(//g,'>').replace(/"/g,'"').replace(/'/g,'''); export async function runGM(raw:string,live:boolean,confirmed:boolean,config:RuntimeConfig){ const command=parseGM(raw); if(!live)return {command,output:`[SIMULATED] Accepted: ${command}\nNo command was sent to a game server.`}; if(!confirmed)throw new Error('Confirm the live GM command before execution'); if(!config.SOAP_URL||!config.SOAP_USER||!config.SOAP_PASSWORD)throw new Error('Configure SOAP_URL, SOAP_USER and SOAP_PASSWORD on the server first'); const r=await fetch(config.SOAP_URL,{method:'POST',headers:{'Content-Type':'text/xml; charset=utf-8',Authorization:`Basic ${btoa(`${config.SOAP_USER}:${config.SOAP_PASSWORD}`)}`},body:`${escapeXML(command)}`,signal:AbortSignal.timeout(12000),redirect:'error'}); const xml=await r.text();if(!r.ok||/<(?:\w+:)?Fault\b/.test(xml))throw new Error(`SOAP command failed (HTTP ${r.status}). Check the worldserver log.`); const output=xml.match(/<(?:\w+:)?result(?:\s[^>]*)?>([\s\S]*?)<\/(?:\w+:)?result>/)?.[1]; if(output===undefined)throw new Error('Unexpected SOAP response; check the worldserver before retrying'); return {command,output:output.replace(/ /g,'\r').replace(/ /g,'\n').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"').replace(/'/g,"'").replace(/&/g,'&').slice(0,8000)}; }