refactor(parser): import only the required asset when in a node environment (#745)

# PR Checklist
- [X] Have you checked if it works normally in all models? *Ignore this
if it doesn't use models.*
- [X] Have you checked if it works normally in all web, local, and node
hosted versions? If it doesn't, have you blocked it in those versions?
- [ ] Have you added type definitions?

# Description
This PR contains modifications to import only the required assets when
talking to Asset Bot.

- refactor parseAdditionalAssets()
- Modified the getClostMatch() to work well with Object<key, value> to
which it is dynamically added.
This commit is contained in:
kwaroran
2025-02-09 16:23:10 +09:00
committed by GitHub

View File

@@ -292,9 +292,38 @@ async function renderHighlightableMarkdown(data:string) {
} }
export const assetRegex = /{{(raw|path|img|image|video|audio|bg|emotion|asset|video-img|source)::(.+?)}}/gms export const assetRegex = /{{(raw|path|img|image|video|audio|bg|emotion|asset|video-img|source)::(.+?)}}/gms
async function replaceAsync(string, regexp, replacerFunction) {
const replacements = await Promise.all(
Array.from(string.matchAll(regexp),
match => replacerFunction(...match as any)))
let i = 0;
return string.replace(regexp, () => replacements[i++])
}
async function getAssetSrc(assetArr: string[][], name: string, assetPaths: {[key: string]:{path: string, ext?: string}}) {
name = name.toLocaleLowerCase()
for (const asset of assetArr) {
if (trimmer(asset[0].toLocaleLowerCase()) !== trimmer(name)) continue
const assetPath = await getFileSrc(asset[1])
assetPaths[asset[0].toLocaleLowerCase()] = {
path: assetPath,
ext: asset[2]
}
return
}
}
async function getEmoSrc(emoArr: string[][], emoPaths: {[key: string]:{path: string}}) {
for (const emo of emoArr) {
const emoPath = await getFileSrc(emo[1])
emoPaths[emo[0].toLocaleLowerCase()] = {
path: emoPath,
}
}
}
async function parseAdditionalAssets(data:string, char:simpleCharacterArgument|character, mode:'normal'|'back', mode2:'unset'|'pre'|'post' = 'unset'){ async function parseAdditionalAssets(data:string, char:simpleCharacterArgument|character, mode:'normal'|'back', mode2:'unset'|'pre'|'post' = 'unset'){
const assetWidthString = (DBState.db.assetWidth && DBState.db.assetWidth !== -1 || DBState.db.assetWidth === 0) ? `max-width:${DBState.db.assetWidth}rem;` : '' const assetWidthString = (DBState.db.assetWidth && DBState.db.assetWidth !== -1 || DBState.db.assetWidth === 0) ? `max-width:${DBState.db.assetWidth}rem;` : ''
@@ -306,37 +335,20 @@ async function parseAdditionalAssets(data:string, char:simpleCharacterArgument|c
path:string path:string
}} = {} }} = {}
if(char.additionalAssets){ if (char.emotionImages) await getEmoSrc(char.emotionImages, emoPaths)
for(const asset of char.additionalAssets){
const assetPath = await getFileSrc(asset[1])
assetPaths[asset[0].toLocaleLowerCase()] = {
path: assetPath,
ext: asset[2]
}
}
}
if(char.emotionImages){
for(const emo of char.emotionImages){
const emoPath = await getFileSrc(emo[1])
emoPaths[emo[0].toLocaleLowerCase()] = {
path: emoPath,
}
}
}
const moduleAssets = getModuleAssets()
if(moduleAssets.length > 0){
for(const asset of moduleAssets){
const assetPath = await getFileSrc(asset[1])
assetPaths[asset[0].toLocaleLowerCase()] = {
path: assetPath,
ext: asset[2]
}
}
}
const videoExtention = ['mp4', 'webm', 'avi', 'm4p', 'm4v'] const videoExtention = ['mp4', 'webm', 'avi', 'm4p', 'm4v']
let needsSourceAccess = false let needsSourceAccess = false
data = data.replaceAll(assetRegex, (full:string, type:string, name:string) => {
name = name.toLocaleLowerCase() data = await replaceAsync(data, assetRegex, async (full:string, type:string, name:string) => {
const moduleAssets = getModuleAssets()
if (char.additionalAssets) {
await getAssetSrc(char.additionalAssets, name, assetPaths)
}
if (moduleAssets.length > 0) {
await getAssetSrc(moduleAssets, name, assetPaths)
}
if(type === 'emotion'){ if(type === 'emotion'){
const path = emoPaths[name]?.path const path = emoPaths[name]?.path
if(!path){ if(!path){
@@ -344,6 +356,7 @@ async function parseAdditionalAssets(data:string, char:simpleCharacterArgument|c
} }
return `<img src="${path}" alt="${path}" style="${assetWidthString} "/>` return `<img src="${path}" alt="${path}" style="${assetWidthString} "/>`
} }
if(type === 'source'){ if(type === 'source'){
needsSourceAccess = true needsSourceAccess = true
switch(name){ switch(name){
@@ -355,13 +368,15 @@ async function parseAdditionalAssets(data:string, char:simpleCharacterArgument|c
} }
} }
} }
let path = assetPaths[name] let path = assetPaths[name]
if(!path){ if(!path){
if(DBState.db.legacyMediaFindings){ if(DBState.db.legacyMediaFindings){
return '' return ''
} }
path = getClosestMatch(name, assetPaths) path = await getClosestMatch(char, name, assetPaths)
if(!path){ if(!path){
return '' return ''
@@ -411,14 +426,41 @@ async function parseAdditionalAssets(data:string, char:simpleCharacterArgument|c
return data return data
} }
function getClosestMatch(name:string, assetPaths:{[key:string]:{path:string, ext?:string}}){ async function getClosestMatch(char: simpleCharacterArgument|character, name:string, assetPaths:{[key:string]:{path:string, ext?:string}}){
if(!char.additionalAssets) return null
if(Object.keys(assetPaths).length === 0){ let closest = ''
let closestDist = 999999
let targetPath = ''
let targetExt = ''
const trimmedName = trimmer(name)
for(const asset of char.additionalAssets) {
const key = asset[0].toLocaleLowerCase()
const dist = getDistance(trimmedName, trimmer(key))
if(dist < closestDist){
closest = key
closestDist = dist
targetPath = asset[1]
targetExt = asset[2]
}
}
if(closestDist > DBState.db.assetMaxDifference){
return null return null
} }
const assetPath = await getFileSrc(targetPath)
assetPaths[closest] = {
path: assetPath,
ext: targetExt
}
return assetPaths[closest]
}
//Levenshtein distance, new with 1d array //Levenshtein distance, new with 1d array
const dest = (a:string, b:string) => { function getDistance(a:string, b:string) {
const h = a.length + 1 const h = a.length + 1
const w = b.length + 1 const w = b.length + 1
let d = new Int16Array(h * w) let d = new Int16Array(h * w)
@@ -437,7 +479,6 @@ function getClosestMatch(name:string, assetPaths:{[key:string]:{path:string, ext
} }
} }
return d[h * w - 1] return d[h * w - 1]
} }
function trimmer(str:string){ function trimmer(str:string){
@@ -448,26 +489,9 @@ function getClosestMatch(name:string, assetPaths:{[key:string]:{path:string, ext
} }
} }
return str.trim().replace(/[_ -.]/g, '') return str.trim().replace(/[_ -.]/g, '')
} }
let closest = ''
let closestDist = 999999
const trimmedName = trimmer(name)
for(const key in assetPaths){
const dist = dest(trimmedName, trimmer(key))
if(dist < closestDist){
closest = key
closestDist = dist
}
}
if(closestDist > DBState.db.assetMaxDifference){
return null
}
return assetPaths[closest]
}
async function parseInlayAssets(data:string){ async function parseInlayAssets(data:string){
const inlayMatch = data.match(/{{(inlay|inlayed)::(.+?)}}/g) const inlayMatch = data.match(/{{(inlay|inlayed)::(.+?)}}/g)
if(inlayMatch){ if(inlayMatch){
@@ -605,7 +629,6 @@ function decodeStyleRule(rule:CssAtRuleAST){
} }
function decodeStyle(text:string){ function decodeStyle(text:string){
return text.replaceAll(styleDecodeRegex, (full, txt:string) => { return text.replaceAll(styleDecodeRegex, (full, txt:string) => {
try { try {
let text = Buffer.from(txt, 'hex').toString('utf-8') let text = Buffer.from(txt, 'hex').toString('utf-8')