Add support for characterFilter to getEntryField and setEntryField (#4185)
* Add support for characterFilter to getEntryField and setEntryField * No more conflicts between lint and autoformat * Fix array type default values hint * Properly capitalize name * Fix search by name --------- Co-authored-by: Cohee <18619528+Cohee1207@users.noreply.github.com>
This commit is contained in:
@@ -2367,7 +2367,7 @@ export function findChar({ name = null, allowAvatar = true, insensitive = true,
|
||||
|
||||
// If allowAvatar is true, search by avatar first
|
||||
if (allowAvatar && name) {
|
||||
const characterByAvatar = filteredCharacters.find(char => char.avatar === name);
|
||||
const characterByAvatar = filteredCharacters.find(char => char.avatar === name || (!name.endsWith('.png') && char.avatar === `${name}.png`));
|
||||
if (characterByAvatar) {
|
||||
return characterByAvatar;
|
||||
}
|
||||
|
||||
@@ -1124,6 +1124,7 @@ function registerWorldInfoSlashCommands() {
|
||||
async function getEntryFieldCallback(args, uid) {
|
||||
const file = args.file;
|
||||
const field = args.field || 'content';
|
||||
const tags = getContext().tags;
|
||||
|
||||
const entries = await getEntriesFromFile(file);
|
||||
|
||||
@@ -1143,7 +1144,31 @@ function registerWorldInfoSlashCommands() {
|
||||
return '';
|
||||
}
|
||||
|
||||
const fieldValue = entry[field];
|
||||
// handle special cases, otherwise execute default logic
|
||||
let fieldValue;
|
||||
switch (field){
|
||||
case 'characterFilterNames':
|
||||
if (entry.characterFilter) {
|
||||
fieldValue = entry.characterFilter.names;
|
||||
}
|
||||
break;
|
||||
case 'characterFilterTags':
|
||||
if (entry.characterFilter) {
|
||||
if (!entry.characterFilter.tags) {
|
||||
return '';
|
||||
}
|
||||
//Find the tag objects corresponding to each ID in the array, then return the names
|
||||
fieldValue = tags.filter((tag) => entry.characterFilter.tags.includes(tag.id)).map((tag) => tag.name);
|
||||
}
|
||||
break;
|
||||
case 'characterFilterExclude':
|
||||
if (entry.characterFilter) {
|
||||
fieldValue = entry.characterFilter.isExclude;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
fieldValue = entry[field];
|
||||
}
|
||||
|
||||
if (fieldValue === undefined) {
|
||||
return '';
|
||||
@@ -1189,6 +1214,23 @@ function registerWorldInfoSlashCommands() {
|
||||
const file = args.file;
|
||||
const uid = args.uid;
|
||||
const field = args.field || 'content';
|
||||
const tags = getContext().tags;
|
||||
|
||||
// characterFilter is an object with internal fields we need to access, which may also may be null and need to be populated
|
||||
const createCharacterFilterFieldObjectIfNeeded = (currentEntry) => {
|
||||
if (!currentEntry.characterFilter) {
|
||||
Object.assign(
|
||||
currentEntry,
|
||||
{
|
||||
characterFilter: {
|
||||
isExclude: false,
|
||||
names: [],
|
||||
tags: [],
|
||||
},
|
||||
},
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
if (value === undefined) {
|
||||
toastr.warning('Value is required');
|
||||
@@ -1216,18 +1258,45 @@ function registerWorldInfoSlashCommands() {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (Array.isArray(entry[field])) {
|
||||
entry[field] = parseStringArray(value);
|
||||
} else if (typeof entry[field] === 'boolean') {
|
||||
entry[field] = isTrueBoolean(value);
|
||||
} else if (typeof entry[field] === 'number') {
|
||||
entry[field] = Number(value);
|
||||
} else {
|
||||
entry[field] = value;
|
||||
}
|
||||
// handle special cases, otherwise execute default logic
|
||||
let tagNames;
|
||||
let charNames;
|
||||
switch (field){
|
||||
case 'characterFilterNames':
|
||||
createCharacterFilterFieldObjectIfNeeded(entry);
|
||||
charNames = parseStringArray(value);
|
||||
entry.characterFilter.names = charNames
|
||||
.map((name) => getCharaFilename(null, { manualAvatarKey: findChar({ name, allowAvatar: true, preferCurrentChar: false, quiet: true })?.avatar }))
|
||||
.filter(Boolean)
|
||||
.filter(onlyUnique);
|
||||
setWIOriginalDataValue(data, uid, 'character_filter', entry.characterFilter);
|
||||
break;
|
||||
case 'characterFilterTags':
|
||||
createCharacterFilterFieldObjectIfNeeded(entry);
|
||||
tagNames = parseStringArray(value);
|
||||
//Find the tag objects corresponding to each name in the user array, then return an array of the corresponding IDs
|
||||
entry.characterFilter.tags = tags.filter((tag) => tagNames.includes(tag.name)).map((tag) => tag.id);
|
||||
setWIOriginalDataValue(data, uid, 'character_filter', entry.characterFilter);
|
||||
break;
|
||||
case 'characterFilterExclude':
|
||||
createCharacterFilterFieldObjectIfNeeded(entry);
|
||||
entry.characterFilter.isExclude = isTrueBoolean(value);
|
||||
setWIOriginalDataValue(data, uid, 'character_filter', entry.characterFilter);
|
||||
break;
|
||||
default:
|
||||
if (Array.isArray(entry[field])) {
|
||||
entry[field] = parseStringArray(value);
|
||||
} else if (typeof entry[field] === 'boolean') {
|
||||
entry[field] = isTrueBoolean(value);
|
||||
} else if (typeof entry[field] === 'number') {
|
||||
entry[field] = Number(value);
|
||||
} else {
|
||||
entry[field] = value;
|
||||
}
|
||||
|
||||
if (originalWIDataKeyMap[field]) {
|
||||
setWIOriginalDataValue(data, uid, originalWIDataKeyMap[field], entry[field]);
|
||||
if (originalWIDataKeyMap[field]) {
|
||||
setWIOriginalDataValue(data, uid, originalWIDataKeyMap[field], entry[field]);
|
||||
}
|
||||
}
|
||||
|
||||
await saveWorldInfo(file, data);
|
||||
@@ -1349,7 +1418,7 @@ function registerWorldInfoSlashCommands() {
|
||||
const localEnumProviders = {
|
||||
/** All possible fields that can be set in a WI entry */
|
||||
wiEntryFields: () => Object.entries(newWorldInfoEntryDefinition).map(([key, value]) =>
|
||||
new SlashCommandEnumValue(key, `[${value.type}] default: ${(typeof value.default === 'string' ? `'${value.default}'` : value.default)}`,
|
||||
new SlashCommandEnumValue(key, `[${value.type}] default: ${(typeof value.default === 'string' ? `'${value.default}'` : JSON.stringify(value.default))}`,
|
||||
enumTypes.enum, enumIcons.getDataTypeIcon(value.type))),
|
||||
|
||||
/** All existing UIDs based on the file argument as world name */
|
||||
@@ -3578,6 +3647,9 @@ export const newWorldInfoEntryDefinition = {
|
||||
sticky: { default: null, type: 'number?' },
|
||||
cooldown: { default: null, type: 'number?' },
|
||||
delay: { default: null, type: 'number?' },
|
||||
characterFilterNames: { default: [], type: 'array' },
|
||||
characterFilterTags: { default: [], type: 'array' },
|
||||
characterFilterExclude: { default: false, type: 'boolean' },
|
||||
};
|
||||
|
||||
export const newWorldInfoEntryTemplate = Object.fromEntries(
|
||||
|
||||
@@ -863,8 +863,7 @@ router.post('/search', validateAvatarUrlMiddleware, function (request, response)
|
||||
|
||||
// Search through title and messages of the chat
|
||||
const fragments = query.trim().toLowerCase().split(/\s+/).filter(x => x);
|
||||
const text = [path.parse(chatFile.path).name,
|
||||
...messages.map(message => message?.mes)].join('\n').toLowerCase();
|
||||
const text = [path.parse(chatFile.path).name, ...messages.map(message => message?.mes)].join('\n').toLowerCase();
|
||||
const hasMatch = fragments.every(fragment => text.includes(fragment));
|
||||
|
||||
if (hasMatch) {
|
||||
|
||||
Reference in New Issue
Block a user