Refactor findChar to utils - Refactor and move finChar to utils, instead of slashcommands function - Refactor scrapers to use actual init functionality
| @@ -230,7 +230,7 @@ import { MacrosParser, evaluateMacros, getLastMessageId } from './scripts/macros | |||
| 230 | import { currentUser, setUserControls } from './scripts/user.js'; | 230 | import { currentUser, setUserControls } from './scripts/user.js'; |
| 231 | import { POPUP_RESULT, POPUP_TYPE, Popup, callGenericPopup, fixToastrForDialogs } from './scripts/popup.js'; | 231 | import { POPUP_RESULT, POPUP_TYPE, Popup, callGenericPopup, fixToastrForDialogs } from './scripts/popup.js'; |
| 232 | import { renderTemplate, renderTemplateAsync } from './scripts/templates.js'; | 232 | import { renderTemplate, renderTemplateAsync } from './scripts/templates.js'; |
| 233 | import { ScraperManager } from './scripts/scrapers.js'; | 233 | import { initScrapers, ScraperManager } from './scripts/scrapers.js'; |
| 234 | import { SlashCommandParser } from './scripts/slash-commands/SlashCommandParser.js'; | 234 | import { SlashCommandParser } from './scripts/slash-commands/SlashCommandParser.js'; |
| 235 | import { SlashCommand } from './scripts/slash-commands/SlashCommand.js'; | 235 | import { SlashCommand } from './scripts/slash-commands/SlashCommand.js'; |
| 236 | import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from './scripts/slash-commands/SlashCommandArgument.js'; | 236 | import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from './scripts/slash-commands/SlashCommandArgument.js'; |
| @@ -959,6 +959,7 @@ async function firstLoadInit() { | |||
| 959 | initCfg(); | 959 | initCfg(); |
| 960 | initLogprobs(); | 960 | initLogprobs(); |
| 961 | initInputMarkdown(); | 961 | initInputMarkdown(); |
| 962 | initScrapers(); | ||
| 962 | doDailyExtensionUpdatesCheck(); | 963 | doDailyExtensionUpdatesCheck(); |
| 963 | await hideLoader(); | 964 | await hideLoader(); |
| 964 | await fixViewport(); | 965 | await fixViewport(); |
| @@ -2,7 +2,7 @@ import { callPopup, eventSource, event_types, generateRaw, getRequestHeaders, ma | |||
| 2 | import { dragElement, isMobile } from '../../RossAscends-mods.js'; | 2 | import { dragElement, isMobile } from '../../RossAscends-mods.js'; |
| 3 | import { getContext, getApiUrl, modules, extension_settings, ModuleWorkerWrapper, doExtrasFetch, renderExtensionTemplateAsync } from '../../extensions.js'; | 3 | import { getContext, getApiUrl, modules, extension_settings, ModuleWorkerWrapper, doExtrasFetch, renderExtensionTemplateAsync } from '../../extensions.js'; |
| 4 | import { loadMovingUIState, power_user } from '../../power-user.js'; | 4 | import { loadMovingUIState, power_user } from '../../power-user.js'; |
| 5 | import { onlyUnique, debounce, getCharaFilename, trimToEndSentence, trimToStartSentence, waitUntilCondition } from '../../utils.js'; | 5 | import { onlyUnique, debounce, getCharaFilename, trimToEndSentence, trimToStartSentence, waitUntilCondition, findChar } from '../../utils.js'; |
| 6 | import { hideMutedSprites } from '../../group-chats.js'; | 6 | import { hideMutedSprites } from '../../group-chats.js'; |
| 7 | import { isJsonSchemaSupported } from '../../textgen-settings.js'; | 7 | import { isJsonSchemaSupported } from '../../textgen-settings.js'; |
| 8 | import { debounce_timeout } from '../../constants.js'; | 8 | import { debounce_timeout } from '../../constants.js'; |
| @@ -12,7 +12,6 @@ import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from ' | |||
| 12 | import { isFunctionCallingSupported } from '../../openai.js'; | 12 | import { isFunctionCallingSupported } from '../../openai.js'; |
| 13 | import { SlashCommandEnumValue, enumTypes } from '../../slash-commands/SlashCommandEnumValue.js'; | 13 | import { SlashCommandEnumValue, enumTypes } from '../../slash-commands/SlashCommandEnumValue.js'; |
| 14 | import { commonEnumProviders } from '../../slash-commands/SlashCommandCommonEnumsProvider.js'; | 14 | import { commonEnumProviders } from '../../slash-commands/SlashCommandCommonEnumsProvider.js'; |
| 15 | import { findChar } from '../../slash-commands.js'; | ||
| 16 | export { MODULE_NAME }; | 15 | export { MODULE_NAME }; |
| 17 | 16 | ||
| 18 | const MODULE_NAME = 'expressions'; | 17 | const MODULE_NAME = 'expressions'; |
| @@ -13,6 +13,7 @@ import { isValidUrl } from './utils.js'; | |||
| 13 | * @property {string} description | 13 | * @property {string} description |
| 14 | * @property {string} iconClass | 14 | * @property {string} iconClass |
| 15 | * @property {boolean} iconAvailable | 15 | * @property {boolean} iconAvailable |
| 16 | * @property {() => Promise<void>} [init=null] | ||
| 16 | * @property {() => Promise<boolean>} isAvailable | 17 | * @property {() => Promise<boolean>} isAvailable |
| 17 | * @property {() => Promise<File[]>} scrape | 18 | * @property {() => Promise<File[]>} scrape |
| 18 | */ | 19 | */ |
| @@ -42,6 +43,10 @@ export class ScraperManager { | |||
| 42 | return; | 43 | return; |
| 43 | } | 44 | } |
| 44 | 45 | ||
| 46 | if (scraper.init) { | ||
| 47 | scraper.init(); | ||
| 48 | } | ||
| 49 | |||
| 45 | ScraperManager.#scrapers.push(scraper); | 50 | ScraperManager.#scrapers.push(scraper); |
| 46 | } | 51 | } |
| 47 | 52 | ||
| @@ -462,7 +467,9 @@ class YouTubeScraper { | |||
| 462 | this.description = 'Download a transcript from a YouTube video.'; | 467 | this.description = 'Download a transcript from a YouTube video.'; |
| 463 | this.iconClass = 'fa-brands fa-youtube'; | 468 | this.iconClass = 'fa-brands fa-youtube'; |
| 464 | this.iconAvailable = true; | 469 | this.iconAvailable = true; |
| 470 | } | ||
| 465 | 471 | ||
| 472 | async init() { | ||
| 466 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | 473 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| 467 | name: 'yt-script', | 474 | name: 'yt-script', |
| 468 | callback: async (args, url) => { | 475 | callback: async (args, url) => { |
| @@ -564,9 +571,11 @@ class YouTubeScraper { | |||
| 564 | } | 571 | } |
| 565 | } | 572 | } |
| 566 | 573 | ||
| 574 | export function initScrapers() { | ||
| 567 | ScraperManager.registerDataBankScraper(new FileScraper()); | 575 | ScraperManager.registerDataBankScraper(new FileScraper()); |
| 568 | ScraperManager.registerDataBankScraper(new Notepad()); | 576 | ScraperManager.registerDataBankScraper(new Notepad()); |
| 569 | ScraperManager.registerDataBankScraper(new WebScraper()); | 577 | ScraperManager.registerDataBankScraper(new WebScraper()); |
| 570 | ScraperManager.registerDataBankScraper(new MediaWikiScraper()); | 578 | ScraperManager.registerDataBankScraper(new MediaWikiScraper()); |
| 571 | ScraperManager.registerDataBankScraper(new FandomScraper()); | 579 | ScraperManager.registerDataBankScraper(new FandomScraper()); |
| 572 | ScraperManager.registerDataBankScraper(new YouTubeScraper()); | 580 | ScraperManager.registerDataBankScraper(new YouTubeScraper()); |
| 581 | } | ||
| @@ -55,7 +55,7 @@ import { autoSelectPersona, retriggerFirstMessageOnEmptyChat, setPersonaLockStat | |||
| 55 | import { addEphemeralStoppingString, chat_styles, flushEphemeralStoppingStrings, power_user } from './power-user.js'; | 55 | import { addEphemeralStoppingString, chat_styles, flushEphemeralStoppingStrings, power_user } from './power-user.js'; |
| 56 | import { SERVER_INPUTS, textgen_types, textgenerationwebui_settings } from './textgen-settings.js'; | 56 | import { SERVER_INPUTS, textgen_types, textgenerationwebui_settings } from './textgen-settings.js'; |
| 57 | import { decodeTextTokens, getAvailableTokenizers, getFriendlyTokenizerName, getTextTokens, getTokenCountAsync, selectTokenizer } from './tokenizers.js'; | 57 | import { decodeTextTokens, getAvailableTokenizers, getFriendlyTokenizerName, getTextTokens, getTokenCountAsync, selectTokenizer } from './tokenizers.js'; |
| 58 | import { debounce, delay, equalsIgnoreCaseAndAccents, isFalseBoolean, isTrueBoolean, showFontAwesomePicker, stringToRange, trimToEndSentence, trimToStartSentence, waitUntilCondition } from './utils.js'; | 58 | import { debounce, delay, equalsIgnoreCaseAndAccents, findChar, isFalseBoolean, isTrueBoolean, showFontAwesomePicker, stringToRange, trimToEndSentence, trimToStartSentence, waitUntilCondition } from './utils.js'; |
| 59 | import { registerVariableCommands, resolveVariable } from './variables.js'; | 59 | import { registerVariableCommands, resolveVariable } from './variables.js'; |
| 60 | import { background_settings } from './backgrounds.js'; | 60 | import { background_settings } from './backgrounds.js'; |
| 61 | import { SlashCommandClosure } from './slash-commands/SlashCommandClosure.js'; | 61 | import { SlashCommandClosure } from './slash-commands/SlashCommandClosure.js'; |
| @@ -68,10 +68,8 @@ import { SlashCommandNamedArgumentAssignment } from './slash-commands/SlashComma | |||
| 68 | import { SlashCommandEnumValue, enumTypes } from './slash-commands/SlashCommandEnumValue.js'; | 68 | import { SlashCommandEnumValue, enumTypes } from './slash-commands/SlashCommandEnumValue.js'; |
| 69 | import { POPUP_TYPE, Popup, callGenericPopup } from './popup.js'; | 69 | import { POPUP_TYPE, Popup, callGenericPopup } from './popup.js'; |
| 70 | import { commonEnumProviders, enumIcons } from './slash-commands/SlashCommandCommonEnumsProvider.js'; | 70 | import { commonEnumProviders, enumIcons } from './slash-commands/SlashCommandCommonEnumsProvider.js'; |
| 71 | import { SlashCommandDebugController } from './slash-commands/SlashCommandDebugController.js'; | ||
| 72 | import { SlashCommandBreakController } from './slash-commands/SlashCommandBreakController.js'; | 71 | import { SlashCommandBreakController } from './slash-commands/SlashCommandBreakController.js'; |
| 73 | import { SlashCommandExecutionError } from './slash-commands/SlashCommandExecutionError.js'; | 72 | import { SlashCommandExecutionError } from './slash-commands/SlashCommandExecutionError.js'; |
| 74 | import { getTagsList } from './tags.js'; | ||
| 75 | export { | 73 | export { |
| 76 | executeSlashCommands, executeSlashCommandsWithOptions, getSlashCommandsHelp, registerSlashCommand, | 74 | executeSlashCommands, executeSlashCommandsWithOptions, getSlashCommandsHelp, registerSlashCommand, |
| 77 | }; | 75 | }; |
| @@ -2969,29 +2967,6 @@ async function deleteMessagesByNameCallback(_, name) { | |||
| 2969 | return ''; | 2967 | return ''; |
| 2970 | } | 2968 | } |
| 2971 | 2969 | ||
| 2972 | function findCharacterIndex(name) { | ||
| 2973 | const matchTypes = [ | ||
| 2974 | (a, b) => a === b, | ||
| 2975 | (a, b) => a.startsWith(b), | ||
| 2976 | (a, b) => a.includes(b), | ||
| 2977 | ]; | ||
| 2978 | |||
| 2979 | const exactAvatarMatch = characters.findIndex(x => x.avatar === name); | ||
| 2980 | |||
| 2981 | if (exactAvatarMatch !== -1) { | ||
| 2982 | return exactAvatarMatch; | ||
| 2983 | } | ||
| 2984 | |||
| 2985 | for (const matchType of matchTypes) { | ||
| 2986 | const index = characters.findIndex(x => matchType(x.name.toLowerCase(), name.toLowerCase())); | ||
| 2987 | if (index !== -1) { | ||
| 2988 | return index; | ||
| 2989 | } | ||
| 2990 | } | ||
| 2991 | |||
| 2992 | return -1; | ||
| 2993 | } | ||
| 2994 | |||
| 2995 | async function goToCharacterCallback(_, name) { | 2970 | async function goToCharacterCallback(_, name) { |
| 2996 | if (!name) { | 2971 | if (!name) { |
| 2997 | console.warn('WARN: No character name provided for /go command'); | 2972 | console.warn('WARN: No character name provided for /go command'); |
| @@ -3238,77 +3213,6 @@ export function getNameAndAvatarForMessage(character, name = null) { | |||
| 3238 | }; | 3213 | }; |
| 3239 | } | 3214 | } |
| 3240 | 3215 | ||
| 3241 | /** | ||
| 3242 | * Finds a character by name, with optional filtering and precedence for avatars | ||
| 3243 | * @param {object} [options={}] - The options for the search | ||
| 3244 | * @param {string?} [options.name=null] - The name to search for | ||
| 3245 | * @param {boolean} [options.allowAvatar=true] - Whether to allow searching by avatar | ||
| 3246 | * @param {boolean} [options.insensitive=true] - Whether the search should be case insensitive | ||
| 3247 | * @param {string[]?} [options.filteredByTags=null] - Tags to filter characters by | ||
| 3248 | * @param {boolean} [options.preferCurrentChar=true] - Whether to prefer the current character(s) | ||
| 3249 | * @param {boolean} [options.quiet=false] - Whether to suppress warnings | ||
| 3250 | * @returns {any?} - The found character or null if not found | ||
| 3251 | */ | ||
| 3252 | export function findChar({ name = null, allowAvatar = true, insensitive = true, filteredByTags = null, preferCurrentChar = true, quiet = false } = {}) { | ||
| 3253 | const matches = (char) => (allowAvatar && char.avatar === name) || (insensitive ? equalsIgnoreCaseAndAccents(char.name, name) : char.name === name); | ||
| 3254 | |||
| 3255 | // Filter characters by tags if provided | ||
| 3256 | let filteredCharacters = characters; | ||
| 3257 | if (filteredByTags) { | ||
| 3258 | filteredCharacters = characters.filter(char => { | ||
| 3259 | const charTags = getTagsList(char.avatar, false); | ||
| 3260 | return filteredByTags.every(tagName => charTags.some(x => x.name == tagName)); | ||
| 3261 | }); | ||
| 3262 | } | ||
| 3263 | |||
| 3264 | // Get the current character(s) | ||
| 3265 | /** @type {any[]} */ | ||
| 3266 | const currentChars = selected_group ? groups.find(group => group.id === selected_group)?.members.map(member => filteredCharacters.find(char => char.avatar === member)) | ||
| 3267 | : [filteredCharacters.find(char => characters[this_chid]?.avatar === char.avatar)]; | ||
| 3268 | |||
| 3269 | // If we have a current char and prefer it, return that if it matches | ||
| 3270 | if (preferCurrentChar) { | ||
| 3271 | const preferredCharSearch = currentChars.filter(matches); | ||
| 3272 | if (preferredCharSearch.length > 1) { | ||
| 3273 | if (!quiet) toastr.warning(`Multiple characters found for name "${name}" and given conditions.`); | ||
| 3274 | else console.warn(`Multiple characters found for name "${name}". Returning the first match.`); | ||
| 3275 | } | ||
| 3276 | if (preferredCharSearch.length) { | ||
| 3277 | return preferredCharSearch[0]; | ||
| 3278 | } | ||
| 3279 | } | ||
| 3280 | |||
| 3281 | // If allowAvatar is true, search by avatar first | ||
| 3282 | if (allowAvatar && name) { | ||
| 3283 | const characterByAvatar = filteredCharacters.find(char => char.avatar === name); | ||
| 3284 | if (characterByAvatar) { | ||
| 3285 | return characterByAvatar; | ||
| 3286 | } | ||
| 3287 | } | ||
| 3288 | |||
| 3289 | // Search for matching characters by name | ||
| 3290 | const matchingCharacters = name ? filteredCharacters.filter(matches) : filteredCharacters; | ||
| 3291 | if (matchingCharacters.length > 1) { | ||
| 3292 | if (!quiet) toastr.warning(`Multiple characters found for name "${name}" and given conditions.`); | ||
| 3293 | else console.warn(`Multiple characters found for name "${name}". Returning the first match.`); | ||
| 3294 | } | ||
| 3295 | |||
| 3296 | return matchingCharacters[0] || null; | ||
| 3297 | } | ||
| 3298 | |||
| 3299 | /** | ||
| 3300 | * Gets the index of a character based on the character object | ||
| 3301 | * @param {object} char - The character object to find the index for | ||
| 3302 | * @throws {Error} If the character is not found | ||
| 3303 | * @returns {number} The index of the character in the characters array | ||
| 3304 | */ | ||
| 3305 | export function getCharIndex(char) { | ||
| 3306 | if (!char) throw new Error('Character is undefined'); | ||
| 3307 | const index = characters.findIndex(c => c.avatar === char.avatar); | ||
| 3308 | if (index === -1) throw new Error(`Character not found: ${char.avatar}`); | ||
| 3309 | return index; | ||
| 3310 | } | ||
| 3311 | |||
| 3312 | export async function sendMessageAs(args, text) { | 3216 | export async function sendMessageAs(args, text) { |
| 3313 | if (!text) { | 3217 | if (!text) { |
| 3314 | return ''; | 3218 | return ''; |
| @@ -15,7 +15,7 @@ import { | |||
| 15 | import { FILTER_TYPES, FILTER_STATES, DEFAULT_FILTER_STATE, isFilterState, FilterHelper } from './filters.js'; | 15 | import { FILTER_TYPES, FILTER_STATES, DEFAULT_FILTER_STATE, isFilterState, FilterHelper } from './filters.js'; |
| 16 | 16 | ||
| 17 | import { groupCandidatesFilter, groups, selected_group } from './group-chats.js'; | 17 | import { groupCandidatesFilter, groups, selected_group } from './group-chats.js'; |
| 18 | import { download, onlyUnique, parseJsonFile, uuidv4, getSortableDelay, flashHighlight, equalsIgnoreCaseAndAccents, includesIgnoreCaseAndAccents, removeFromArray, getFreeName, debounce } from './utils.js'; | 18 | import { download, onlyUnique, parseJsonFile, uuidv4, getSortableDelay, flashHighlight, equalsIgnoreCaseAndAccents, includesIgnoreCaseAndAccents, removeFromArray, getFreeName, debounce, findChar } from './utils.js'; |
| 19 | import { power_user } from './power-user.js'; | 19 | import { power_user } from './power-user.js'; |
| 20 | import { SlashCommandParser } from './slash-commands/SlashCommandParser.js'; | 20 | import { SlashCommandParser } from './slash-commands/SlashCommandParser.js'; |
| 21 | import { SlashCommand } from './slash-commands/SlashCommand.js'; | 21 | import { SlashCommand } from './slash-commands/SlashCommand.js'; |
| @@ -26,7 +26,6 @@ import { debounce_timeout } from './constants.js'; | |||
| 26 | import { INTERACTABLE_CONTROL_CLASS } from './keyboard.js'; | 26 | import { INTERACTABLE_CONTROL_CLASS } from './keyboard.js'; |
| 27 | import { commonEnumProviders } from './slash-commands/SlashCommandCommonEnumsProvider.js'; | 27 | import { commonEnumProviders } from './slash-commands/SlashCommandCommonEnumsProvider.js'; |
| 28 | import { renderTemplateAsync } from './templates.js'; | 28 | import { renderTemplateAsync } from './templates.js'; |
| 29 | import { findChar } from './slash-commands.js'; | ||
| 30 | 29 | ||
| 31 | export { | 30 | export { |
| 32 | TAG_FOLDER_TYPES, | 31 | TAG_FOLDER_TYPES, |
| @@ -51,7 +50,6 @@ export { | |||
| 51 | removeTagFromMap, | 50 | removeTagFromMap, |
| 52 | }; | 51 | }; |
| 53 | 52 | ||
| 54 | /** @typedef {import('../scripts/popup.js').Popup} Popup */ | ||
| 55 | /** @typedef {import('../script.js').Character} Character */ | 53 | /** @typedef {import('../script.js').Character} Character */ |
| 56 | 54 | ||
| 57 | const CHARACTER_FILTER_SELECTOR = '#rm_characters_block .rm_tag_filter'; | 55 | const CHARACTER_FILTER_SELECTOR = '#rm_characters_block .rm_tag_filter'; |
| @@ -1,10 +1,12 @@ | |||
| 1 | import { getContext } from './extensions.js'; | 1 | import { getContext } from './extensions.js'; |
| 2 | import { getRequestHeaders } from '../script.js'; | 2 | import { characters, getRequestHeaders, this_chid } from '../script.js'; |
| 3 | import { isMobile } from './RossAscends-mods.js'; | 3 | import { isMobile } from './RossAscends-mods.js'; |
| 4 | import { collapseNewlines } from './power-user.js'; | 4 | import { collapseNewlines } from './power-user.js'; |
| 5 | import { debounce_timeout } from './constants.js'; | 5 | import { debounce_timeout } from './constants.js'; |
| 6 | import { Popup, POPUP_RESULT, POPUP_TYPE } from './popup.js'; | 6 | import { Popup, POPUP_RESULT, POPUP_TYPE } from './popup.js'; |
| 7 | import { SlashCommandClosure } from './slash-commands/SlashCommandClosure.js'; | 7 | import { SlashCommandClosure } from './slash-commands/SlashCommandClosure.js'; |
| 8 | import { getTagsList } from './tags.js'; | ||
| 9 | import { groups, selected_group } from './group-chats.js'; | ||
| 8 | 10 | ||
| 9 | /** | 11 | /** |
| 10 | * Pagination status string template. | 12 | * Pagination status string template. |
| @@ -2110,3 +2112,74 @@ export async function showFontAwesomePicker(customList = null) { | |||
| 2110 | } | 2112 | } |
| 2111 | return null; | 2113 | return null; |
| 2112 | } | 2114 | } |
| 2115 | |||
| 2116 | /** | ||
| 2117 | * Finds a character by name, with optional filtering and precedence for avatars | ||
| 2118 | * @param {object} [options={}] - The options for the search | ||
| 2119 | * @param {string?} [options.name=null] - The name to search for | ||
| 2120 | * @param {boolean} [options.allowAvatar=true] - Whether to allow searching by avatar | ||
| 2121 | * @param {boolean} [options.insensitive=true] - Whether the search should be case insensitive | ||
| 2122 | * @param {string[]?} [options.filteredByTags=null] - Tags to filter characters by | ||
| 2123 | * @param {boolean} [options.preferCurrentChar=true] - Whether to prefer the current character(s) | ||
| 2124 | * @param {boolean} [options.quiet=false] - Whether to suppress warnings | ||
| 2125 | * @returns {any?} - The found character or null if not found | ||
| 2126 | */ | ||
| 2127 | export function findChar({ name = null, allowAvatar = true, insensitive = true, filteredByTags = null, preferCurrentChar = true, quiet = false } = {}) { | ||
| 2128 | const matches = (char) => (allowAvatar && char.avatar === name) || (insensitive ? equalsIgnoreCaseAndAccents(char.name, name) : char.name === name); | ||
| 2129 | |||
| 2130 | // Filter characters by tags if provided | ||
| 2131 | let filteredCharacters = characters; | ||
| 2132 | if (filteredByTags) { | ||
| 2133 | filteredCharacters = characters.filter(char => { | ||
| 2134 | const charTags = getTagsList(char.avatar, false); | ||
| 2135 | return filteredByTags.every(tagName => charTags.some(x => x.name == tagName)); | ||
| 2136 | }); | ||
| 2137 | } | ||
| 2138 | |||
| 2139 | // Get the current character(s) | ||
| 2140 | /** @type {any[]} */ | ||
| 2141 | const currentChars = selected_group ? groups.find(group => group.id === selected_group)?.members.map(member => filteredCharacters.find(char => char.avatar === member)) | ||
| 2142 | : [filteredCharacters.find(char => characters[this_chid]?.avatar === char.avatar)]; | ||
| 2143 | |||
| 2144 | // If we have a current char and prefer it, return that if it matches | ||
| 2145 | if (preferCurrentChar) { | ||
| 2146 | const preferredCharSearch = currentChars.filter(matches); | ||
| 2147 | if (preferredCharSearch.length > 1) { | ||
| 2148 | if (!quiet) toastr.warning(`Multiple characters found for name "${name}" and given conditions.`); | ||
| 2149 | else console.warn(`Multiple characters found for name "${name}". Returning the first match.`); | ||
| 2150 | } | ||
| 2151 | if (preferredCharSearch.length) { | ||
| 2152 | return preferredCharSearch[0]; | ||
| 2153 | } | ||
| 2154 | } | ||
| 2155 | |||
| 2156 | // If allowAvatar is true, search by avatar first | ||
| 2157 | if (allowAvatar && name) { | ||
| 2158 | const characterByAvatar = filteredCharacters.find(char => char.avatar === name); | ||
| 2159 | if (characterByAvatar) { | ||
| 2160 | return characterByAvatar; | ||
| 2161 | } | ||
| 2162 | } | ||
| 2163 | |||
| 2164 | // Search for matching characters by name | ||
| 2165 | const matchingCharacters = name ? filteredCharacters.filter(matches) : filteredCharacters; | ||
| 2166 | if (matchingCharacters.length > 1) { | ||
| 2167 | if (!quiet) toastr.warning(`Multiple characters found for name "${name}" and given conditions.`); | ||
| 2168 | else console.warn(`Multiple characters found for name "${name}". Returning the first match.`); | ||
| 2169 | } | ||
| 2170 | |||
| 2171 | return matchingCharacters[0] || null; | ||
| 2172 | } | ||
| 2173 | |||
| 2174 | /** | ||
| 2175 | * Gets the index of a character based on the character object | ||
| 2176 | * @param {object} char - The character object to find the index for | ||
| 2177 | * @throws {Error} If the character is not found | ||
| 2178 | * @returns {number} The index of the character in the characters array | ||
| 2179 | */ | ||
| 2180 | export function getCharIndex(char) { | ||
| 2181 | if (!char) throw new Error('Character is undefined'); | ||
| 2182 | const index = characters.findIndex(c => c.avatar === char.avatar); | ||
| 2183 | if (index === -1) throw new Error(`Character not found: ${char.avatar}`); | ||
| 2184 | return index; | ||
| 2185 | } | ||