2024-10-03 21:13:37 +02:00
import { event _types , eventSource , saveSettingsDebounced } from '../../../script.js' ;
2024-05-30 00:47:33 +02:00
import { deleteAttachment , getDataBankAttachments , getDataBankAttachmentsForSource , getFileAttachment , uploadFileAttachmentToServer } from '../../chats.js' ;
2024-05-30 21:18:00 +02:00
import { extension _settings , renderExtensionTemplateAsync } from '../../extensions.js' ;
2024-05-12 21:15:05 +02:00
import { SlashCommand } from '../../slash-commands/SlashCommand.js' ;
2024-05-30 00:47:33 +02:00
import { ARGUMENT _TYPE , SlashCommandArgument , SlashCommandNamedArgument } from '../../slash-commands/SlashCommandArgument.js' ;
2024-06-20 20:33:45 +02:00
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' ;
2024-05-12 21:15:05 +02:00
import { SlashCommandParser } from '../../slash-commands/SlashCommandParser.js' ;
2024-04-16 01:14:34 +02:00
2024-05-30 00:47:33 +02:00
/ * *
* List of attachment sources
* @ type { string [ ] }
* /
const TYPES = [ 'global' , 'character' , 'chat' ] ;
const FIELDS = [ 'name' , 'url' ] ;
/ * *
2024-05-30 21:18:00 +02:00
* Get attachments from the data bank . Includes disabled attachments .
2024-05-30 00:47:33 +02:00
* @ param { string } [ source ] Source for the attachments
* @ returns { import ( '../../chats' ) . FileAttachment [ ] } List of attachments
* /
function getAttachments ( source ) {
if ( ! source || ! TYPES . includes ( source ) ) {
return getDataBankAttachments ( true ) ;
}
2024-05-30 21:18:00 +02:00
return getDataBankAttachmentsForSource ( source , true ) ;
}
/ * *
* Get attachment by a single name or URL .
* @ param { import ( '../../chats' ) . FileAttachment [ ] } attachments List of attachments
* @ param { string } value Name or URL of the attachment
* @ returns { import ( '../../chats' ) . FileAttachment } Attachment
* /
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 ;
}
/ * *
* Get attachment by multiple fields .
* @ param { import ( '../../chats' ) . FileAttachment [ ] } attachments List of attachments
* @ param { string [ ] } values Name and URL of the attachment to search for
* @ returns
* /
function getAttachmentByFields ( attachments , values ) {
for ( const value of values ) {
const attachment = getAttachmentByField ( attachments , value ) ;
if ( attachment ) {
return attachment ;
}
}
return null ;
2024-05-30 00:47:33 +02:00
}
/ * *
* Callback for listing attachments in the data bank .
* @ param { object } args Named arguments
* @ returns { string } JSON string of the list of attachments
* /
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 ) ) ;
}
/ * *
* Callback for getting text from an attachment in the data bank .
* @ param { object } args Named arguments
* @ param { string } value Name or URL of the attachment
* @ returns { Promise < string > } Content of the attachment
* /
async function getDataBankText ( args , value ) {
if ( ! value ) {
toastr . warning ( 'No attachment name or URL provided.' ) ;
return ;
}
const attachments = getAttachments ( args ? . source ) ;
2024-05-30 21:18:00 +02:00
const attachment = getAttachmentByField ( attachments , value ) ;
2024-05-30 00:47:33 +02:00
if ( ! attachment ) {
toastr . warning ( 'Attachment not found.' ) ;
return ;
}
const content = await getFileAttachment ( attachment . url ) ;
return content ;
}
/ * *
* Callback for adding an attachment to the data bank .
* @ param { object } args Named arguments
* @ param { string } value Content of the attachment
* @ returns { Promise < string > } URL of the attachment
* /
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 ;
}
/ * *
* Callback for updating an attachment in the data bank .
* @ param { object } args Named arguments
* @ param { string } value Content of the attachment
* @ returns { Promise < string > } URL of the attachment
* /
async function updateDataBankAttachment ( args , value ) {
const source = args ? . source && TYPES . includes ( args . source ) ? args . source : 'chat' ;
const attachments = getAttachments ( source ) ;
2024-05-30 21:18:00 +02:00
const attachment = getAttachmentByFields ( attachments , [ args ? . url , args ? . name ] ) ;
2024-05-30 00:47:33 +02:00
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 ;
}
/ * *
* Callback for deleting an attachment from the data bank .
* @ param { object } args Named arguments
* @ param { string } value Name or URL of the attachment
* @ returns { Promise < string > } Empty string
* /
async function deleteDataBankAttachment ( args , value ) {
const source = args ? . source && TYPES . includes ( args . source ) ? args . source : 'chat' ;
const attachments = getAttachments ( source ) ;
2024-05-30 21:18:00 +02:00
const attachment = getAttachmentByField ( attachments , value ) ;
2024-05-30 00:47:33 +02:00
if ( ! attachment ) {
toastr . warning ( 'Attachment not found.' ) ;
return '' ;
}
await deleteAttachment ( attachment , source , ( ) => { } , false ) ;
return '' ;
}
2024-05-30 21:18:00 +02:00
/ * *
* Callback for disabling an attachment in the data bank .
* @ param { object } args Named arguments
* @ param { string } value Name or URL of the attachment
* @ returns { Promise < string > } Empty string
* /
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 '' ;
}
/ * *
* Callback for enabling an attachment in the data bank .
* @ param { object } args Named arguments
* @ param { string } value Name or URL of the attachment
* @ returns { Promise < string > } Empty string
* /
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 '' ;
}
2024-10-03 21:13:37 +02:00
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 ( ) ;
}
}
2024-04-16 01:14:34 +02:00
jQuery ( async ( ) => {
2024-10-03 21:13:37 +02:00
eventSource . on ( event _types . APP _READY , cleanUpAttachments ) ;
2024-06-24 22:17:58 +02:00
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 ) ;
2024-04-24 01:37:57 +02:00
2024-06-20 20:33:45 +02:00
/** A collection of local enum providers for this context of data bank */
const localEnumProviders = {
/ * *
2024-06-21 22:48:11 +02:00
* All attachments in the data bank based on the source argument . If not provided , defaults to 'chat' .
2024-06-20 20:33:45 +02:00
* @ param { 'name' | 'url' } returnField - Whether the enum should return the 'name' field or the 'url'
2024-06-21 22:48:11 +02:00
* @ param { 'chat' | 'character' | 'global' | '' } fallbackSource - The source to use if the source argument is not provided . Empty string to use all sources .
2024-06-20 20:33:45 +02:00
* * /
2024-06-21 22:48:11 +02:00
attachments : ( returnField = 'name' , fallbackSource = 'chat' ) => ( /** @type {SlashCommandExecutor} */ executor ) => {
const source = executor . namedArgumentList . find ( it => it . name == 'source' ) ? . value ? ? fallbackSource ;
2024-06-20 20:33:45 +02:00
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 ,
2024-06-21 20:04:55 +02:00
` ${ enumIcons . getStateIcon ( ! extension _settings . disabled _attachments . includes ( attachment . url ) ) } [ ${ source } ] ${ returnField === 'url' ? attachment . name : attachment . url } ` ,
2024-06-20 20:33:45 +02:00
enumTypes . enum , enumIcons . file ) ) ;
} ,
} ;
2024-05-30 00:47:33 +02:00
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'db' ,
2024-06-17 07:04:10 +02:00
callback : ( ) => {
document . getElementById ( 'manageAttachments' ) ? . click ( ) ;
return '' ;
} ,
2024-05-12 21:15:05 +02:00
aliases : [ 'databank' , 'data-bank' ] ,
helpString : 'Open the data bank' ,
} ) ) ;
2024-05-30 00:47:33 +02:00
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'db-list' ,
callback : listDataBankAttachments ,
aliases : [ 'databank-list' , 'data-bank-list' ] ,
2024-05-30 13:49:57 +02:00
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.' ,
2024-05-30 00:47:33 +02:00
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' ] ,
2024-05-30 13:49:57 +02:00
helpString : 'Get attachment text from the Data Bank. Either provide the name or URL of the attachment. Optionally, provide the source of the attachment.' ,
2024-05-30 00:47:33 +02:00
namedArgumentList : [
new SlashCommandNamedArgument ( 'source' , 'The source of the attachment.' , ARGUMENT _TYPE . STRING , false , false , '' , TYPES ) ,
] ,
unnamedArgumentList : [
2024-06-21 22:48:11 +02:00
SlashCommandArgument . fromProps ( {
description : 'The name or URL of the attachment.' ,
typeList : [ ARGUMENT _TYPE . STRING ] ,
isRequired : true ,
acceptsMultiple : false ,
enumProvider : localEnumProviders . attachments ( 'name' , '' ) ,
} ) ,
2024-05-30 00:47:33 +02:00
] ,
returns : ARGUMENT _TYPE . STRING ,
} ) ) ;
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'db-add' ,
callback : uploadDataBankAttachment ,
aliases : [ 'databank-add' , 'data-bank-add' ] ,
2024-05-30 13:49:57 +02:00
helpString : 'Add an attachment to the Data Bank. If name is not provided, it will be generated automatically. Returns the URL of the attachment.' ,
2024-05-30 00:47:33 +02:00
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' ] ,
2024-05-30 13:49:57 +02:00
helpString : 'Update an attachment in the Data Bank, preserving its name. Returns a new URL of the attachment.' ,
2024-05-30 00:47:33 +02:00
namedArgumentList : [
new SlashCommandNamedArgument ( 'source' , 'The source for the attachment.' , ARGUMENT _TYPE . STRING , false , false , 'chat' , TYPES ) ,
2024-06-20 20:33:45 +02:00
SlashCommandNamedArgument . fromProps ( {
name : 'name' ,
description : 'The name of the attachment.' ,
typeList : [ ARGUMENT _TYPE . STRING ] ,
2024-06-21 22:48:11 +02:00
enumProvider : localEnumProviders . attachments ( 'name' ) ,
2024-06-20 20:33:45 +02:00
} ) ,
SlashCommandNamedArgument . fromProps ( {
name : 'url' ,
description : 'The URL of the attachment.' ,
typeList : [ ARGUMENT _TYPE . STRING ] ,
2024-06-21 22:48:11 +02:00
enumProvider : localEnumProviders . attachments ( 'url' ) ,
2024-06-20 20:33:45 +02:00
} ) ,
2024-05-30 00:47:33 +02:00
] ,
unnamedArgumentList : [
new SlashCommandArgument ( 'The content of the file attachment.' , ARGUMENT _TYPE . STRING , true , false ) ,
] ,
returns : ARGUMENT _TYPE . STRING ,
} ) ) ;
2024-05-30 21:18:00 +02:00
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 : [
2024-06-20 20:33:45 +02:00
SlashCommandArgument . fromProps ( {
description : 'The name or URL of the attachment.' ,
typeList : [ ARGUMENT _TYPE . STRING ] ,
isRequired : true ,
2024-06-21 22:48:11 +02:00
enumProvider : localEnumProviders . attachments ( 'name' , '' ) ,
2024-06-20 20:33:45 +02:00
} ) ,
2024-05-30 21:18:00 +02:00
] ,
} ) ) ;
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 : [
2024-06-20 20:33:45 +02:00
SlashCommandArgument . fromProps ( {
description : 'The name or URL of the attachment.' ,
typeList : [ ARGUMENT _TYPE . STRING ] ,
isRequired : true ,
2024-06-21 22:48:11 +02:00
enumProvider : localEnumProviders . attachments ( 'name' , '' ) ,
2024-06-20 20:33:45 +02:00
} ) ,
2024-05-30 21:18:00 +02:00
] ,
} ) ) ;
2024-05-30 00:47:33 +02:00
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'db-delete' ,
callback : deleteDataBankAttachment ,
aliases : [ 'databank-delete' , 'data-bank-delete' ] ,
2024-05-30 13:49:57 +02:00
helpString : 'Delete an attachment from the Data Bank.' ,
2024-05-30 00:47:33 +02:00
namedArgumentList : [
new SlashCommandNamedArgument ( 'source' , 'The source of the attachment.' , ARGUMENT _TYPE . STRING , false , false , 'chat' , TYPES ) ,
] ,
unnamedArgumentList : [
2024-06-20 20:33:45 +02:00
SlashCommandArgument . fromProps ( {
description : 'The name or URL of the attachment.' ,
typeList : [ ARGUMENT _TYPE . STRING ] ,
isRequired : true ,
2024-06-21 22:48:11 +02:00
enumProvider : localEnumProviders . attachments ( ) ,
2024-06-20 20:33:45 +02:00
} ) ,
2024-05-30 00:47:33 +02:00
] ,
} ) ) ;
2024-04-16 01:14:34 +02:00
} ) ;