|
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(); |
|
} |
|
} |
|
|
|
jQuery(async () => { |
|
eventSource.on(event_types.APP_READY, cleanUpAttachments); |
|
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(), |
|
}), |
|
], |
|
})); |
|
}); |
|
|