/** * @file modules/variables/var-commands.js * @description 变量斜杠命令与宏替换,常驻模块 */ import { getContext } from "../../../../../extensions.js"; import { getLocalVariable, setLocalVariable } from "../../../../../variables.js"; import { createModuleEvents, event_types } from "../../core/event-manager.js"; import jsYaml from "../../libs/js-yaml.mjs"; import { lwbSplitPathWithBrackets, lwbSplitPathAndValue, normalizePath, ensureDeepContainer, safeJSONStringify, maybeParseObject, valueToString, deepClone, } from "../../core/variable-path.js"; const MODULE_ID = 'varCommands'; const TAG_RE_XBGETVAR = /\{\{xbgetvar::([^}]+)\}\}/gi; const TAG_RE_XBGETVAR_YAML = /\{\{xbgetvar_yaml::([^}]+)\}\}/gi; let events = null; let initialized = false; function getMsgKey(msg) { return (typeof msg?.mes === 'string') ? 'mes' : (typeof msg?.content === 'string' ? 'content' : null); } export function parseValueForSet(value) { try { const t = String(value ?? '').trim(); if (t.startsWith('{') || t.startsWith('[')) { try { return JSON.parse(t); } catch {} } const looksLikeJson = (t[0] === '{' || t[0] === '[') && /[:\],}]/.test(t); if (looksLikeJson && !t.includes('"') && t.includes("'")) { try { return JSON.parse(t.replace(/'/g, '"')); } catch {} } if (t === 'true' || t === 'false' || t === 'null') { return JSON.parse(t); } if (/^-?\d+(\.\d+)?$/.test(t)) { return JSON.parse(t); } return value; } catch { return value; } } function extractPathFromArgs(namedArgs, unnamedArgs) { try { if (namedArgs && typeof namedArgs.key === 'string' && namedArgs.key.trim()) { return String(namedArgs.key).trim(); } const arr = Array.isArray(unnamedArgs) ? unnamedArgs : [unnamedArgs]; const first = String(arr[0] ?? '').trim(); const m = /^key\s*=\s*(.+)$/i.exec(first); return m ? m[1].trim() : first; } catch { return ''; } } function ensureAbsTargetPath(basePath, token) { const t = String(token || '').trim(); if (!t) return String(basePath || ''); const base = String(basePath || ''); if (t === base || t.startsWith(base + '.')) return t; return base ? (base + '.' + t) : t; } function segmentsRelativeToBase(absPath, basePath) { const segs = lwbSplitPathWithBrackets(absPath); const baseSegs = lwbSplitPathWithBrackets(basePath); if (!segs.length || !baseSegs.length) return segs || []; const matches = baseSegs.every((b, i) => String(segs[i]) === String(b)); return matches ? segs.slice(baseSegs.length) : segs; } function setDeepBySegments(target, segs, value) { let cur = target; for (let i = 0; i < segs.length; i++) { const isLast = i === segs.length - 1; const key = segs[i]; if (isLast) { cur[key] = value; } else { const nxt = cur[key]; const nextSeg = segs[i + 1]; const wantArray = (typeof nextSeg === 'number'); // 已存在且类型正确:继续深入 if (wantArray && Array.isArray(nxt)) { cur = nxt; continue; } if (!wantArray && (nxt && typeof nxt === 'object') && !Array.isArray(nxt)) { cur = nxt; continue; } // 不存在或类型不匹配:创建正确的容器 cur[key] = wantArray ? [] : {}; cur = cur[key]; } } } export function lwbResolveVarPath(path) { try { const segs = lwbSplitPathWithBrackets(path); if (!segs.length) return ''; const rootName = String(segs[0]); const rootRaw = getLocalVariable(rootName); if (segs.length === 1) { return valueToString(rootRaw); } const obj = maybeParseObject(rootRaw); if (!obj) return ''; let cur = obj; for (let i = 1; i < segs.length; i++) { cur = cur?.[segs[i]]; if (cur === undefined) return ''; } return valueToString(cur); } catch { return ''; } } export function replaceXbGetVarInString(s) { s = String(s ?? ''); if (!s || s.indexOf('{{xbgetvar::') === -1) return s; TAG_RE_XBGETVAR.lastIndex = 0; return s.replace(TAG_RE_XBGETVAR, (_, p) => lwbResolveVarPath(p)); } /** * 将 {{xbgetvar_yaml::路径}} 替换为 YAML 格式的值 * @param {string} s * @returns {string} */ export function replaceXbGetVarYamlInString(s) { s = String(s ?? ''); if (!s || s.indexOf('{{xbgetvar_yaml::') === -1) return s; TAG_RE_XBGETVAR_YAML.lastIndex = 0; return s.replace(TAG_RE_XBGETVAR_YAML, (_, p) => { const value = lwbResolveVarPath(p); if (!value) return ''; // 尝试解析为对象/数组,然后转 YAML try { const parsed = JSON.parse(value); if (typeof parsed === 'object' && parsed !== null) { return jsYaml.dump(parsed, { indent: 2, lineWidth: -1, noRefs: true, quotingType: '"', }).trim(); } return value; } catch { return value; } }); } export function replaceXbGetVarInChat(chat) { if (!Array.isArray(chat)) return; for (const msg of chat) { try { const key = getMsgKey(msg); if (!key) continue; const old = String(msg[key] ?? ''); const hasJson = old.indexOf('{{xbgetvar::') !== -1; const hasYaml = old.indexOf('{{xbgetvar_yaml::') !== -1; if (!hasJson && !hasYaml) continue; let result = hasJson ? replaceXbGetVarInString(old) : old; result = hasYaml ? replaceXbGetVarYamlInString(result) : result; msg[key] = result; } catch {} } } export function applyXbGetVarForMessage(messageId, writeback = true) { try { const ctx = getContext(); const msg = ctx?.chat?.[messageId]; if (!msg) return; const key = getMsgKey(msg); if (!key) return; const old = String(msg[key] ?? ''); const hasJson = old.indexOf('{{xbgetvar::') !== -1; const hasYaml = old.indexOf('{{xbgetvar_yaml::') !== -1; if (!hasJson && !hasYaml) return; let out = hasJson ? replaceXbGetVarInString(old) : old; out = hasYaml ? replaceXbGetVarYamlInString(out) : out; if (writeback && out !== old) { msg[key] = out; } } catch {} } export function parseDirectivesTokenList(tokens) { const out = { ro: false, objectPolicy: null, arrayPolicy: null, constraints: {}, clear: false }; for (const tok of tokens) { const t = String(tok || '').trim(); if (!t) continue; if (t === '$ro') { out.ro = true; continue; } if (t === '$ext') { out.objectPolicy = 'ext'; continue; } if (t === '$prune') { out.objectPolicy = 'prune'; continue; } if (t === '$free') { out.objectPolicy = 'free'; continue; } if (t === '$grow') { out.arrayPolicy = 'grow'; continue; } if (t === '$shrink') { out.arrayPolicy = 'shrink'; continue; } if (t === '$list') { out.arrayPolicy = 'list'; continue; } if (t.startsWith('$min=')) { const num = Number(t.slice(5)); if (Number.isFinite(num)) out.constraints.min = num; continue; } if (t.startsWith('$max=')) { const num = Number(t.slice(5)); if (Number.isFinite(num)) out.constraints.max = num; continue; } if (t.startsWith('$range=')) { const m = t.match(/^\$range=\[\s*(-?\d+(?:\.\d+)?)\s*,\s*(-?\d+(?:\.\d+)?)\s*\]$/); if (m) { const a = Number(m[1]), b = Number(m[2]); if (Number.isFinite(a) && Number.isFinite(b)) { out.constraints.min = Math.min(a, b); out.constraints.max = Math.max(a, b); } } continue; } if (t.startsWith('$step=')) { const num = Number(t.slice(6)); if (Number.isFinite(num)) { out.constraints.step = Math.max(0, Math.abs(num)); } continue; } if (t.startsWith('$enum=')) { const m = t.match(/^\$enum=\{\s*([^}]+)\s*\}$/); if (m) { const vals = m[1].split(/[;;]/).map(s => s.trim()).filter(Boolean); if (vals.length) out.constraints.enum = vals; } continue; } if (t.startsWith('$match=')) { const raw = t.slice(7); if (raw.startsWith('/') && raw.lastIndexOf('/') > 0) { const last = raw.lastIndexOf('/'); const pattern = raw.slice(1, last).replace(/\\\//g, '/'); const flags = raw.slice(last + 1) || ''; out.constraints.regex = { source: pattern, flags }; } continue; } if (t === '$clear') { out.clear = true; continue; } } return out; } export function expandShorthandRuleObject(basePath, valueObj) { try { const base = String(basePath || ''); const isObj = v => v && typeof v === 'object' && !Array.isArray(v); if (!isObj(valueObj)) return null; function stripDollarKeysDeep(val) { if (Array.isArray(val)) return val.map(stripDollarKeysDeep); if (isObj(val)) { const out = {}; for (const k in val) { if (!Object.prototype.hasOwnProperty.call(val, k)) continue; if (String(k).trim().startsWith('$')) continue; out[k] = stripDollarKeysDeep(val[k]); } return out; } return val; } function formatPathWithBrackets(pathStr) { const segs = lwbSplitPathWithBrackets(String(pathStr || '')); let out = ''; for (const s of segs) { if (typeof s === 'number') out += `[${s}]`; else out += out ? `.${s}` : `${s}`; } return out; } function assignDeep(dst, src) { for (const k in src) { if (!Object.prototype.hasOwnProperty.call(src, k)) continue; const v = src[k]; if (v && typeof v === 'object' && !Array.isArray(v)) { if (!dst[k] || typeof dst[k] !== 'object' || Array.isArray(dst[k])) { dst[k] = {}; } assignDeep(dst[k], v); } else { dst[k] = v; } } } const rulesTop = {}; const dataTree = {}; function writeDataAt(relPathStr, val) { const abs = ensureAbsTargetPath(base, relPathStr); const relSegs = segmentsRelativeToBase(abs, base); if (relSegs.length) { setDeepBySegments(dataTree, relSegs, val); } else { if (val && typeof val === 'object' && !Array.isArray(val)) { assignDeep(dataTree, val); } else { dataTree['$root'] = val; } } } function walk(node, currentRelPathStr) { if (Array.isArray(node)) { const cleanedArr = node.map(stripDollarKeysDeep); if (currentRelPathStr) writeDataAt(currentRelPathStr, cleanedArr); for (let i = 0; i < node.length; i++) { const el = node[i]; if (el && typeof el === 'object') { const childRel = currentRelPathStr ? `${currentRelPathStr}.${i}` : String(i); walk(el, childRel); } } return; } if (!isObj(node)) { if (currentRelPathStr) writeDataAt(currentRelPathStr, node); return; } const cleaned = stripDollarKeysDeep(node); if (currentRelPathStr) writeDataAt(currentRelPathStr, cleaned); else assignDeep(dataTree, cleaned); for (const key in node) { if (!Object.prototype.hasOwnProperty.call(node, key)) continue; const v = node[key]; const keyStr = String(key).trim(); const isRule = keyStr.startsWith('$'); if (!isRule) { const childRel = currentRelPathStr ? `${currentRelPathStr}.${keyStr}` : keyStr; if (v && typeof v === 'object') walk(v, childRel); continue; } const rest = keyStr.slice(1).trim(); if (!rest) continue; const parts = rest.split(/\s+/).filter(Boolean); if (!parts.length) continue; const targetToken = parts.pop(); const dirs = parts.map(t => String(t).trim().startsWith('$') ? String(t).trim() : ('$' + String(t).trim()) ); const fullRelTarget = currentRelPathStr ? `${currentRelPathStr}.${targetToken}` : targetToken; const absTarget = ensureAbsTargetPath(base, fullRelTarget); const absDisplay = formatPathWithBrackets(absTarget); const ruleKey = `$ ${dirs.join(' ')} ${absDisplay}`.trim(); rulesTop[ruleKey] = {}; if (v !== undefined) { const cleanedVal = stripDollarKeysDeep(v); writeDataAt(fullRelTarget, cleanedVal); if (v && typeof v === 'object') { walk(v, fullRelTarget); } } } } walk(valueObj, ''); const out = {}; assignDeep(out, rulesTop); assignDeep(out, dataTree); return out; } catch { return null; } } export function lwbAssignVarPath(path, value) { try { const segs = lwbSplitPathWithBrackets(path); if (!segs.length) return ''; const rootName = String(segs[0]); let vParsed = parseValueForSet(value); if (vParsed && typeof vParsed === 'object') { try { if (globalThis.LWB_Guard?.loadRules) { const res = globalThis.LWB_Guard.loadRules(vParsed, rootName); if (res?.cleanValue !== undefined) vParsed = res.cleanValue; if (res?.rulesDelta && typeof res.rulesDelta === 'object') { if (globalThis.LWB_Guard?.applyDeltaTable) { globalThis.LWB_Guard.applyDeltaTable(res.rulesDelta); } else if (globalThis.LWB_Guard?.applyDelta) { for (const [p, d] of Object.entries(res.rulesDelta)) { globalThis.LWB_Guard.applyDelta(p, d); } } globalThis.LWB_Guard.save?.(); } } } catch {} } const absPath = normalizePath(path); let guardOk = true; let guardVal = vParsed; try { if (globalThis.LWB_Guard?.validate) { const g = globalThis.LWB_Guard.validate('set', absPath, vParsed); guardOk = !!g?.allow; if ('value' in g) guardVal = g.value; } } catch {} if (!guardOk) return ''; if (segs.length === 1) { if (guardVal && typeof guardVal === 'object') { setLocalVariable(rootName, safeJSONStringify(guardVal)); } else { setLocalVariable(rootName, String(guardVal ?? '')); } return ''; } const rootRaw = getLocalVariable(rootName); let obj; const parsed = maybeParseObject(rootRaw); if (parsed) { obj = deepClone(parsed); } else { // 若根变量不存在:A[0].x 这类路径期望根为数组 obj = (typeof segs[1] === 'number') ? [] : {}; } const { parent, lastKey } = ensureDeepContainer(obj, segs.slice(1)); parent[lastKey] = guardVal; setLocalVariable(rootName, safeJSONStringify(obj)); return ''; } catch { return ''; } } export function lwbAddVarPath(path, increment) { try { const segs = lwbSplitPathWithBrackets(path); if (!segs.length) return ''; const currentStr = lwbResolveVarPath(path); const incStr = String(increment ?? ''); const currentNum = Number(currentStr); const incNum = Number(incStr); const bothNumeric = currentStr !== '' && incStr !== '' && Number.isFinite(currentNum) && Number.isFinite(incNum); const newValue = bothNumeric ? (currentNum + incNum) : (currentStr + incStr); lwbAssignVarPath(path, newValue); return valueToString(newValue); } catch { return ''; } } /** * 删除变量或深层属性(支持点路径/中括号路径) * @param {string} path * @returns {string} 空字符串 */ export function lwbDeleteVarPath(path) { try { const segs = lwbSplitPathWithBrackets(path); if (!segs.length) return ''; const rootName = String(segs[0]); const absPath = normalizePath(path); // 只有根变量:对齐 /flushvar 的“清空”语义 if (segs.length === 1) { try { if (globalThis.LWB_Guard?.validate) { const g = globalThis.LWB_Guard.validate('delNode', absPath); if (!g?.allow) return ''; } } catch {} setLocalVariable(rootName, ''); return ''; } const rootRaw = getLocalVariable(rootName); const parsed = maybeParseObject(rootRaw); if (!parsed) return ''; const obj = deepClone(parsed); const subSegs = segs.slice(1); let cur = obj; for (let i = 0; i < subSegs.length - 1; i++) { cur = cur?.[subSegs[i]]; if (cur == null || typeof cur !== 'object') return ''; } try { if (globalThis.LWB_Guard?.validate) { const g = globalThis.LWB_Guard.validate('delNode', absPath); if (!g?.allow) return ''; } } catch {} const lastKey = subSegs[subSegs.length - 1]; if (Array.isArray(cur)) { if (typeof lastKey === 'number' && lastKey >= 0 && lastKey < cur.length) { cur.splice(lastKey, 1); } else { const equal = (a, b) => a === b || a == b || String(a) === String(b); for (let i = cur.length - 1; i >= 0; i--) { if (equal(cur[i], lastKey)) cur.splice(i, 1); } } } else { try { delete cur[lastKey]; } catch {} } setLocalVariable(rootName, safeJSONStringify(obj)); return ''; } catch { return ''; } } /** * 向数组推入值(支持点路径/中括号路径) * @param {string} path * @param {*} value * @returns {string} 新数组长度(字符串) */ export function lwbPushVarPath(path, value) { try { const segs = lwbSplitPathWithBrackets(path); if (!segs.length) return ''; const rootName = String(segs[0]); const absPath = normalizePath(path); const vParsed = parseValueForSet(value); // 仅根变量:将 root 视为数组 if (segs.length === 1) { try { if (globalThis.LWB_Guard?.validate) { const g = globalThis.LWB_Guard.validate('push', absPath, vParsed); if (!g?.allow) return ''; } } catch {} const rootRaw = getLocalVariable(rootName); let arr; try { arr = JSON.parse(rootRaw); } catch { arr = undefined; } if (!Array.isArray(arr)) arr = rootRaw != null && rootRaw !== '' ? [rootRaw] : []; arr.push(vParsed); setLocalVariable(rootName, safeJSONStringify(arr)); return String(arr.length); } const rootRaw = getLocalVariable(rootName); let obj; const parsed = maybeParseObject(rootRaw); if (parsed) { obj = deepClone(parsed); } else { const firstSubSeg = segs[1]; obj = typeof firstSubSeg === 'number' ? [] : {}; } const { parent, lastKey } = ensureDeepContainer(obj, segs.slice(1)); let arr = parent[lastKey]; if (!Array.isArray(arr)) { arr = arr != null ? [arr] : []; } try { if (globalThis.LWB_Guard?.validate) { const g = globalThis.LWB_Guard.validate('push', absPath, vParsed); if (!g?.allow) return ''; } } catch {} arr.push(vParsed); parent[lastKey] = arr; setLocalVariable(rootName, safeJSONStringify(obj)); return String(arr.length); } catch { return ''; } } function registerXbGetVarSlashCommand() { try { const ctx = getContext(); const { SlashCommandParser, SlashCommand, SlashCommandArgument, ARGUMENT_TYPE } = ctx || {}; if (!SlashCommandParser?.addCommandObject || !SlashCommand?.fromProps || !SlashCommandArgument?.fromProps) { return; } SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'xbgetvar', returns: 'string', helpString: `
/xbgetvar 人物状态.姓名
/xbgetvar A[0].name | /echo {{pipe}}
`,
unnamedArgumentList: [
SlashCommandArgument.fromProps({
description: '变量路径',
typeList: [ARGUMENT_TYPE.STRING],
isRequired: true,
acceptsMultiple: false,
}),
],
callback: (namedArgs, unnamedArgs) => {
try {
const path = extractPathFromArgs(namedArgs, unnamedArgs);
return lwbResolveVarPath(String(path || ''));
} catch {
return '';
}
},
}));
} catch {}
}
function registerXbSetVarSlashCommand() {
try {
const ctx = getContext();
const { SlashCommandParser, SlashCommand, SlashCommandArgument, ARGUMENT_TYPE } = ctx || {};
if (!SlashCommandParser?.addCommandObject || !SlashCommand?.fromProps || !SlashCommandArgument?.fromProps) {
return;
}
function joinUnnamed(args) {
if (Array.isArray(args)) {
return args.filter(v => v != null).map(v => String(v)).join(' ').trim();
}
return String(args ?? '').trim();
}
function splitTokensBySpace(s) {
return String(s || '').split(/\s+/).filter(Boolean);
}
function isDirectiveToken(tok) {
const t = String(tok || '').trim();
if (!t) return false;
if (['$ro', '$ext', '$prune', '$free', '$grow', '$shrink', '$list', '$clear'].includes(t)) {
return true;
}
if (/^\$(min|max|range|enum|match|step)=/.test(t)) {
return true;
}
return false;
}
function parseKeyAndValue(namedArgs, unnamedArgs) {
const unnamedJoined = joinUnnamed(unnamedArgs);
const hasNamedKey = typeof namedArgs?.key === 'string' && namedArgs.key.trim().length > 0;
if (hasNamedKey) {
const keyRaw = namedArgs.key.trim();
const keyParts = splitTokensBySpace(keyRaw);
if (keyParts.length > 1 && keyParts.every((p, i) =>
isDirectiveToken(p) || i === keyParts.length - 1
)) {
const directives = keyParts.slice(0, -1);
const realPath = keyParts[keyParts.length - 1];
return { directives, realPath, valueText: unnamedJoined };
}
if (isDirectiveToken(keyRaw)) {
const m = unnamedJoined.match(/^\S+/);
const realPath = m ? m[0] : '';
const valueText = realPath ? unnamedJoined.slice(realPath.length).trim() : '';
return { directives: [keyRaw], realPath, valueText };
}
return { directives: [], realPath: keyRaw, valueText: unnamedJoined };
}
const firstRaw = joinUnnamed(unnamedArgs);
if (!firstRaw) return { directives: [], realPath: '', valueText: '' };
const sp = lwbSplitPathAndValue(firstRaw);
let head = String(sp.path || '').trim();
let rest = String(sp.value || '').trim();
const parts = splitTokensBySpace(head);
if (parts.length > 1 && parts.every((p, i) =>
isDirectiveToken(p) || i === parts.length - 1
)) {
const directives = parts.slice(0, -1);
const realPath = parts[parts.length - 1];
return { directives, realPath, valueText: rest };
}
if (isDirectiveToken(head)) {
const m = rest.match(/^\S+/);
const realPath = m ? m[0] : '';
const valueText = realPath ? rest.slice(realPath.length).trim() : '';
return { directives: [head], realPath, valueText };
}
return { directives: [], realPath: head, valueText: rest };
}
SlashCommandParser.addCommandObject(SlashCommand.fromProps({
name: 'xbsetvar',
returns: 'string',
helpString: `
/xbsetvar A.B.C 123
/xbsetvar key="$list 情节小结" ["item1"]
`,
unnamedArgumentList: [
SlashCommandArgument.fromProps({
description: '变量路径或(指令 + 路径)',
typeList: [ARGUMENT_TYPE.STRING],
isRequired: true,
acceptsMultiple: false,
}),
SlashCommandArgument.fromProps({
description: '要设置的值',
typeList: [ARGUMENT_TYPE.STRING],
isRequired: false,
acceptsMultiple: false,
}),
],
callback: (namedArgs, unnamedArgs) => {
try {
const parsed = parseKeyAndValue(namedArgs, unnamedArgs);
const directives = parsed.directives || [];
const realPath = String(parsed.realPath || '').trim();
let rest = String(parsed.valueText || '').trim();
if (!realPath) return '';
if (directives.length > 0 && globalThis.LWB_Guard?.applyDelta) {
const delta = parseDirectivesTokenList(directives);
const absPath = normalizePath(realPath);
globalThis.LWB_Guard.applyDelta(absPath, delta);
globalThis.LWB_Guard.save?.();
}
let toSet = rest;
try {
const parsedVal = parseValueForSet(rest);
if (parsedVal && typeof parsedVal === 'object' && !Array.isArray(parsedVal)) {
const expanded = expandShorthandRuleObject(realPath, parsedVal);
if (expanded && typeof expanded === 'object') {
toSet = safeJSONStringify(expanded) || rest;
}
}
} catch {}
lwbAssignVarPath(realPath, toSet);
return '';
} catch {
return '';
}
},
}));
} catch {}
}
function registerXbAddVarSlashCommand() {
try {
const ctx = getContext();
const { SlashCommandParser, SlashCommand, SlashCommandArgument, SlashCommandNamedArgument, ARGUMENT_TYPE } = ctx || {};
if (!SlashCommandParser?.addCommandObject || !SlashCommand?.fromProps || !SlashCommandArgument?.fromProps) {
return;
}
SlashCommandParser.addCommandObject(SlashCommand.fromProps({
name: 'xbaddvar',
returns: 'string',
helpString: `
/xbaddvar key=人物状态.金币 100
/xbaddvar A.B.count 1
/xbaddvar 名字 _后缀
`,
namedArgumentList: [
SlashCommandNamedArgument.fromProps({
name: 'key',
description: '变量路径',
typeList: [ARGUMENT_TYPE.STRING],
isRequired: false,
}),
],
unnamedArgumentList: [
SlashCommandArgument.fromProps({
description: '路径+增量 或 仅增量',
typeList: [ARGUMENT_TYPE.STRING],
isRequired: true,
}),
],
callback: (namedArgs, unnamedArgs) => {
try {
let path, increment;
const unnamedJoined = Array.isArray(unnamedArgs)
? unnamedArgs.filter(v => v != null).map(String).join(' ').trim()
: String(unnamedArgs ?? '').trim();
if (namedArgs?.key && String(namedArgs.key).trim()) {
path = String(namedArgs.key).trim();
increment = unnamedJoined;
} else {
const sp = lwbSplitPathAndValue(unnamedJoined);
path = sp.path;
increment = sp.value;
}
if (!path) return '';
return lwbAddVarPath(path, increment);
} catch {
return '';
}
},
}));
} catch {}
}
function registerXbDelVarSlashCommand() {
try {
const ctx = getContext();
const { SlashCommandParser, SlashCommand, SlashCommandArgument, ARGUMENT_TYPE } = ctx || {};
if (!SlashCommandParser?.addCommandObject || !SlashCommand?.fromProps || !SlashCommandArgument?.fromProps) {
return;
}
SlashCommandParser.addCommandObject(SlashCommand.fromProps({
name: 'xbdelvar',
returns: 'string',
helpString: `
/xbdelvar 临时变量
/xbdelvar 角色状态.临时buff
/xbdelvar 背包[0]
`,
unnamedArgumentList: [
SlashCommandArgument.fromProps({
description: '变量路径',
typeList: [ARGUMENT_TYPE.STRING],
isRequired: true,
}),
],
callback: (namedArgs, unnamedArgs) => {
try {
const path = extractPathFromArgs(namedArgs, unnamedArgs);
if (!path) return '';
return lwbDeleteVarPath(path);
} catch {
return '';
}
},
}));
} catch {}
}
function registerXbPushVarSlashCommand() {
try {
const ctx = getContext();
const { SlashCommandParser, SlashCommand, SlashCommandArgument, SlashCommandNamedArgument, ARGUMENT_TYPE } = ctx || {};
if (!SlashCommandParser?.addCommandObject || !SlashCommand?.fromProps || !SlashCommandArgument?.fromProps) {
return;
}
SlashCommandParser.addCommandObject(SlashCommand.fromProps({
name: 'xbpushvar',
returns: 'string',
helpString: `
/xbpushvar key=背包 苹果
/xbpushvar 角色.技能列表 火球术
`,
namedArgumentList: [
SlashCommandNamedArgument.fromProps({
name: 'key',
description: '数组路径',
typeList: [ARGUMENT_TYPE.STRING],
isRequired: false,
}),
],
unnamedArgumentList: [
SlashCommandArgument.fromProps({
description: '路径+值 或 仅值',
typeList: [ARGUMENT_TYPE.STRING],
isRequired: true,
}),
],
callback: (namedArgs, unnamedArgs) => {
try {
let path, value;
const unnamedJoined = Array.isArray(unnamedArgs)
? unnamedArgs.filter(v => v != null).map(String).join(' ').trim()
: String(unnamedArgs ?? '').trim();
if (namedArgs?.key && String(namedArgs.key).trim()) {
path = String(namedArgs.key).trim();
value = unnamedJoined;
} else {
const sp = lwbSplitPathAndValue(unnamedJoined);
path = sp.path;
value = sp.value;
}
if (!path) return '';
return lwbPushVarPath(path, value);
} catch {
return '';
}
},
}));
} catch {}
}
function onMessageRendered(data) {
try {
if (globalThis.LWB_Guard?.validate) return;
const id = typeof data === 'object' && data !== null
? (data.messageId ?? data.id ?? data)
: data;
if (typeof id === 'number') {
applyXbGetVarForMessage(id, true);
}
} catch {}
}
export function initVarCommands() {
if (initialized) return;
initialized = true;
events = createModuleEvents(MODULE_ID);
registerXbGetVarSlashCommand();
registerXbSetVarSlashCommand();
registerXbAddVarSlashCommand();
registerXbDelVarSlashCommand();
registerXbPushVarSlashCommand();
events.on(event_types.USER_MESSAGE_RENDERED, onMessageRendered);
events.on(event_types.CHARACTER_MESSAGE_RENDERED, onMessageRendered);
events.on(event_types.MESSAGE_UPDATED, onMessageRendered);
events.on(event_types.MESSAGE_EDITED, onMessageRendered);
events.on(event_types.MESSAGE_SWIPED, onMessageRendered);
}
export function cleanupVarCommands() {
if (!initialized) return;
events?.cleanup();
events = null;
initialized = false;
}
export {
MODULE_ID,
};