|
import { characters, substituteParams, substituteParamsExtended, this_chid } from '../../../script.js'; |
|
import { extension_settings } from '../../extensions.js'; |
|
import { regexFromString } from '../../utils.js'; |
|
export { |
|
regex_placement, |
|
getRegexedString, |
|
runRegexScript, |
|
}; |
|
|
|
|
|
|
|
|
|
const regex_placement = { |
|
|
|
|
|
|
|
MD_DISPLAY: 0, |
|
USER_INPUT: 1, |
|
AI_OUTPUT: 2, |
|
SLASH_COMMAND: 3, |
|
|
|
WORLD_INFO: 5, |
|
REASONING: 6, |
|
}; |
|
|
|
export const substitute_find_regex = { |
|
NONE: 0, |
|
RAW: 1, |
|
ESCAPED: 2, |
|
}; |
|
|
|
function sanitizeRegexMacro(x) { |
|
return (x && typeof x === 'string') ? |
|
x.replaceAll(/[\n\r\t\v\f\0.^$*+?{}[\]\\/|()]/gs, function (s) { |
|
switch (s) { |
|
case '\n': |
|
return '\\n'; |
|
case '\r': |
|
return '\\r'; |
|
case '\t': |
|
return '\\t'; |
|
case '\v': |
|
return '\\v'; |
|
case '\f': |
|
return '\\f'; |
|
case '\0': |
|
return '\\0'; |
|
default: |
|
return '\\' + s; |
|
} |
|
}) : x; |
|
} |
|
|
|
function getScopedRegex() { |
|
const isAllowed = extension_settings?.character_allowed_regex?.includes(characters?.[this_chid]?.avatar); |
|
|
|
if (!isAllowed) { |
|
return []; |
|
} |
|
|
|
const scripts = characters[this_chid]?.data?.extensions?.regex_scripts; |
|
|
|
if (!Array.isArray(scripts)) { |
|
return []; |
|
} |
|
|
|
return scripts; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function getRegexedString(rawString, placement, { characterOverride, isMarkdown, isPrompt, isEdit, depth } = {}) { |
|
|
|
if (typeof rawString !== 'string') { |
|
console.warn('getRegexedString: rawString is not a string. Returning empty string.'); |
|
return ''; |
|
} |
|
|
|
let finalString = rawString; |
|
if (extension_settings.disabledExtensions.includes('regex') || !rawString || placement === undefined) { |
|
return finalString; |
|
} |
|
|
|
const allRegex = [...(extension_settings.regex ?? []), ...(getScopedRegex() ?? [])]; |
|
allRegex.forEach((script) => { |
|
if ( |
|
|
|
(script.markdownOnly && isMarkdown) || |
|
|
|
(script.promptOnly && isPrompt) || |
|
|
|
(!script.markdownOnly && !script.promptOnly && !isMarkdown && !isPrompt) |
|
) { |
|
if (isEdit && !script.runOnEdit) { |
|
console.debug(`getRegexedString: Skipping script ${script.scriptName} because it does not run on edit`); |
|
return; |
|
} |
|
|
|
|
|
if (typeof depth === 'number') { |
|
if (!isNaN(script.minDepth) && script.minDepth !== null && script.minDepth >= -1 && depth < script.minDepth) { |
|
console.debug(`getRegexedString: Skipping script ${script.scriptName} because depth ${depth} is less than minDepth ${script.minDepth}`); |
|
return; |
|
} |
|
|
|
if (!isNaN(script.maxDepth) && script.maxDepth !== null && script.maxDepth >= 0 && depth > script.maxDepth) { |
|
console.debug(`getRegexedString: Skipping script ${script.scriptName} because depth ${depth} is greater than maxDepth ${script.maxDepth}`); |
|
return; |
|
} |
|
} |
|
|
|
if (script.placement.includes(placement)) { |
|
finalString = runRegexScript(script, finalString, { characterOverride }); |
|
} |
|
} |
|
}); |
|
|
|
return finalString; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function runRegexScript(regexScript, rawString, { characterOverride } = {}) { |
|
let newString = rawString; |
|
if (!regexScript || !!(regexScript.disabled) || !regexScript?.findRegex || !rawString) { |
|
return newString; |
|
} |
|
|
|
const getRegexString = () => { |
|
switch (Number(regexScript.substituteRegex)) { |
|
case substitute_find_regex.NONE: |
|
return regexScript.findRegex; |
|
case substitute_find_regex.RAW: |
|
return substituteParamsExtended(regexScript.findRegex); |
|
case substitute_find_regex.ESCAPED: |
|
return substituteParamsExtended(regexScript.findRegex, {}, sanitizeRegexMacro); |
|
default: |
|
console.warn(`runRegexScript: Unknown substituteRegex value ${regexScript.substituteRegex}. Using raw regex.`); |
|
return regexScript.findRegex; |
|
} |
|
}; |
|
const regexString = getRegexString(); |
|
const findRegex = regexFromString(regexString); |
|
|
|
|
|
if (!findRegex) { |
|
return newString; |
|
} |
|
|
|
|
|
newString = rawString.replace(findRegex, function (match) { |
|
const args = [...arguments]; |
|
const replaceString = regexScript.replaceString.replace(/{{match}}/gi, '$0'); |
|
const replaceWithGroups = replaceString.replaceAll(/\$(\d+)/g, (_, num) => { |
|
|
|
const match = args[Number(num)]; |
|
|
|
|
|
if (!match) { |
|
return ''; |
|
} |
|
|
|
|
|
const filteredMatch = filterString(match, regexScript.trimStrings, { characterOverride }); |
|
|
|
|
|
|
|
return filteredMatch; |
|
}); |
|
|
|
|
|
return substituteParams(replaceWithGroups); |
|
}); |
|
|
|
return newString; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function filterString(rawString, trimStrings, { characterOverride } = {}) { |
|
let finalString = rawString; |
|
trimStrings.forEach((trimString) => { |
|
const subTrimString = substituteParams(trimString, undefined, characterOverride); |
|
finalString = finalString.replaceAll(subTrimString, ''); |
|
}); |
|
|
|
return finalString; |
|
} |
|
|