| | import { event_types, eventSource, saveSettingsDebounced } from '../../../script.js'; |
| | import { deleteAttachment, getDataBankAttachments, getDataBankAttachmentsForSource, getFileAttachment, uploadFileAttachmentToServer } from '../../chats.js'; |
| | import { extension_settings, renderExtensionTemplateAsync } from '../../extensions.js'; |
| | import { SlashCommand } from '../../slash-commands/SlashCommand.js'; |
| | import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from '../../slash-commands/SlashCommandArgument.js'; |
| | import { SlashCommandClosure } from '../../slash-commands/SlashCommandClosure.js'; |
| | import { enumIcons } from '../../slash-commands/SlashCommandCommonEnumsProvider.js'; |
| | import { SlashCommandEnumValue, enumTypes } from '../../slash-commands/SlashCommandEnumValue.js'; |
| | import { SlashCommandExecutor } from '../../slash-commands/SlashCommandExecutor.js'; |
| | import { SlashCommandParser } from '../../slash-commands/SlashCommandParser.js'; |
| |
|
| | |
| | |
| | |
| | |
| | const TYPES = ['global', 'character', 'chat']; |
| | const FIELDS = ['name', 'url']; |
| |
|
| | |
| | |
| | |
| | |
| | |
| | function getAttachments(source) { |
| | if (!source || !TYPES.includes(source)) { |
| | return getDataBankAttachments(true); |
| | } |
| |
|
| | return getDataBankAttachmentsForSource(source, true); |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | function getAttachmentByField(attachments, value) { |
| | const match = (a) => String(a).trim().toLowerCase() === String(value).trim().toLowerCase(); |
| | const fullMatchByURL = attachments.find(it => match(it.url)); |
| | const fullMatchByName = attachments.find(it => match(it.name)); |
| | return fullMatchByURL || fullMatchByName; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | function getAttachmentByFields(attachments, values) { |
| | for (const value of values) { |
| | const attachment = getAttachmentByField(attachments, value); |
| | if (attachment) { |
| | return attachment; |
| | } |
| | } |
| |
|
| | return null; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | function listDataBankAttachments(args) { |
| | const attachments = getAttachments(args?.source); |
| | const field = args?.field; |
| | return JSON.stringify(attachments.map(a => FIELDS.includes(field) ? a[field] : a.url)); |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | async function getDataBankText(args, value) { |
| | if (!value) { |
| | toastr.warning('No attachment name or URL provided.'); |
| | return; |
| | } |
| |
|
| | const attachments = getAttachments(args?.source); |
| | const attachment = getAttachmentByField(attachments, value); |
| |
|
| | if (!attachment) { |
| | toastr.warning('Attachment not found.'); |
| | return; |
| | } |
| |
|
| | const content = await getFileAttachment(attachment.url); |
| | return content; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | async function uploadDataBankAttachment(args, value) { |
| | const source = args?.source && TYPES.includes(args.source) ? args.source : 'chat'; |
| | const name = args?.name || new Date().toLocaleString(); |
| | const file = new File([value], name, { type: 'text/plain' }); |
| | const url = await uploadFileAttachmentToServer(file, source); |
| | return url; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | async function updateDataBankAttachment(args, value) { |
| | const source = args?.source && TYPES.includes(args.source) ? args.source : 'chat'; |
| | const attachments = getAttachments(source); |
| | const attachment = getAttachmentByFields(attachments, [args?.url, args?.name]); |
| |
|
| | if (!attachment) { |
| | toastr.warning('Attachment not found.'); |
| | return ''; |
| | } |
| |
|
| | await deleteAttachment(attachment, source, () => { }, false); |
| | const file = new File([value], attachment.name, { type: 'text/plain' }); |
| | const url = await uploadFileAttachmentToServer(file, source); |
| | return url; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | async function deleteDataBankAttachment(args, value) { |
| | const source = args?.source && TYPES.includes(args.source) ? args.source : 'chat'; |
| | const attachments = getAttachments(source); |
| | const attachment = getAttachmentByField(attachments, value); |
| |
|
| | if (!attachment) { |
| | toastr.warning('Attachment not found.'); |
| | return ''; |
| | } |
| |
|
| | await deleteAttachment(attachment, source, () => { }, false); |
| | return ''; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | async function disableDataBankAttachment(args, value) { |
| | const attachments = getAttachments(args?.source); |
| | const attachment = getAttachmentByField(attachments, value); |
| |
|
| | if (!attachment) { |
| | toastr.warning('Attachment not found.'); |
| | return ''; |
| | } |
| |
|
| | if (extension_settings.disabled_attachments.includes(attachment.url)) { |
| | return ''; |
| | } |
| |
|
| | extension_settings.disabled_attachments.push(attachment.url); |
| | return ''; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | async function enableDataBankAttachment(args, value) { |
| | const attachments = getAttachments(args?.source); |
| | const attachment = getAttachmentByField(attachments, value); |
| |
|
| | if (!attachment) { |
| | toastr.warning('Attachment not found.'); |
| | return ''; |
| | } |
| |
|
| | const index = extension_settings.disabled_attachments.indexOf(attachment.url); |
| | if (index === -1) { |
| | return ''; |
| | } |
| |
|
| | extension_settings.disabled_attachments.splice(index, 1); |
| | return ''; |
| | } |
| |
|
| | function cleanUpAttachments() { |
| | let shouldSaveSettings = false; |
| | if (extension_settings.character_attachments) { |
| | Object.values(extension_settings.character_attachments).flat().filter(a => a.text).forEach(a => { |
| | shouldSaveSettings = true; |
| | delete a.text; |
| | }); |
| | } |
| | if (Array.isArray(extension_settings.attachments)) { |
| | extension_settings.attachments.filter(a => a.text).forEach(a => { |
| | shouldSaveSettings = true; |
| | delete a.text; |
| | }); |
| | } |
| | if (shouldSaveSettings) { |
| | saveSettingsDebounced(); |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | function cleanUpCharacterAttachments(data) { |
| | const avatar = data?.character?.avatar; |
| | if (!avatar) return; |
| | if (Array.isArray(extension_settings?.character_attachments?.[avatar])) { |
| | delete extension_settings.character_attachments[avatar]; |
| | saveSettingsDebounced(); |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | function handleCharacterRename(oldAvatar, newAvatar) { |
| | if (!oldAvatar || !newAvatar) return; |
| | if (Array.isArray(extension_settings?.character_attachments?.[oldAvatar])) { |
| | extension_settings.character_attachments[newAvatar] = extension_settings.character_attachments[oldAvatar]; |
| | delete extension_settings.character_attachments[oldAvatar]; |
| | saveSettingsDebounced(); |
| | } |
| | } |
| |
|
| | jQuery(async () => { |
| | eventSource.on(event_types.APP_READY, cleanUpAttachments); |
| | eventSource.on(event_types.CHARACTER_DELETED, cleanUpCharacterAttachments); |
| | eventSource.on(event_types.CHARACTER_RENAMED, handleCharacterRename); |
| | const manageButton = await renderExtensionTemplateAsync('attachments', 'manage-button', {}); |
| | const attachButton = await renderExtensionTemplateAsync('attachments', 'attach-button', {}); |
| | $('#data_bank_wand_container').append(manageButton); |
| | $('#attach_file_wand_container').append(attachButton); |
| |
|
| | |
| | const localEnumProviders = { |
| | |
| | |
| | |
| | |
| | |
| | attachments: (returnField = 'name', fallbackSource = 'chat') => ( executor) => { |
| | const source = executor.namedArgumentList.find(it => it.name == 'source')?.value ?? fallbackSource; |
| | if (source instanceof SlashCommandClosure) throw new Error('Argument \'source\' does not support closures'); |
| | const attachments = getAttachments(source); |
| |
|
| | return attachments.map(attachment => new SlashCommandEnumValue( |
| | returnField === 'name' ? attachment.name : attachment.url, |
| | `${enumIcons.getStateIcon(!extension_settings.disabled_attachments.includes(attachment.url))} [${source}] ${returnField === 'url' ? attachment.name : attachment.url}`, |
| | enumTypes.enum, enumIcons.file)); |
| | }, |
| | }; |
| |
|
| | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| | name: 'db', |
| | callback: () => { |
| | document.getElementById('manageAttachments')?.click(); |
| | return ''; |
| | }, |
| | aliases: ['databank', 'data-bank'], |
| | helpString: 'Open the data bank', |
| | })); |
| |
|
| | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| | name: 'db-list', |
| | callback: listDataBankAttachments, |
| | aliases: ['databank-list', 'data-bank-list'], |
| | helpString: 'List attachments in the Data Bank as a JSON-serialized array. Optionally, provide the source of the attachments and the field to list by.', |
| | namedArgumentList: [ |
| | new SlashCommandNamedArgument('source', 'The source of the attachments.', ARGUMENT_TYPE.STRING, false, false, '', TYPES), |
| | new SlashCommandNamedArgument('field', 'The field to list by.', ARGUMENT_TYPE.STRING, false, false, 'url', FIELDS), |
| | ], |
| | returns: ARGUMENT_TYPE.LIST, |
| | })); |
| |
|
| | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| | name: 'db-get', |
| | callback: getDataBankText, |
| | aliases: ['databank-get', 'data-bank-get'], |
| | helpString: 'Get attachment text from the Data Bank. Either provide the name or URL of the attachment. Optionally, provide the source of the attachment.', |
| | namedArgumentList: [ |
| | new SlashCommandNamedArgument('source', 'The source of the attachment.', ARGUMENT_TYPE.STRING, false, false, '', TYPES), |
| | ], |
| | unnamedArgumentList: [ |
| | SlashCommandArgument.fromProps({ |
| | description: 'The name or URL of the attachment.', |
| | typeList: [ARGUMENT_TYPE.STRING], |
| | isRequired: true, |
| | acceptsMultiple: false, |
| | enumProvider: localEnumProviders.attachments('name', ''), |
| | }), |
| | ], |
| | returns: ARGUMENT_TYPE.STRING, |
| | })); |
| |
|
| | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| | name: 'db-add', |
| | callback: uploadDataBankAttachment, |
| | aliases: ['databank-add', 'data-bank-add'], |
| | helpString: 'Add an attachment to the Data Bank. If name is not provided, it will be generated automatically. Returns the URL of the attachment.', |
| | namedArgumentList: [ |
| | new SlashCommandNamedArgument('source', 'The source for the attachment.', ARGUMENT_TYPE.STRING, false, false, 'chat', TYPES), |
| | new SlashCommandNamedArgument('name', 'The name of the attachment.', ARGUMENT_TYPE.STRING, false, false), |
| | ], |
| | unnamedArgumentList: [ |
| | new SlashCommandArgument('The content of the file attachment.', ARGUMENT_TYPE.STRING, true, false), |
| | ], |
| | returns: ARGUMENT_TYPE.STRING, |
| | })); |
| |
|
| | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| | name: 'db-update', |
| | callback: updateDataBankAttachment, |
| | aliases: ['databank-update', 'data-bank-update'], |
| | helpString: 'Update an attachment in the Data Bank, preserving its name. Returns a new URL of the attachment.', |
| | namedArgumentList: [ |
| | new SlashCommandNamedArgument('source', 'The source for the attachment.', ARGUMENT_TYPE.STRING, false, false, 'chat', TYPES), |
| | SlashCommandNamedArgument.fromProps({ |
| | name: 'name', |
| | description: 'The name of the attachment.', |
| | typeList: [ARGUMENT_TYPE.STRING], |
| | enumProvider: localEnumProviders.attachments('name'), |
| | }), |
| | SlashCommandNamedArgument.fromProps({ |
| | name: 'url', |
| | description: 'The URL of the attachment.', |
| | typeList: [ARGUMENT_TYPE.STRING], |
| | enumProvider: localEnumProviders.attachments('url'), |
| | }), |
| | ], |
| | unnamedArgumentList: [ |
| | new SlashCommandArgument('The content of the file attachment.', ARGUMENT_TYPE.STRING, true, false), |
| | ], |
| | returns: ARGUMENT_TYPE.STRING, |
| | })); |
| |
|
| | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| | name: 'db-disable', |
| | callback: disableDataBankAttachment, |
| | aliases: ['databank-disable', 'data-bank-disable'], |
| | helpString: 'Disable an attachment in the Data Bank by its name or URL. Optionally, provide the source of the attachment.', |
| | namedArgumentList: [ |
| | new SlashCommandNamedArgument('source', 'The source of the attachment.', ARGUMENT_TYPE.STRING, false, false, '', TYPES), |
| | ], |
| | unnamedArgumentList: [ |
| | SlashCommandArgument.fromProps({ |
| | description: 'The name or URL of the attachment.', |
| | typeList: [ARGUMENT_TYPE.STRING], |
| | isRequired: true, |
| | enumProvider: localEnumProviders.attachments('name', ''), |
| | }), |
| | ], |
| | })); |
| |
|
| | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| | name: 'db-enable', |
| | callback: enableDataBankAttachment, |
| | aliases: ['databank-enable', 'data-bank-enable'], |
| | helpString: 'Enable an attachment in the Data Bank by its name or URL. Optionally, provide the source of the attachment.', |
| | namedArgumentList: [ |
| | new SlashCommandNamedArgument('source', 'The source of the attachment.', ARGUMENT_TYPE.STRING, false, false, '', TYPES), |
| | ], |
| | unnamedArgumentList: [ |
| | SlashCommandArgument.fromProps({ |
| | description: 'The name or URL of the attachment.', |
| | typeList: [ARGUMENT_TYPE.STRING], |
| | isRequired: true, |
| | enumProvider: localEnumProviders.attachments('name', ''), |
| | }), |
| | ], |
| | })); |
| |
|
| | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| | name: 'db-delete', |
| | callback: deleteDataBankAttachment, |
| | aliases: ['databank-delete', 'data-bank-delete'], |
| | helpString: 'Delete an attachment from the Data Bank.', |
| | namedArgumentList: [ |
| | new SlashCommandNamedArgument('source', 'The source of the attachment.', ARGUMENT_TYPE.STRING, false, false, 'chat', TYPES), |
| | ], |
| | unnamedArgumentList: [ |
| | SlashCommandArgument.fromProps({ |
| | description: 'The name or URL of the attachment.', |
| | typeList: [ARGUMENT_TYPE.STRING], |
| | isRequired: true, |
| | enumProvider: localEnumProviders.attachments(), |
| | }), |
| | ], |
| | })); |
| | }); |
| |
|