2023-07-20 19:32:15 +02:00
import {
2023-12-07 02:26:29 +01:00
Generate ,
activateSendButtons ,
2023-07-20 19:32:15 +02:00
addOneMessage ,
2023-12-07 02:26:29 +01:00
callPopup ,
2023-07-20 19:32:15 +02:00
characters ,
chat ,
chat _metadata ,
2023-12-07 02:26:29 +01:00
comment _avatar ,
deactivateSendButtons ,
2023-07-20 19:32:15 +02:00
default _avatar ,
eventSource ,
event _types ,
2024-03-23 17:44:40 +01:00
extension _prompt _roles ,
2023-12-07 02:26:29 +01:00
extension _prompt _types ,
2023-07-20 19:32:15 +02:00
extractMessageBias ,
2023-12-07 02:26:29 +01:00
generateQuietPrompt ,
generateRaw ,
2023-07-20 19:32:15 +02:00
getThumbnailUrl ,
2023-12-07 02:26:29 +01:00
is _send _press ,
main _api ,
name1 ,
2024-04-28 02:53:17 +02:00
name2 ,
2023-12-07 02:26:29 +01:00
reloadCurrentChat ,
2023-12-21 19:49:03 +01:00
removeMacros ,
2024-06-05 02:19:22 +02:00
renameCharacter ,
2023-07-20 19:32:15 +02:00
saveChatConditional ,
2023-12-07 02:26:29 +01:00
sendMessageAsUser ,
2023-07-20 19:32:15 +02:00
sendSystemMessage ,
2024-03-26 17:21:22 +01:00
setActiveCharacter ,
setActiveGroup ,
2023-12-07 02:26:29 +01:00
setCharacterId ,
setCharacterName ,
setExtensionPrompt ,
2023-07-20 19:32:15 +02:00
setUserName ,
substituteParams ,
system _avatar ,
system _message _types ,
2023-10-16 08:12:12 +02:00
this _chid ,
2023-12-02 19:04:51 +01:00
} from '../script.js' ;
2024-05-12 21:15:05 +02:00
import { PARSER _FLAG , SlashCommandParser } from './slash-commands/SlashCommandParser.js' ;
import { SlashCommandParserError } from './slash-commands/SlashCommandParserError.js' ;
2023-12-02 19:04:51 +01:00
import { getMessageTimeStamp } from './RossAscends-mods.js' ;
2024-04-12 09:03:50 +02:00
import { hideChatMessageRange } from './chats.js' ;
2023-12-07 02:26:29 +01:00
import { getContext , saveMetadataDebounced } from './extensions.js' ;
2023-12-02 19:04:51 +01:00
import { getRegexedString , regex _placement } from './extensions/regex/engine.js' ;
2024-01-05 19:02:44 +01:00
import { findGroupMemberId , groups , is _group _generating , openGroupById , resetSelectedGroup , saveGroupChat , selected _group } from './group-chats.js' ;
2024-06-16 04:37:42 +02:00
import { chat _completion _sources , oai _settings , setupChatCompletionPromptManager } from './openai.js' ;
2024-05-06 00:18:59 +02:00
import { autoSelectPersona , retriggerFirstMessageOnEmptyChat , user _avatar } from './personas.js' ;
2023-12-07 02:26:29 +01:00
import { addEphemeralStoppingString , chat _styles , flushEphemeralStoppingStrings , power _user } from './power-user.js' ;
2024-03-15 15:08:22 +01:00
import { textgen _types , textgenerationwebui _settings } from './textgen-settings.js' ;
2024-04-13 20:33:19 +02:00
import { decodeTextTokens , getFriendlyTokenizerName , getTextTokens , getTokenCountAsync } from './tokenizers.js' ;
2024-04-19 21:46:49 +02:00
import { debounce , delay , isFalseBoolean , isTrueBoolean , stringToRange , trimToEndSentence , trimToStartSentence , waitUntilCondition } from './utils.js' ;
2023-12-02 19:04:51 +01:00
import { registerVariableCommands , resolveVariable } from './variables.js' ;
2024-04-12 01:21:17 +02:00
import { background _settings } from './backgrounds.js' ;
2024-05-12 21:15:05 +02:00
import { SlashCommandScope } from './slash-commands/SlashCommandScope.js' ;
import { SlashCommandClosure } from './slash-commands/SlashCommandClosure.js' ;
import { SlashCommandClosureResult } from './slash-commands/SlashCommandClosureResult.js' ;
import { ARGUMENT _TYPE , SlashCommandArgument , SlashCommandNamedArgument } from './slash-commands/SlashCommandArgument.js' ;
import { AutoComplete } from './autocomplete/AutoComplete.js' ;
import { SlashCommand } from './slash-commands/SlashCommand.js' ;
import { SlashCommandAbortController } from './slash-commands/SlashCommandAbortController.js' ;
import { SlashCommandNamedArgumentAssignment } from './slash-commands/SlashCommandNamedArgumentAssignment.js' ;
2024-06-14 23:48:41 +02:00
import { SlashCommandEnumValue } from './slash-commands/SlashCommandEnumValue.js' ;
2024-05-12 21:15:05 +02:00
export {
executeSlashCommands , executeSlashCommandsWithOptions , getSlashCommandsHelp , registerSlashCommand ,
} ;
export const parser = new SlashCommandParser ( ) ;
2024-04-19 13:19:57 +02:00
/ * *
2024-05-12 21:15:05 +02:00
* @ deprecated Use SlashCommandParser . addCommandObject ( ) instead
2024-04-19 13:19:57 +02:00
* /
2024-05-12 21:15:05 +02:00
const registerSlashCommand = SlashCommandParser . addCommand . bind ( SlashCommandParser ) ;
const getSlashCommandsHelp = parser . getHelpString . bind ( parser ) ;
2024-05-19 21:22:32 +02:00
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : '?' ,
2024-05-12 21:15:05 +02:00
callback : helpCommandCallback ,
aliases : [ 'help' ] ,
unnamedArgumentList : [ new SlashCommandArgument (
'help topic' , ARGUMENT _TYPE . STRING , false , false , null , [ 'slash' , 'format' , 'hotkeys' , 'macros' ] ,
) ] ,
helpString : 'Get help on macros, chat formatting and commands.' ,
} ) ) ;
2024-05-19 21:22:32 +02:00
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
2024-06-11 01:00:13 +02:00
name : 'persona' ,
2024-05-12 21:15:05 +02:00
callback : setNameCallback ,
2024-06-11 01:00:13 +02:00
namedArgumentList : [
new SlashCommandNamedArgument (
'mode' , 'The mode for persona selection. ("lookup" = search for existing persona, "temp" = create a temporary name, set a temporary name, "all" = allow both in the same command)' ,
[ ARGUMENT _TYPE . STRING ] , false , false , 'all' , [ 'lookup' , 'temp' , 'all' ] ,
) ,
] ,
2024-05-12 21:15:05 +02:00
unnamedArgumentList : [
new SlashCommandArgument (
2024-06-11 01:00:13 +02:00
'persona name' , [ ARGUMENT _TYPE . STRING ] , true ,
2024-05-12 21:15:05 +02:00
) ,
] ,
2024-06-11 01:00:13 +02:00
helpString : 'Selects the given persona with its name and avatar (by name or avatar url). If no matching persona exists, applies a temporary name.' ,
aliases : [ 'name' ] ,
2024-05-12 21:15:05 +02:00
} ) ) ;
2024-05-19 21:22:32 +02:00
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'sync' ,
2024-05-12 21:15:05 +02:00
callback : syncCallback ,
helpString : 'Syncs the user persona in user-attributed messages in the current chat.' ,
} ) ) ;
2024-05-19 21:22:32 +02:00
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'lock' ,
2024-05-12 21:15:05 +02:00
callback : bindCallback ,
aliases : [ 'bind' ] ,
helpString : 'Locks/unlocks a persona (name and avatar) to the current chat' ,
} ) ) ;
2024-05-19 21:22:32 +02:00
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'bg' ,
2024-05-12 21:15:05 +02:00
callback : setBackgroundCallback ,
aliases : [ 'background' ] ,
returns : 'the current background' ,
unnamedArgumentList : [
2024-06-14 23:48:41 +02:00
SlashCommandArgument . fromProps ( { description : 'filename' ,
typeList : [ ARGUMENT _TYPE . STRING ] ,
isRequired : true ,
enumProvider : ( ) => [ ... document . querySelectorAll ( '.bg_example' ) ]
. map ( ( it ) => new SlashCommandEnumValue ( it . getAttribute ( 'bgfile' ) ) )
. filter ( it => it . value ? . length )
,
} ) ,
2024-05-12 21:15:05 +02:00
] ,
helpString : `
< div >
Sets a background according to the provided filename . Partial names allowed .
< / d i v >
< div >
< strong > Example : < / s t r o n g >
< ul >
< li >
< pre > < code > / b g b e a c h . j p g < / c o d e > < / p r e >
< / l i >
< / u l >
< / d i v >
` ,
} ) ) ;
2024-05-19 21:22:32 +02:00
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'sendas' ,
2024-05-12 21:15:05 +02:00
callback : sendMessageAs ,
namedArgumentList : [
new SlashCommandNamedArgument (
'name' , 'Character name' , [ ARGUMENT _TYPE . STRING ] , true ,
) ,
new SlashCommandNamedArgument (
'compact' , 'Use compact layout' , [ ARGUMENT _TYPE . BOOLEAN ] , false , false , 'false' , [ 'true' , 'false' ] ,
) ,
SlashCommandNamedArgument . fromProps ( {
name : 'at' ,
description : 'position to insert the message' ,
typeList : [ ARGUMENT _TYPE . NUMBER , ARGUMENT _TYPE . VARIABLE _NAME ] ,
} ) ,
] ,
unnamedArgumentList : [
new SlashCommandArgument (
'text' , [ ARGUMENT _TYPE . STRING ] , true ,
) ,
] ,
helpString : `
< div >
Sends a message as a specific character . Uses the character avatar if it exists in the characters list .
< / d i v >
< div >
< strong > Example : < / s t r o n g >
< ul >
< li >
< pre > < code > / s e n d a s n a m e = " C h l o e " H e l l o , g u y s ! < / c o d e > < / p r e >
will send "Hello, guys!" from "Chloe" .
< / l i >
< / u l >
< / d i v >
< div >
If "compact" is set to true , the message is sent using a compact layout .
< / d i v >
` ,
} ) ) ;
2024-05-19 21:22:32 +02:00
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'sys' ,
2024-05-12 21:15:05 +02:00
callback : sendNarratorMessage ,
aliases : [ 'nar' ] ,
namedArgumentList : [
new SlashCommandNamedArgument (
'compact' ,
'compact layout' ,
[ ARGUMENT _TYPE . BOOLEAN ] ,
false ,
false ,
'false' ,
) ,
SlashCommandNamedArgument . fromProps ( {
name : 'at' ,
description : 'position to insert the message' ,
typeList : [ ARGUMENT _TYPE . NUMBER , ARGUMENT _TYPE . VARIABLE _NAME ] ,
} ) ,
] ,
unnamedArgumentList : [
new SlashCommandArgument (
'text' , [ ARGUMENT _TYPE . STRING ] , true ,
) ,
] ,
helpString : `
< div >
Sends a message as a system narrator .
< / d i v >
< div >
If < code > compact < / c o d e > i s s e t t o < c o d e > t r u e < / c o d e > , t h e m e s s a g e i s s e n t u s i n g a c o m p a c t l a y o u t .
< / d i v >
< div >
< strong > Example : < / s t r o n g >
< ul >
< li >
< pre > < code > / s y s T h e s u n s e t s i n t h e w e s t . < / c o d e > < / p r e >
< / l i >
< li >
< pre > < code > / s y s c o m p a c t = t r u e A b r i e f n o t e . < / c o d e > < / p r e >
< / l i >
< / u l >
< / d i v >
` ,
} ) ) ;
2024-05-19 21:22:32 +02:00
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'sysname' ,
2024-05-12 21:15:05 +02:00
callback : setNarratorName ,
unnamedArgumentList : [
new SlashCommandArgument (
'name' , [ ARGUMENT _TYPE . STRING ] , false ,
) ,
] ,
helpString : 'Sets a name for future system narrator messages in this chat (display only). Default: System. Leave empty to reset.' ,
} ) ) ;
2024-05-19 21:22:32 +02:00
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'comment' ,
2024-05-12 21:15:05 +02:00
callback : sendCommentMessage ,
namedArgumentList : [
new SlashCommandNamedArgument (
'compact' ,
'Whether to use a compact layout' ,
[ ARGUMENT _TYPE . BOOLEAN ] ,
false ,
false ,
'false' ,
) ,
SlashCommandNamedArgument . fromProps ( {
name : 'at' ,
description : 'position to insert the message' ,
typeList : [ ARGUMENT _TYPE . NUMBER , ARGUMENT _TYPE . VARIABLE _NAME ] ,
} ) ,
] ,
unnamedArgumentList : [
new SlashCommandArgument (
'text' ,
[ ARGUMENT _TYPE . STRING ] ,
true ,
) ,
] ,
helpString : `
< div >
Adds a note / comment message not part of the chat .
< / d i v >
< div >
If < code > compact < / c o d e > i s s e t t o < c o d e > t r u e < / c o d e > , t h e m e s s a g e i s s e n t u s i n g a c o m p a c t l a y o u t .
< / d i v >
< div >
< strong > Example : < / s t r o n g >
< ul >
< li >
< pre > < code > / c o m m e n t T h i s i s a c o m m e n t < / c o d e > < / p r e >
< / l i >
< li >
< pre > < code > / c o m m e n t c o m p a c t = t r u e T h i s i s a c o m p a c t c o m m e n t < / c o d e > < / p r e >
< / l i >
< / u l >
< / d i v >
` ,
} ) ) ;
2024-05-19 21:22:32 +02:00
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'single' ,
2024-05-12 21:15:05 +02:00
callback : setStoryModeCallback ,
aliases : [ 'story' ] ,
helpString : 'Sets the message style to single document mode without names or avatars visible.' ,
} ) ) ;
2024-05-19 21:22:32 +02:00
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'bubble' ,
2024-05-12 21:15:05 +02:00
callback : setBubbleModeCallback ,
aliases : [ 'bubbles' ] ,
helpString : 'Sets the message style to bubble chat mode.' ,
} ) ) ;
2024-05-19 21:22:32 +02:00
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'flat' ,
2024-05-12 21:15:05 +02:00
callback : setFlatModeCallback ,
aliases : [ 'default' ] ,
helpString : 'Sets the message style to flat chat mode.' ,
} ) ) ;
2024-05-19 21:22:32 +02:00
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'continue' ,
2024-05-12 21:15:05 +02:00
callback : continueChatCallback ,
aliases : [ 'cont' ] ,
unnamedArgumentList : [
new SlashCommandArgument (
'prompt' , [ ARGUMENT _TYPE . STRING ] , false ,
) ,
] ,
helpString : `
< div >
Continues the last message in the chat , with an optional additional prompt .
< / d i v >
< div >
< strong > Example : < / s t r o n g >
< ul >
< li >
< pre > < code > / c o n t i n u e < / c o d e > < / p r e >
Continues the chat with no additional prompt .
< / l i >
< li >
< pre > < code > / c o n t i n u e L e t ' s e x p l o r e t h i s f u r t h e r . . . < / c o d e > < / p r e >
Continues the chat with the provided prompt .
< / l i >
< / u l >
< / d i v >
` ,
} ) ) ;
2024-05-19 21:22:32 +02:00
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'go' ,
2024-05-12 21:15:05 +02:00
callback : goToCharacterCallback ,
unnamedArgumentList : [
2024-06-14 23:48:41 +02:00
SlashCommandArgument . fromProps ( { description : 'name' ,
typeList : [ ARGUMENT _TYPE . STRING ] ,
isRequired : true ,
enumProvider : ( ) => [
... characters . map ( it => new SlashCommandEnumValue ( it . name , null , 'qr' , 'C' ) ) ,
... groups . map ( it => new SlashCommandEnumValue ( it . name , null , 'variable' , 'G' ) ) ,
] ,
} ) ,
2024-05-12 21:15:05 +02:00
] ,
helpString : 'Opens up a chat with the character or group by its name' ,
aliases : [ 'char' ] ,
} ) ) ;
2024-06-05 02:19:22 +02:00
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'rename-char' ,
2024-06-05 21:53:41 +02:00
/** @param {{silent: string, chats: string}} options @param {string} name */
callback : async ( { silent = 'true' , chats = null } , name ) => {
const renamed = await renameCharacter ( name , { silent : isTrueBoolean ( silent ) , renameChats : chats !== null ? isTrueBoolean ( chats ) : null } ) ;
2024-06-05 02:19:22 +02:00
return String ( renamed ) ;
} ,
returns : 'true/false - Whether the rename was successful' ,
namedArgumentList : [
new SlashCommandNamedArgument (
2024-06-05 21:53:41 +02:00
'silent' , 'Hide any blocking popups. (if false, the name is optional. If not supplied, a popup asking for it will appear)' , [ ARGUMENT _TYPE . BOOLEAN ] , false , false , 'true' ,
2024-06-05 02:19:22 +02:00
) ,
new SlashCommandNamedArgument (
2024-06-05 21:53:41 +02:00
'chats' , 'Rename char in all previous chats' , [ ARGUMENT _TYPE . BOOLEAN ] , false , false , '<null>' ,
2024-06-05 02:19:22 +02:00
) ,
] ,
unnamedArgumentList : [
new SlashCommandArgument (
'new char name' , [ ARGUMENT _TYPE . STRING ] , true ,
) ,
] ,
2024-06-05 21:53:41 +02:00
helpString : 'Renames the current character.' ,
2024-06-05 02:19:22 +02:00
} ) ) ;
2024-05-19 21:22:32 +02:00
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'sysgen' ,
2024-05-12 21:15:05 +02:00
callback : generateSystemMessage ,
unnamedArgumentList : [
new SlashCommandArgument (
'prompt' , [ ARGUMENT _TYPE . STRING ] , true ,
) ,
] ,
helpString : 'Generates a system message using a specified prompt.' ,
} ) ) ;
2024-05-19 21:22:32 +02:00
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'ask' ,
2024-05-12 21:15:05 +02:00
callback : askCharacter ,
2024-06-05 20:37:33 +02:00
namedArgumentList : [
2024-06-14 23:48:41 +02:00
SlashCommandNamedArgument . fromProps ( { name : 'name' ,
description : 'character name' ,
typeList : [ ARGUMENT _TYPE . STRING ] ,
isRequired : true ,
enumProvider : ( ) => characters . map ( it => new SlashCommandEnumValue ( it . name , null , 'qr' , 'C' ) ) ,
} ) ,
2024-06-05 20:37:33 +02:00
] ,
unnamedArgumentList : [
2024-05-12 21:15:05 +02:00
new SlashCommandArgument (
2024-06-05 20:37:33 +02:00
'prompt' , [ ARGUMENT _TYPE . STRING ] , true , false ,
2024-05-12 21:15:05 +02:00
) ,
] ,
2024-06-05 20:37:33 +02:00
helpString : 'Asks a specified character card a prompt. Character name must be provided in a named argument.' ,
2024-05-12 21:15:05 +02:00
} ) ) ;
2024-05-19 21:22:32 +02:00
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'delname' ,
2024-05-12 21:15:05 +02:00
callback : deleteMessagesByNameCallback ,
namedArgumentList : [ ] ,
unnamedArgumentList : [
new SlashCommandArgument (
'name' , [ ARGUMENT _TYPE . STRING ] , true ,
) ,
] ,
aliases : [ 'cancel' ] ,
helpString : `
< div >
Deletes all messages attributed to a specified name .
< / d i v >
< div >
< strong > Example : < / s t r o n g >
< ul >
< li >
< pre > < code > / d e l n a m e J o h n < / c o d e > < / p r e >
< / l i >
< / u l >
< / d i v >
` ,
} ) ) ;
2024-05-19 21:22:32 +02:00
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'send' ,
2024-05-12 21:15:05 +02:00
callback : sendUserMessageCallback ,
namedArgumentList : [
new SlashCommandNamedArgument (
'compact' ,
'whether to use a compact layout' ,
[ ARGUMENT _TYPE . BOOLEAN ] ,
false ,
false ,
'false' ,
) ,
SlashCommandNamedArgument . fromProps ( {
name : 'at' ,
description : 'position to insert the message' ,
typeList : [ ARGUMENT _TYPE . NUMBER , ARGUMENT _TYPE . VARIABLE _NAME ] ,
} ) ,
new SlashCommandNamedArgument (
'name' ,
'display name' ,
[ ARGUMENT _TYPE . STRING , ARGUMENT _TYPE . VARIABLE _NAME ] ,
false ,
false ,
'{{user}}' ,
) ,
] ,
unnamedArgumentList : [
new SlashCommandArgument (
'text' ,
[ ARGUMENT _TYPE . STRING ] ,
true ,
) ,
] ,
helpString : `
< div >
Adds a user message to the chat log without triggering a generation .
< / d i v >
< div >
If < code > compact < / c o d e > i s s e t t o < c o d e > t r u e < / c o d e > , t h e m e s s a g e i s s e n t u s i n g a c o m p a c t l a y o u t .
< / d i v >
< div >
If < code > name < / c o d e > i s s e t , i t w i l l b e d i s p l a y e d a s t h e m e s s a g e s e n d e r . C a n b e a n e m p t y f o r n o n a m e .
< / d i v >
< div >
< strong > Example : < / s t r o n g >
< ul >
< li >
< pre > < code > / s e n d H e l l o t h e r e ! < / c o d e > < / p r e >
< / l i >
< li >
< pre > < code > / s e n d c o m p a c t = t r u e H i < / c o d e > < / p r e >
< / l i >
< / u l >
< / d i v >
` ,
} ) ) ;
2024-05-19 21:22:32 +02:00
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'trigger' ,
2024-05-12 21:15:05 +02:00
callback : triggerGenerationCallback ,
namedArgumentList : [
new SlashCommandNamedArgument (
'await' ,
'Whether to await for the triggered generation before continuing' ,
[ ARGUMENT _TYPE . BOOLEAN ] ,
false ,
false ,
'false' ,
) ,
] ,
helpString : `
< div >
Triggers a message generation . If in group , can trigger a message for the specified group member index or name .
< / d i v >
< div >
If < code > await = true < / c o d e > n a m e d a r g u m e n t i s p a s s e d , t h e c o m m a n d w i l l a w a i t f o r t h e t r i g g e r e d g e n e r a t i o n b e f o r e c o n t i n u i n g .
< / d i v >
` ,
} ) ) ;
2024-05-19 21:22:32 +02:00
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'hide' ,
2024-05-12 21:15:05 +02:00
callback : hideMessageCallback ,
unnamedArgumentList : [
new SlashCommandArgument (
'message index or range' , [ ARGUMENT _TYPE . NUMBER , ARGUMENT _TYPE . RANGE ] , true ,
) ,
] ,
helpString : 'Hides a chat message from the prompt.' ,
} ) ) ;
2024-05-19 21:22:32 +02:00
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'unhide' ,
2024-05-12 21:15:05 +02:00
callback : unhideMessageCallback ,
unnamedArgumentList : [
new SlashCommandArgument (
'message index or range' , [ ARGUMENT _TYPE . NUMBER , ARGUMENT _TYPE . RANGE ] , true ,
) ,
] ,
helpString : 'Unhides a message from the prompt.' ,
} ) ) ;
2024-05-19 21:22:32 +02:00
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'member-disable' ,
2024-05-12 21:15:05 +02:00
callback : disableGroupMemberCallback ,
2024-05-17 19:56:58 +02:00
aliases : [ 'disable' , 'disablemember' , 'memberdisable' ] ,
2024-05-12 21:15:05 +02:00
unnamedArgumentList : [
new SlashCommandArgument (
'member index or name' , [ ARGUMENT _TYPE . NUMBER , ARGUMENT _TYPE . STRING ] , true ,
) ,
] ,
helpString : 'Disables a group member from being drafted for replies.' ,
} ) ) ;
2024-05-19 21:22:32 +02:00
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'member-enable' ,
2024-05-17 19:56:58 +02:00
aliases : [ 'enable' , 'enablemember' , 'memberenable' ] ,
2024-05-12 21:15:05 +02:00
callback : enableGroupMemberCallback ,
unnamedArgumentList : [
new SlashCommandArgument (
'member index or name' , [ ARGUMENT _TYPE . NUMBER , ARGUMENT _TYPE . STRING ] , true ,
) ,
] ,
helpString : 'Enables a group member to be drafted for replies.' ,
} ) ) ;
2024-05-19 21:22:32 +02:00
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'member-add' ,
2024-05-12 21:15:05 +02:00
callback : addGroupMemberCallback ,
2024-05-17 19:56:58 +02:00
aliases : [ 'addmember' , 'memberadd' ] ,
2024-05-12 21:15:05 +02:00
unnamedArgumentList : [
new SlashCommandArgument (
'character name' , [ ARGUMENT _TYPE . STRING ] , true ,
) ,
] ,
helpString : `
< div >
Adds a new group member to the group chat .
< / d i v >
< div >
< strong > Example : < / s t r o n g >
< ul >
< li >
2024-05-17 19:59:00 +02:00
< pre > < code > / m e m b e r - a d d J o h n D o e < / c o d e > < / p r e >
2024-05-12 21:15:05 +02:00
< / l i >
< / u l >
< / d i v >
` ,
} ) ) ;
2024-05-19 21:22:32 +02:00
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'member-remove' ,
2024-05-12 21:15:05 +02:00
callback : removeGroupMemberCallback ,
2024-05-17 19:56:58 +02:00
aliases : [ 'removemember' , 'memberremove' ] ,
2024-05-12 21:15:05 +02:00
unnamedArgumentList : [
new SlashCommandArgument (
'member index or name' , [ ARGUMENT _TYPE . NUMBER , ARGUMENT _TYPE . STRING ] , true ,
) ,
] ,
helpString : `
< div >
Removes a group member from the group chat .
< / d i v >
< div >
< strong > Example : < / s t r o n g >
< ul >
< li >
2024-05-17 19:59:00 +02:00
< pre > < code > / m e m b e r - r e m o v e 2 < / c o d e > < / p r e >
< pre > < code > / m e m b e r - r e m o v e J o h n D o e < / c o d e > < / p r e >
2024-05-12 21:15:05 +02:00
< / l i >
< / u l >
< / d i v >
` ,
} ) ) ;
2024-05-19 21:22:32 +02:00
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'member-up' ,
2024-05-12 21:15:05 +02:00
callback : moveGroupMemberUpCallback ,
2024-05-17 19:56:58 +02:00
aliases : [ 'upmember' , 'memberup' ] ,
2024-05-12 21:15:05 +02:00
unnamedArgumentList : [
new SlashCommandArgument (
'member index or name' , [ ARGUMENT _TYPE . NUMBER , ARGUMENT _TYPE . STRING ] , true ,
) ,
] ,
helpString : 'Moves a group member up in the group chat list.' ,
} ) ) ;
2024-05-19 21:22:32 +02:00
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'member-down' ,
2024-05-12 21:15:05 +02:00
callback : moveGroupMemberDownCallback ,
2024-05-17 19:56:58 +02:00
aliases : [ 'downmember' , 'memberdown' ] ,
2024-05-12 21:15:05 +02:00
unnamedArgumentList : [
new SlashCommandArgument (
'member index or name' , [ ARGUMENT _TYPE . NUMBER , ARGUMENT _TYPE . STRING ] , true ,
) ,
] ,
helpString : 'Moves a group member down in the group chat list.' ,
} ) ) ;
2024-05-19 21:22:32 +02:00
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'peek' ,
2024-05-12 21:15:05 +02:00
callback : peekCallback ,
unnamedArgumentList : [
new SlashCommandArgument (
'message index or range' , [ ARGUMENT _TYPE . NUMBER , ARGUMENT _TYPE . RANGE ] , false , true ,
) ,
] ,
helpString : `
< div >
Shows a group member character card without switching chats .
< / d i v >
< div >
< strong > Examples : < / s t r o n g >
< ul >
< li >
< pre > < code > / p e e k 5 < / c o d e > < / p r e >
Shows the character card for the 5 th message .
< / l i >
< li >
< pre > < code > / p e e k 2 - 5 < / c o d e > < / p r e >
Shows the character cards for messages 2 through 5.
< / l i >
< / u l >
< / d i v >
` ,
} ) ) ;
2024-05-19 21:22:32 +02:00
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'delswipe' ,
2024-05-12 21:15:05 +02:00
callback : deleteSwipeCallback ,
aliases : [ 'swipedel' ] ,
unnamedArgumentList : [
new SlashCommandArgument (
'1-based swipe id' , [ ARGUMENT _TYPE . NUMBER ] ,
) ,
] ,
helpString : `
< div >
Deletes a swipe from the last chat message . If swipe id is not provided , it deletes the current swipe .
< / d i v >
< div >
< strong > Example : < / s t r o n g >
< ul >
< li >
< pre > < code > / d e l s w i p e < / c o d e > < / p r e >
Deletes the current swipe .
< / l i >
< li >
< pre > < code > / d e l s w i p e 2 < / c o d e > < / p r e >
Deletes the second swipe from the last chat message .
< / l i >
< / u l >
< / d i v >
` ,
} ) ) ;
2024-05-19 21:22:32 +02:00
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'echo' ,
2024-05-12 21:15:05 +02:00
callback : echoCallback ,
returns : 'the text' ,
namedArgumentList : [
new SlashCommandNamedArgument (
'title' , 'title of the toast message' , [ ARGUMENT _TYPE . STRING ] , false ,
) ,
new SlashCommandNamedArgument (
'severity' , 'severity level of the toast message' , [ ARGUMENT _TYPE . STRING ] , false , false , null , [ 'info' , 'warning' , 'error' , 'success' ] ,
) ,
] ,
unnamedArgumentList : [
new SlashCommandArgument (
'text' , [ ARGUMENT _TYPE . STRING ] , true ,
) ,
] ,
helpString : `
< div >
Echoes the provided text to a toast message . Useful for pipes debugging .
< / d i v >
< div >
< strong > Example : < / s t r o n g >
< ul >
< li >
< pre > < code > / e c h o t i t l e = " M y M e s s a g e " s e v e r i t y = i n f o T h i s i s a n i n f o m e s s a g e < / c o d e > < / p r e >
< / l i >
< / u l >
< / d i v >
` ,
} ) ) ;
2024-05-19 21:22:32 +02:00
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'gen' ,
2024-05-12 21:15:05 +02:00
callback : generateCallback ,
returns : 'generated text' ,
namedArgumentList : [
new SlashCommandNamedArgument (
'lock' , 'lock user input during generation' , [ ARGUMENT _TYPE . BOOLEAN ] , false , false , null , [ 'on' , 'off' ] ,
) ,
new SlashCommandNamedArgument (
'name' , 'in-prompt name for instruct mode' , [ ARGUMENT _TYPE . STRING ] , false , false , 'System' ,
) ,
new SlashCommandNamedArgument (
'length' , 'API response length in tokens' , [ ARGUMENT _TYPE . NUMBER ] , false ,
) ,
new SlashCommandNamedArgument (
'as' , 'role of the output prompt' , [ ARGUMENT _TYPE . STRING ] , false , false , 'system' , [ 'system' , 'char' ] ,
) ,
] ,
unnamedArgumentList : [
new SlashCommandArgument (
'prompt' , [ ARGUMENT _TYPE . STRING ] , true ,
) ,
] ,
helpString : `
< div >
Generates text using the provided prompt and passes it to the next command through the pipe , optionally locking user input while generating and allowing to configure the in - prompt name for instruct mode ( default = "System" ) .
< / d i v >
< div >
"as" argument controls the role of the output prompt : system ( default ) or char . If "length" argument is provided as a number in tokens , allows to temporarily override an API response length .
< / d i v >
` ,
} ) ) ;
2024-05-19 21:22:32 +02:00
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'genraw' ,
2024-05-12 21:15:05 +02:00
callback : generateRawCallback ,
returns : 'generated text' ,
namedArgumentList : [
new SlashCommandNamedArgument (
'lock' , 'lock user input during generation' , [ ARGUMENT _TYPE . BOOLEAN ] , false , false , null , [ 'on' , 'off' ] ,
) ,
new SlashCommandNamedArgument (
'instruct' , 'use instruct mode' , [ ARGUMENT _TYPE . BOOLEAN ] , false , false , 'on' , [ 'on' , 'off' ] ,
) ,
new SlashCommandNamedArgument (
'stop' , 'one-time custom stop strings' , [ ARGUMENT _TYPE . LIST ] , false ,
) ,
new SlashCommandNamedArgument (
'as' , 'role of the output prompt' , [ ARGUMENT _TYPE . STRING ] , false , false , 'system' , [ 'system' , 'char' ] ,
) ,
new SlashCommandNamedArgument (
'system' , 'system prompt at the start' , [ ARGUMENT _TYPE . STRING ] , false ,
) ,
new SlashCommandNamedArgument (
'length' , 'API response length in tokens' , [ ARGUMENT _TYPE . NUMBER ] , false ,
) ,
] ,
unnamedArgumentList : [
new SlashCommandArgument (
'prompt' , [ ARGUMENT _TYPE . STRING ] , true ,
) ,
] ,
helpString : `
< div >
Generates text using the provided prompt and passes it to the next command through the pipe , optionally locking user input while generating . Does not include chat history or character card .
< / d i v >
< div >
Use instruct = off to skip instruct formatting , e . g . < pre > < code > / g e n r a w i n s t r u c t = o f f W h y i s t h e s k y b l u e ? < / c o d e > < / p r e >
< / d i v >
< div >
Use stop = ... with a JSON - serialized array to add one - time custom stop strings , e . g . < pre > < code > / g e n r a w s t o p = [ " \ \ n " ] S a y h i < / c o d e > < / p r e >
< / d i v >
< div >
"as" argument controls the role of the output prompt : system ( default ) or char . "system" argument adds an ( optional ) system prompt at the start .
< / d i v >
< div >
If "length" argument is provided as a number in tokens , allows to temporarily override an API response length .
< / d i v >
` ,
} ) ) ;
2024-05-19 21:22:32 +02:00
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'addswipe' ,
2024-05-12 21:15:05 +02:00
callback : addSwipeCallback ,
aliases : [ 'swipeadd' ] ,
unnamedArgumentList : [
new SlashCommandArgument (
'text' , [ ARGUMENT _TYPE . STRING ] , true ,
) ,
] ,
helpString : 'Adds a swipe to the last chat message.' ,
} ) ) ;
2024-05-19 21:22:32 +02:00
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'abort' ,
2024-05-12 21:15:05 +02:00
callback : abortCallback ,
2024-05-18 20:48:31 +02:00
namedArgumentList : [
2024-05-19 21:22:32 +02:00
SlashCommandNamedArgument . fromProps ( {
name : 'quiet' ,
2024-05-18 20:48:31 +02:00
description : 'Whether to suppress the toast message notifying about the /abort call.' ,
typeList : [ ARGUMENT _TYPE . BOOLEAN ] ,
defaultValue : 'true' ,
enumList : [ 'true' , 'false' ] ,
} ) ,
] ,
unnamedArgumentList : [
2024-05-19 21:22:32 +02:00
SlashCommandArgument . fromProps ( {
description : 'The reason for aborting command execution. Shown when quiet=false' ,
2024-05-18 20:48:31 +02:00
typeList : [ ARGUMENT _TYPE . STRING ] ,
} ) ,
] ,
2024-05-12 21:15:05 +02:00
helpString : 'Aborts the slash command batch execution.' ,
} ) ) ;
2024-05-19 21:22:32 +02:00
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'fuzzy' ,
2024-05-12 21:15:05 +02:00
callback : fuzzyCallback ,
returns : 'first matching item' ,
namedArgumentList : [
new SlashCommandNamedArgument (
'list' , 'list of items to match against' , [ ARGUMENT _TYPE . LIST ] , true ,
) ,
new SlashCommandNamedArgument (
'threshold' , 'fuzzy match threshold (0.0 to 1.0)' , [ ARGUMENT _TYPE . NUMBER ] , false , false , '0.4' ,
) ,
] ,
unnamedArgumentList : [
new SlashCommandArgument (
'text to search' , [ ARGUMENT _TYPE . STRING ] , true ,
) ,
] ,
helpString : `
< div >
Performs a fuzzy match of each item in the < code > list < / c o d e > a g a i n s t t h e < c o d e > t e x t t o s e a r c h < / c o d e > .
If any item matches , then its name is returned . If no item matches the text , no value is returned .
< / d i v >
< div >
The optional < code > threshold < / c o d e > ( d e f a u l t i s 0 . 4 ) a l l o w s c o n t r o l o v e r t h e m a t c h s t r i c t n e s s .
A low value ( min 0.0 ) means the match is very strict .
At 1.0 ( max ) the match is very loose and will match anything .
< / d i v >
< div >
The returned value passes to the next command through the pipe .
< / d i v >
< div >
< strong > Example : < / s t r o n g >
< ul >
< li >
< pre > < code > / f u z z y l i s t = [ " a " , " b " , " c " ] t h r e s h o l d = 0 . 4 a b c < / c o d e > < / p r e >
< / l i >
< / u l >
< / d i v >
` ,
} ) ) ;
2024-05-19 21:22:32 +02:00
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'pass' ,
2024-05-12 21:15:05 +02:00
callback : ( _ , arg ) => arg ,
returns : 'the provided value' ,
unnamedArgumentList : [
new SlashCommandArgument (
'text' , [ ARGUMENT _TYPE . STRING , ARGUMENT _TYPE . NUMBER , ARGUMENT _TYPE . BOOLEAN , ARGUMENT _TYPE . LIST , ARGUMENT _TYPE . DICTIONARY , ARGUMENT _TYPE . CLOSURE ] , true ,
) ,
] ,
aliases : [ 'return' ] ,
helpString : `
< div >
< pre > < span class = "monospace" > / p a s s ( t e x t ) < / s p a n > – p a s s e s t h e t e x t t o t h e n e x t c o m m a n d t h r o u g h t h e p i p e . < / p r e >
< / d i v >
< div >
< strong > Example : < / s t r o n g >
< ul >
< li > < pre > < code > / p a s s H e l l o w o r l d < / c o d e > < / p r e > < / l i >
< / u l >
< / d i v >
` ,
} ) ) ;
2024-05-19 21:22:32 +02:00
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'delay' ,
2024-05-12 21:15:05 +02:00
callback : delayCallback ,
aliases : [ 'wait' , 'sleep' ] ,
unnamedArgumentList : [
new SlashCommandArgument (
'milliseconds' , [ ARGUMENT _TYPE . NUMBER ] , true ,
) ,
] ,
helpString : `
< div >
Delays the next command in the pipe by the specified number of milliseconds .
< / d i v >
< div >
< strong > Example : < / s t r o n g >
< ul >
< li >
< pre > < code > / d e l a y 1 0 0 0 < / c o d e > < / p r e >
< / l i >
< / u l >
< / d i v >
` ,
} ) ) ;
2024-05-19 21:22:32 +02:00
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'input' ,
2024-05-12 21:15:05 +02:00
aliases : [ 'prompt' ] ,
callback : inputCallback ,
returns : 'user input' ,
namedArgumentList : [
new SlashCommandNamedArgument (
'default' , 'default value of the input field' , [ ARGUMENT _TYPE . STRING ] , false , false , '"string"' ,
) ,
new SlashCommandNamedArgument (
'large' , 'show large input field' , [ ARGUMENT _TYPE . BOOLEAN ] , false , false , 'off' , [ 'on' , 'off' ] ,
) ,
new SlashCommandNamedArgument (
'wide' , 'show wide input field' , [ ARGUMENT _TYPE . BOOLEAN ] , false , false , 'off' , [ 'on' , 'off' ] ,
) ,
new SlashCommandNamedArgument (
'okButton' , 'text for the ok button' , [ ARGUMENT _TYPE . STRING ] , false ,
) ,
new SlashCommandNamedArgument (
'rows' , 'number of rows for the input field' , [ ARGUMENT _TYPE . NUMBER ] , false ,
) ,
] ,
unnamedArgumentList : [
new SlashCommandArgument (
'text to display' , [ ARGUMENT _TYPE . STRING ] , false ,
) ,
] ,
helpString : `
< div >
Shows a popup with the provided text and an input field .
The < code > default < / c o d e > a r g u m e n t i s t h e d e f a u l t v a l u e o f t h e i n p u t f i e l d , a n d t h e t e x t a r g u m e n t i s t h e t e x t t o d i s p l a y .
< / d i v >
` ,
} ) ) ;
2024-05-19 21:22:32 +02:00
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'run' ,
2024-05-12 21:15:05 +02:00
aliases : [ 'call' , 'exec' ] ,
callback : runCallback ,
returns : 'result of the executed closure of QR' ,
namedArgumentList : [
new SlashCommandNamedArgument (
'args' , 'named arguments' , [ ARGUMENT _TYPE . STRING , ARGUMENT _TYPE . NUMBER , ARGUMENT _TYPE . BOOLEAN , ARGUMENT _TYPE . LIST , ARGUMENT _TYPE . DICTIONARY ] , false , true ,
) ,
] ,
unnamedArgumentList : [
new SlashCommandArgument (
'scoped variable or qr label' , [ ARGUMENT _TYPE . VARIABLE _NAME , ARGUMENT _TYPE . STRING ] , true ,
) ,
] ,
helpString : `
< div >
Runs a closure from a scoped variable , or a Quick Reply with the specified name from a currently active preset or from another preset .
Named arguments can be referenced in a QR with < code > { { arg : : key } } < / c o d e > .
< / d i v >
` ,
} ) ) ;
2024-05-19 21:22:32 +02:00
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'messages' ,
2024-05-12 21:15:05 +02:00
callback : getMessagesCallback ,
aliases : [ 'message' ] ,
namedArgumentList : [
new SlashCommandNamedArgument (
'names' , 'show message author names' , [ ARGUMENT _TYPE . BOOLEAN ] , false , false , 'off' , [ 'off' , 'on' ] ,
) ,
2024-05-13 20:36:55 +02:00
new SlashCommandNamedArgument (
'hidden' , 'include hidden messages' , [ ARGUMENT _TYPE . BOOLEAN ] , false , false , 'on' , [ 'off' , 'on' ] ,
) ,
new SlashCommandNamedArgument (
2024-05-19 21:22:32 +02:00
'role' , 'filter messages by role' , [ ARGUMENT _TYPE . STRING ] , false , false , null , [ 'system' , 'assistant' , 'user' ] ,
2024-05-13 20:36:55 +02:00
) ,
2024-05-12 21:15:05 +02:00
] ,
unnamedArgumentList : [
new SlashCommandArgument (
'message index or range' , [ ARGUMENT _TYPE . NUMBER , ARGUMENT _TYPE . RANGE ] , false , true ,
) ,
] ,
returns : 'the specified message or range of messages as a string' ,
helpString : `
< div >
Returns the specified message or range of messages as a string .
< / d i v >
2024-05-13 20:36:55 +02:00
< div >
Use the < code > hidden = off < / c o d e > a r g u m e n t t o e x c l u d e h i d d e n m e s s a g e s .
< / d i v >
< div >
Use the < code > role < / c o d e > a r g u m e n t t o f i l t e r m e s s a g e s b y r o l e . P o s s i b l e v a l u e s a r e : s y s t e m , a s s i s t a n t , u s e r .
< / d i v >
2024-05-12 21:15:05 +02:00
< div >
< strong > Examples : < / s t r o n g >
< ul >
< li >
< pre > < code > / m e s s a g e s 1 0 < / c o d e > < / p r e >
Returns the 10 th message .
< / l i >
< li >
< pre > < code > / m e s s a g e s n a m e s = o n 5 - 1 0 < / c o d e > < / p r e >
Returns messages 5 through 10 with author names .
< / l i >
< / u l >
< / d i v >
` ,
} ) ) ;
2024-05-19 21:22:32 +02:00
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'setinput' ,
2024-05-12 21:15:05 +02:00
callback : setInputCallback ,
unnamedArgumentList : [
new SlashCommandArgument (
'text' , [ ARGUMENT _TYPE . STRING ] , true ,
) ,
] ,
helpString : `
< div >
Sets the user input to the specified text and passes it to the next command through the pipe .
< / d i v >
< div >
< strong > Example : < / s t r o n g >
< ul >
< li >
< pre > < code > / s e t i n p u t H e l l o w o r l d < / c o d e > < / p r e >
< / l i >
< / u l >
< / d i v >
` ,
} ) ) ;
2024-05-19 21:22:32 +02:00
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'popup' ,
2024-05-12 21:15:05 +02:00
callback : popupCallback ,
returns : 'popup text' ,
namedArgumentList : [
new SlashCommandNamedArgument (
'large' , 'show large popup' , [ ARGUMENT _TYPE . BOOLEAN ] , false , false , null , [ 'on' , 'off' ] ,
) ,
new SlashCommandNamedArgument (
'wide' , 'show wide popup' , [ ARGUMENT _TYPE . BOOLEAN ] , false , false , null , [ 'on' , 'off' ] ,
) ,
new SlashCommandNamedArgument (
'okButton' , 'text for the OK button' , [ ARGUMENT _TYPE . STRING ] , false ,
) ,
] ,
unnamedArgumentList : [
new SlashCommandArgument (
'text' , [ ARGUMENT _TYPE . STRING ] , true ,
) ,
] ,
helpString : `
< div >
Shows a blocking popup with the specified text and buttons .
Returns the popup text .
< / d i v >
< div >
< strong > Example : < / s t r o n g >
< ul >
< li >
< pre > < code > / p o p u p l a r g e = o n w i d e = o n o k B u t t o n = " S u b m i t " E n t e r s o m e t e x t : < / c o d e > < / p r e >
< / l i >
< / u l >
< / d i v >
` ,
} ) ) ;
2024-05-19 21:22:32 +02:00
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'buttons' ,
2024-05-12 21:15:05 +02:00
callback : buttonsCallback ,
returns : 'clicked button label' ,
namedArgumentList : [
new SlashCommandNamedArgument (
'labels' , 'button labels' , [ ARGUMENT _TYPE . LIST ] , true ,
) ,
] ,
unnamedArgumentList : [
new SlashCommandArgument (
'text' , [ ARGUMENT _TYPE . STRING ] , true ,
) ,
] ,
helpString : `
< div >
Shows a blocking popup with the specified text and buttons .
Returns the clicked button label into the pipe or empty string if canceled .
< / d i v >
< div >
< strong > Example : < / s t r o n g >
< ul >
< li >
< pre > < code > / b u t t o n s l a b e l s = [ " Y e s " , " N o " ] D o y o u w a n t t o c o n t i n u e ? < / c o d e > < / p r e >
< / l i >
< / u l >
< / d i v >
` ,
} ) ) ;
2024-05-19 21:22:32 +02:00
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'trimtokens' ,
2024-05-12 21:15:05 +02:00
callback : trimTokensCallback ,
returns : 'trimmed text' ,
namedArgumentList : [
new SlashCommandNamedArgument (
'limit' , 'number of tokens to keep' , [ ARGUMENT _TYPE . NUMBER ] , true ,
) ,
new SlashCommandNamedArgument (
'direction' , 'trim direction' , [ ARGUMENT _TYPE . STRING ] , true , false , null , [ 'start' , 'end' ] ,
) ,
] ,
unnamedArgumentList : [
new SlashCommandArgument (
'text' , [ ARGUMENT _TYPE . STRING ] , false ,
) ,
] ,
helpString : `
< div >
Trims the start or end of text to the specified number of tokens .
< / d i v >
< div >
< strong > Example : < / s t r o n g >
< ul >
< li >
< pre > < code > / t r i m t o k e n s l i m i t = 5 d i r e c t i o n = s t a r t T h i s i s a l o n g s e n t e n c e w i t h m a n y w o r d s < / c o d e > < / p r e >
< / l i >
< / u l >
< / d i v >
` ,
} ) ) ;
2024-05-19 21:22:32 +02:00
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'trimstart' ,
2024-05-12 21:15:05 +02:00
callback : trimStartCallback ,
returns : 'trimmed text' ,
unnamedArgumentList : [
new SlashCommandArgument (
'text' , [ ARGUMENT _TYPE . STRING ] , true ,
) ,
] ,
helpString : `
< div >
Trims the text to the start of the first full sentence .
< / d i v >
< div >
< strong > Example : < / s t r o n g >
< ul >
< li >
< pre > < code > / t r i m s t a r t T h i s i s a s e n t e n c e . A n d h e r e i s a n o t h e r s e n t e n c e . < / c o d e > < / p r e >
< / l i >
< / u l >
< / d i v >
` ,
} ) ) ;
2024-05-19 21:22:32 +02:00
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'trimend' ,
2024-05-12 21:15:05 +02:00
callback : trimEndCallback ,
returns : 'trimmed text' ,
unnamedArgumentList : [
new SlashCommandArgument (
'text' , [ ARGUMENT _TYPE . STRING ] , true ,
) ,
] ,
helpString : 'Trims the text to the end of the last full sentence.' ,
} ) ) ;
2024-05-19 21:22:32 +02:00
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'inject' ,
2024-05-12 21:15:05 +02:00
callback : injectCallback ,
namedArgumentList : [
new SlashCommandNamedArgument (
'id' , 'injection ID' , [ ARGUMENT _TYPE . STRING ] , true ,
) ,
new SlashCommandNamedArgument (
'position' , 'injection position' , [ ARGUMENT _TYPE . STRING ] , false , false , 'after' , [ 'before' , 'after' , 'chat' ] ,
) ,
new SlashCommandNamedArgument (
'depth' , 'injection depth' , [ ARGUMENT _TYPE . NUMBER ] , false , false , '4' ,
) ,
new SlashCommandNamedArgument (
'scan' , 'include injection content into World Info scans' , [ ARGUMENT _TYPE . BOOLEAN ] , false , false , 'false' ,
) ,
new SlashCommandNamedArgument (
'role' , 'role for in-chat injections' , [ ARGUMENT _TYPE . STRING ] , false , false , 'system' , [ 'system' , 'user' , 'assistant' ] ,
) ,
2024-05-23 01:34:13 +02:00
new SlashCommandNamedArgument (
'ephemeral' , 'remove injection after generation' , [ ARGUMENT _TYPE . BOOLEAN ] , false , false , 'false' , [ 'true' , 'false' ] ,
) ,
2024-05-12 21:15:05 +02:00
] ,
unnamedArgumentList : [
new SlashCommandArgument (
'text' , [ ARGUMENT _TYPE . STRING ] , false ,
) ,
] ,
helpString : 'Injects a text into the LLM prompt for the current chat. Requires a unique injection ID. Positions: "before" main prompt, "after" main prompt, in-"chat" (default: after). Depth: injection depth for the prompt (default: 4). Role: role for in-chat injections (default: system). Scan: include injection content into World Info scans (default: false).' ,
} ) ) ;
2024-05-19 21:22:32 +02:00
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'listinjects' ,
2024-05-12 21:15:05 +02:00
callback : listInjectsCallback ,
helpString : 'Lists all script injections for the current chat.' ,
} ) ) ;
2024-05-19 21:22:32 +02:00
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
2024-06-05 20:20:55 +02:00
name : 'flushinject' ,
aliases : [ 'flushinjects' ] ,
unnamedArgumentList : [
new SlashCommandArgument (
'injection ID or a variable name pointing to ID' , [ ARGUMENT _TYPE . STRING , ARGUMENT _TYPE . VARIABLE _NAME ] , false , false , '' ,
) ,
] ,
2024-05-12 21:15:05 +02:00
callback : flushInjectsCallback ,
2024-06-05 20:20:55 +02:00
helpString : 'Removes a script injection for the current chat. If no ID is provided, removes all script injections.' ,
2024-05-12 21:15:05 +02:00
} ) ) ;
2024-05-19 21:22:32 +02:00
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'tokens' ,
2024-05-12 21:15:05 +02:00
callback : ( _ , text ) => getTokenCountAsync ( text ) ,
returns : 'number of tokens' ,
unnamedArgumentList : [
new SlashCommandArgument (
'text' , [ ARGUMENT _TYPE . STRING ] , true ,
) ,
] ,
helpString : 'Counts the number of tokens in the provided text.' ,
} ) ) ;
2024-05-19 21:22:32 +02:00
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'model' ,
2024-05-12 21:15:05 +02:00
callback : modelCallback ,
returns : 'current model' ,
unnamedArgumentList : [
new SlashCommandArgument (
'model name' , [ ARGUMENT _TYPE . STRING ] , false ,
) ,
] ,
helpString : 'Sets the model for the current API. Gets the current model name if no argument is provided.' ,
} ) ) ;
2024-06-16 04:37:42 +02:00
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'setpromptentry' ,
aliases : [ 'setpromptentries' ] ,
callback : setPromptEntryCallback ,
namedArgumentList : [
new SlashCommandNamedArgument (
2024-06-16 12:16:56 +02:00
'identifier' , 'Prompt entry identifier(s) to target' , [ ARGUMENT _TYPE . STRING , ARGUMENT _TYPE . LIST ] , false , true ,
2024-06-16 04:37:42 +02:00
) ,
new SlashCommandNamedArgument (
2024-06-16 12:16:56 +02:00
'name' , 'Prompt entry name(s) to target' , [ ARGUMENT _TYPE . STRING , ARGUMENT _TYPE . LIST ] , false , true ,
2024-06-16 04:37:42 +02:00
) ,
] ,
unnamedArgumentList : [
2024-06-16 12:16:56 +02:00
SlashCommandArgument . fromProps ( { description : 'Set entry/entries on or off' ,
2024-06-16 04:37:42 +02:00
typeList : [ ARGUMENT _TYPE . STRING ] ,
isRequired : true ,
acceptsMultiple : false ,
defaultValue : 'toggle' , // unnamed arguments don't support default values yet
enumList : [ 'on' , 'off' , 'toggle' ] ,
} ) ,
] ,
2024-06-16 12:36:52 +02:00
helpString : 'Sets the toggles for prompt entries. Toggle by default.' ,
2024-06-16 04:37:42 +02:00
} ) ) ;
2024-04-19 13:19:57 +02:00
2023-11-23 21:36:48 +01:00
registerVariableCommands ( ) ;
2023-07-20 19:32:15 +02:00
const NARRATOR _NAME _KEY = 'narrator_name' ;
const NARRATOR _NAME _DEFAULT = 'System' ;
2023-08-26 19:26:23 +02:00
export const COMMENT _NAME _DEFAULT = 'Note' ;
2023-11-28 00:44:13 +01:00
const SCRIPT _PROMPT _KEY = 'script_inject_' ;
2023-12-07 12:35:48 +01:00
function injectCallback ( args , value ) {
2023-11-28 00:44:13 +01:00
const positions = {
'before' : extension _prompt _types . BEFORE _PROMPT ,
'after' : extension _prompt _types . IN _PROMPT ,
'chat' : extension _prompt _types . IN _CHAT ,
} ;
2024-03-23 17:44:40 +01:00
const roles = {
'system' : extension _prompt _roles . SYSTEM ,
'user' : extension _prompt _roles . USER ,
'assistant' : extension _prompt _roles . ASSISTANT ,
} ;
2023-11-28 00:44:13 +01:00
const id = resolveVariable ( args ? . id ) ;
2024-05-23 01:34:13 +02:00
const ephemeral = isTrueBoolean ( args ? . ephemeral ) ;
2023-11-28 00:44:13 +01:00
if ( ! id ) {
console . warn ( 'WARN: No ID provided for /inject command' ) ;
toastr . warning ( 'No ID provided for /inject command' ) ;
return '' ;
}
const defaultPosition = 'after' ;
const defaultDepth = 4 ;
const positionValue = args ? . position ? ? defaultPosition ;
2023-12-07 12:35:48 +01:00
const position = positions [ positionValue ] ? ? positions [ defaultPosition ] ;
2023-11-28 00:44:13 +01:00
const depthValue = Number ( args ? . depth ) ? ? defaultDepth ;
const depth = isNaN ( depthValue ) ? defaultDepth : depthValue ;
2024-03-23 17:44:40 +01:00
const roleValue = typeof args ? . role === 'string' ? args . role . toLowerCase ( ) . trim ( ) : Number ( args ? . role ? ? extension _prompt _roles . SYSTEM ) ;
const role = roles [ roleValue ] ? ? roles [ extension _prompt _roles . SYSTEM ] ;
const scan = isTrueBoolean ( args ? . scan ) ;
2023-11-28 00:44:13 +01:00
value = value || '' ;
const prefixedId = ` ${ SCRIPT _PROMPT _KEY } ${ id } ` ;
if ( ! chat _metadata . script _injects ) {
chat _metadata . script _injects = { } ;
}
2024-04-27 19:25:55 +02:00
if ( value ) {
const inject = { value , position , depth , scan , role } ;
chat _metadata . script _injects [ id ] = inject ;
} else {
delete chat _metadata . script _injects [ id ] ;
}
2023-11-28 00:44:13 +01:00
2024-03-23 17:44:40 +01:00
setExtensionPrompt ( prefixedId , value , position , depth , scan , role ) ;
2023-11-28 00:44:13 +01:00
saveMetadataDebounced ( ) ;
2024-05-23 01:34:13 +02:00
if ( ephemeral ) {
2024-05-24 20:53:29 +02:00
let deleted = false ;
2024-05-23 01:34:13 +02:00
const unsetInject = ( ) => {
2024-05-24 20:53:29 +02:00
if ( deleted ) {
return ;
}
2024-05-23 01:34:13 +02:00
console . log ( 'Removing ephemeral script injection' , id ) ;
delete chat _metadata . script _injects [ id ] ;
2024-05-24 20:53:29 +02:00
setExtensionPrompt ( prefixedId , '' , position , depth , scan , role ) ;
saveMetadataDebounced ( ) ;
deleted = true ;
2024-05-23 01:34:13 +02:00
} ;
eventSource . once ( event _types . GENERATION _ENDED , unsetInject ) ;
eventSource . once ( event _types . GENERATION _STOPPED , unsetInject ) ;
}
2023-11-28 00:44:13 +01:00
return '' ;
}
2023-12-07 12:35:48 +01:00
function listInjectsCallback ( ) {
2024-06-05 20:20:55 +02:00
if ( ! chat _metadata . script _injects || ! Object . keys ( chat _metadata . script _injects ) . length ) {
2023-11-28 00:44:13 +01:00
toastr . info ( 'No script injections for the current chat' ) ;
return '' ;
}
const injects = Object . entries ( chat _metadata . script _injects )
2023-12-07 12:35:48 +01:00
. map ( ( [ id , inject ] ) => {
2023-11-28 00:44:13 +01:00
const position = Object . entries ( extension _prompt _types ) ;
2023-12-07 12:35:48 +01:00
const positionName = position . find ( ( [ _ , value ] ) => value === inject . position ) ? . [ 0 ] ? ? 'unknown' ;
2024-03-23 17:44:40 +01:00
return ` * ** ${ id } **: <code> ${ inject . value } </code> ( ${ positionName } , depth: ${ inject . depth } , scan: ${ inject . scan ? ? false } , role: ${ inject . role ? ? extension _prompt _roles . SYSTEM } ) ` ;
2023-11-28 00:44:13 +01:00
} )
. join ( '\n' ) ;
const converter = new showdown . Converter ( ) ;
const messageText = ` ### Script injections: \n ${ injects } ` ;
const htmlMessage = DOMPurify . sanitize ( converter . makeHtml ( messageText ) ) ;
sendSystemMessage ( system _message _types . GENERIC , htmlMessage ) ;
}
2024-06-05 20:20:55 +02:00
/ * *
* Flushes script injections for the current chat .
* @ param { import ( './slash-commands/SlashCommand.js' ) . NamedArguments } args Named arguments
* @ param { string } value Unnamed argument
* @ returns { string } Empty string
* /
function flushInjectsCallback ( args , value ) {
2023-11-28 00:44:13 +01:00
if ( ! chat _metadata . script _injects ) {
return '' ;
}
2024-06-05 20:20:55 +02:00
const idArgument = resolveVariable ( value , args . _scope ) ;
2023-12-07 12:35:48 +01:00
for ( const [ id , inject ] of Object . entries ( chat _metadata . script _injects ) ) {
2024-06-05 20:20:55 +02:00
if ( idArgument && id !== idArgument ) {
continue ;
}
2023-11-28 00:44:13 +01:00
const prefixedId = ` ${ SCRIPT _PROMPT _KEY } ${ id } ` ;
2024-03-23 17:44:40 +01:00
setExtensionPrompt ( prefixedId , '' , inject . position , inject . depth , inject . scan , inject . role ) ;
2024-06-05 20:20:55 +02:00
delete chat _metadata . script _injects [ id ] ;
2023-11-28 00:44:13 +01:00
}
saveMetadataDebounced ( ) ;
return '' ;
}
2023-12-07 12:35:48 +01:00
export function processChatSlashCommands ( ) {
2023-11-28 00:44:13 +01:00
const context = getContext ( ) ;
if ( ! ( context . chatMetadata . script _injects ) ) {
return ;
}
for ( const id of Object . keys ( context . extensionPrompts ) ) {
if ( ! id . startsWith ( SCRIPT _PROMPT _KEY ) ) {
continue ;
}
console . log ( 'Removing script injection' , id ) ;
2023-12-07 12:35:48 +01:00
delete context . extensionPrompts [ id ] ;
2023-11-28 00:44:13 +01:00
}
2023-12-07 12:35:48 +01:00
for ( const [ id , inject ] of Object . entries ( context . chatMetadata . script _injects ) ) {
2023-11-28 00:44:13 +01:00
const prefixedId = ` ${ SCRIPT _PROMPT _KEY } ${ id } ` ;
console . log ( 'Adding script injection' , id ) ;
2024-03-23 17:44:40 +01:00
setExtensionPrompt ( prefixedId , inject . value , inject . position , inject . depth , inject . scan , inject . role ) ;
2023-11-28 00:44:13 +01:00
}
}
2023-07-20 19:32:15 +02:00
2023-12-07 12:35:48 +01:00
function setInputCallback ( _ , value ) {
2024-05-19 21:22:32 +02:00
$ ( '#send_textarea' ) . val ( value || '' ) [ 0 ] . dispatchEvent ( new Event ( 'input' , { bubbles : true } ) ) ;
2023-11-24 16:12:59 +01:00
return value ;
}
2023-12-07 12:35:48 +01:00
function trimStartCallback ( _ , value ) {
2023-11-26 12:55:22 +01:00
if ( ! value ) {
return '' ;
}
return trimToStartSentence ( value ) ;
}
2023-12-07 12:35:48 +01:00
function trimEndCallback ( _ , value ) {
2023-11-26 12:55:22 +01:00
if ( ! value ) {
return '' ;
}
return trimToEndSentence ( value ) ;
}
2024-04-13 20:33:19 +02:00
async function trimTokensCallback ( arg , value ) {
2023-11-26 12:55:22 +01:00
if ( ! value ) {
console . warn ( 'WARN: No argument provided for /trimtokens command' ) ;
return '' ;
}
const limit = Number ( resolveVariable ( arg . limit ) ) ;
if ( isNaN ( limit ) ) {
console . warn ( ` WARN: Invalid limit provided for /trimtokens command: ${ limit } ` ) ;
return value ;
}
if ( limit <= 0 ) {
return '' ;
}
const direction = arg . direction || 'end' ;
2024-04-13 20:33:19 +02:00
const tokenCount = await getTokenCountAsync ( value ) ;
2023-11-26 12:55:22 +01:00
// Token count is less than the limit, do nothing
if ( tokenCount <= limit ) {
return value ;
}
const { tokenizerName , tokenizerId } = getFriendlyTokenizerName ( main _api ) ;
console . debug ( 'Requesting tokenization for /trimtokens command' , tokenizerName ) ;
try {
const textTokens = getTextTokens ( tokenizerId , value ) ;
if ( ! Array . isArray ( textTokens ) || ! textTokens . length ) {
console . warn ( 'WARN: No tokens returned for /trimtokens command, falling back to estimation' ) ;
const percentage = limit / tokenCount ;
const trimIndex = Math . floor ( value . length * percentage ) ;
const trimmedText = direction === 'start' ? value . substring ( trimIndex ) : value . substring ( 0 , value . length - trimIndex ) ;
return trimmedText ;
}
const sliceTokens = direction === 'start' ? textTokens . slice ( 0 , limit ) : textTokens . slice ( - limit ) ;
2024-01-23 06:00:31 +01:00
const { text } = decodeTextTokens ( tokenizerId , sliceTokens ) ;
return text ;
2023-11-26 12:55:22 +01:00
} catch ( error ) {
console . warn ( 'WARN: Tokenization failed for /trimtokens command, returning original' , error ) ;
return value ;
}
}
2023-12-07 12:35:48 +01:00
async function buttonsCallback ( args , text ) {
2023-11-26 16:05:55 +01:00
try {
const buttons = JSON . parse ( resolveVariable ( args ? . labels ) ) ;
if ( ! Array . isArray ( buttons ) || ! buttons . length ) {
console . warn ( 'WARN: Invalid labels provided for /buttons command' ) ;
return '' ;
}
return new Promise ( async ( resolve ) => {
const safeValue = DOMPurify . sanitize ( text || '' ) ;
const buttonContainer = document . createElement ( 'div' ) ;
buttonContainer . classList . add ( 'flex-container' , 'flexFlowColumn' , 'wide100p' , 'm-t-1' ) ;
for ( const button of buttons ) {
const buttonElement = document . createElement ( 'div' ) ;
buttonElement . classList . add ( 'menu_button' , 'wide100p' ) ;
buttonElement . addEventListener ( 'click' , ( ) => {
resolve ( button ) ;
$ ( '#dialogue_popup_ok' ) . trigger ( 'click' ) ;
} ) ;
buttonElement . innerText = button ;
buttonContainer . appendChild ( buttonElement ) ;
}
const popupContainer = document . createElement ( 'div' ) ;
popupContainer . innerHTML = safeValue ;
popupContainer . appendChild ( buttonContainer ) ;
callPopup ( popupContainer , 'text' , '' , { okButton : 'Cancel' } )
. then ( ( ) => resolve ( '' ) )
. catch ( ( ) => resolve ( '' ) ) ;
2023-12-02 20:11:06 +01:00
} ) ;
2023-11-26 16:05:55 +01:00
} catch {
return '' ;
}
}
2023-12-07 12:35:48 +01:00
async function popupCallback ( args , value ) {
2023-11-24 18:50:49 +01:00
const safeValue = DOMPurify . sanitize ( value || '' ) ;
2023-12-07 12:35:48 +01:00
const popupOptions = {
2023-12-07 02:26:29 +01:00
large : isTrueBoolean ( args ? . large ) ,
wide : isTrueBoolean ( args ? . wide ) ,
2023-12-07 12:35:48 +01:00
okButton : args ? . okButton !== undefined && typeof args ? . okButton === 'string' ? args . okButton : 'Ok' ,
2023-12-07 02:26:29 +01:00
} ;
2023-11-24 18:50:49 +01:00
await delay ( 1 ) ;
2023-12-07 02:26:29 +01:00
await callPopup ( safeValue , 'text' , '' , popupOptions ) ;
2023-11-24 18:50:49 +01:00
await delay ( 1 ) ;
return value ;
}
2023-12-07 12:35:48 +01:00
function getMessagesCallback ( args , value ) {
2023-11-24 16:12:59 +01:00
const includeNames = ! isFalseBoolean ( args ? . names ) ;
2024-05-13 20:36:55 +02:00
const includeHidden = isTrueBoolean ( args ? . hidden ) ;
const role = args ? . role ;
2023-11-24 16:12:59 +01:00
const range = stringToRange ( value , 0 , chat . length - 1 ) ;
if ( ! range ) {
2024-05-13 20:36:55 +02:00
console . warn ( ` WARN: Invalid range provided for /messages command: ${ value } ` ) ;
2023-11-24 16:12:59 +01:00
return '' ;
}
2024-05-13 20:36:55 +02:00
const filterByRole = ( mes ) => {
if ( ! role ) {
return true ;
}
2024-05-13 21:45:06 +02:00
const isNarrator = mes . extra ? . type === system _message _types . NARRATOR ;
2024-05-13 20:36:55 +02:00
if ( role === 'system' ) {
2024-05-13 21:45:06 +02:00
return isNarrator && ! mes . is _user ;
2024-05-13 20:36:55 +02:00
}
if ( role === 'assistant' ) {
2024-05-13 21:45:06 +02:00
return ! isNarrator && ! mes . is _user ;
2024-05-13 20:36:55 +02:00
}
if ( role === 'user' ) {
2024-05-13 21:45:06 +02:00
return ! isNarrator && mes . is _user ;
2024-05-13 20:36:55 +02:00
}
throw new Error ( ` Invalid role provided. Expected one of: system, assistant, user. Got: ${ role } ` ) ;
} ;
2023-11-24 16:12:59 +01:00
const messages = [ ] ;
for ( let messageId = range . start ; messageId <= range . end ; messageId ++ ) {
2023-12-07 12:35:48 +01:00
const message = chat [ messageId ] ;
2023-11-24 16:12:59 +01:00
if ( ! message ) {
console . warn ( ` WARN: No message found with ID ${ messageId } ` ) ;
continue ;
}
2024-05-13 20:36:55 +02:00
if ( role && ! filterByRole ( message ) ) {
console . debug ( ` /messages: Skipping message with ID ${ messageId } due to role filter ` ) ;
continue ;
}
if ( ! includeHidden && message . is _system ) {
console . debug ( ` /messages: Skipping hidden message with ID ${ messageId } ` ) ;
2023-11-26 00:15:19 +01:00
continue ;
}
2023-11-24 16:12:59 +01:00
if ( includeNames ) {
messages . push ( ` ${ message . name } : ${ message . mes } ` ) ;
} else {
messages . push ( message . mes ) ;
}
}
return messages . join ( '\n\n' ) ;
}
2023-12-27 13:28:15 +01:00
async function runCallback ( args , name ) {
2023-11-24 14:58:00 +01:00
if ( ! name ) {
2024-05-12 21:15:05 +02:00
throw new Error ( 'No name provided for /run command' ) ;
}
/**@type {SlashCommandScope} */
const scope = args . _scope ;
if ( scope . existsVariable ( name ) ) {
const closure = scope . getVariable ( name ) ;
if ( ! ( closure instanceof SlashCommandClosure ) ) {
throw new Error ( ` " ${ name } " is not callable. ` ) ;
}
closure . scope . parent = scope ;
2024-05-19 21:22:32 +02:00
closure . argumentList . forEach ( arg => {
2024-05-12 21:15:05 +02:00
if ( Object . keys ( args ) . includes ( arg . name ) ) {
const providedArg = new SlashCommandNamedArgumentAssignment ( ) ;
providedArg . name = arg . name ;
providedArg . value = args [ arg . name ] ;
closure . providedArgumentList . push ( providedArg ) ;
}
} ) ;
const result = await closure . execute ( ) ;
return result . pipe ;
2023-11-24 14:58:00 +01:00
}
2023-12-07 12:35:48 +01:00
if ( typeof window [ 'executeQuickReplyByName' ] !== 'function' ) {
2024-05-12 21:15:05 +02:00
throw new Error ( 'Quick Reply extension is not loaded' ) ;
2023-11-24 14:58:00 +01:00
}
try {
name = name . trim ( ) ;
2023-12-27 13:28:15 +01:00
return await window [ 'executeQuickReplyByName' ] ( name , args ) ;
2023-11-24 14:58:00 +01:00
} catch ( error ) {
2024-05-12 21:15:05 +02:00
throw new Error ( ` Error running Quick Reply " ${ name } ": ${ error . message } ` , 'Error' ) ;
2023-11-24 14:58:00 +01:00
}
}
2024-05-18 20:48:31 +02:00
/ * *
*
* @ param { object } param0
* @ param { SlashCommandAbortController } param0 . _abortController
* @ param { string } [ param0 . quiet ]
* @ param { string } [ reason ]
* /
function abortCallback ( { _abortController , quiet } , reason ) {
_abortController . abort ( ( reason ? ? '' ) . toString ( ) . length == 0 ? '/abort command executed' : reason , ! isFalseBoolean ( quiet ? ? 'true' ) ) ;
2023-11-23 23:18:07 +01:00
}
2023-12-07 12:35:48 +01:00
async function delayCallback ( _ , amount ) {
2023-11-24 11:49:14 +01:00
if ( ! amount ) {
console . warn ( 'WARN: No amount provided for /delay command' ) ;
2024-06-14 23:48:41 +02:00
return '' ;
2023-11-24 11:49:14 +01:00
}
amount = Number ( amount ) ;
if ( isNaN ( amount ) ) {
amount = 0 ;
}
await delay ( amount ) ;
2024-06-14 23:48:41 +02:00
return '' ;
2023-11-24 11:49:14 +01:00
}
2023-12-07 12:35:48 +01:00
async function inputCallback ( args , prompt ) {
2023-12-07 02:26:29 +01:00
const safeValue = DOMPurify . sanitize ( prompt || '' ) ;
2023-12-07 12:44:49 +01:00
const defaultInput = args ? . default !== undefined && typeof args ? . default === 'string' ? args . default : '' ;
2023-12-07 02:26:29 +01:00
const popupOptions = {
large : isTrueBoolean ( args ? . large ) ,
wide : isTrueBoolean ( args ? . wide ) ,
2023-12-07 12:35:48 +01:00
okButton : args ? . okButton !== undefined && typeof args ? . okButton === 'string' ? args . okButton : 'Ok' ,
2023-12-07 02:26:29 +01:00
rows : args ? . rows !== undefined && typeof args ? . rows === 'string' ? isNaN ( Number ( args . rows ) ) ? 4 : Number ( args . rows ) : 4 ,
} ;
2023-11-24 11:49:14 +01:00
// Do not remove this delay, otherwise the prompt will not show up
await delay ( 1 ) ;
2023-12-07 12:44:49 +01:00
const result = await callPopup ( safeValue , 'input' , defaultInput , popupOptions ) ;
2023-11-24 11:49:14 +01:00
await delay ( 1 ) ;
return result || '' ;
}
2024-03-03 03:43:44 +01:00
/ * *
* Each item in "args.list" is searched within "search_item" using fuzzy search . If any matches it returns the matched "item" .
2024-03-03 15:04:48 +01:00
* @ param { FuzzyCommandArgs } args - arguments containing "list" ( JSON array ) and optionaly "threshold" ( float between 0.0 and 1.0 )
* @ param { string } searchInValue - the string where items of list are searched
* @ returns { string } - the matched item from the list
* @ typedef { { list : string , threshold : string } } FuzzyCommandArgs - arguments for / f u z z y c o m m a n d
* @ example / fuzzy list = [ "down" , "left" , "up" , "right" ] "he looks up" | /echo / / should return "up"
* @ link https : //www.fusejs.io/
2024-03-03 03:43:44 +01:00
* /
2024-03-03 15:04:48 +01:00
function fuzzyCallback ( args , searchInValue ) {
if ( ! searchInValue ) {
2023-11-24 01:21:50 +01:00
console . warn ( 'WARN: No argument provided for /fuzzy command' ) ;
return '' ;
}
if ( ! args . list ) {
console . warn ( 'WARN: No list argument provided for /fuzzy command' ) ;
return '' ;
}
try {
2023-11-24 16:41:49 +01:00
const list = JSON . parse ( resolveVariable ( args . list ) ) ;
2023-11-24 01:21:50 +01:00
if ( ! Array . isArray ( list ) ) {
console . warn ( 'WARN: Invalid list argument provided for /fuzzy command' ) ;
return '' ;
}
2024-03-03 03:43:44 +01:00
const params = {
2023-11-24 01:21:50 +01:00
includeScore : true ,
findAllMatches : true ,
ignoreLocation : true ,
2024-03-03 03:43:44 +01:00
threshold : 0.4 ,
} ;
// threshold determines how strict is the match, low threshold value is very strict, at 1 (nearly?) everything matches
2024-03-03 15:04:48 +01:00
if ( 'threshold' in args ) {
2024-03-03 03:43:44 +01:00
params . threshold = parseFloat ( resolveVariable ( args . threshold ) ) ;
2024-03-03 15:04:48 +01:00
if ( isNaN ( params . threshold ) ) {
2024-03-03 03:43:44 +01:00
console . warn ( 'WARN: \'threshold\' argument must be a float between 0.0 and 1.0 for /fuzzy command' ) ;
return '' ;
}
2024-03-03 15:04:48 +01:00
if ( params . threshold < 0 ) {
2024-03-03 03:43:44 +01:00
params . threshold = 0 ;
}
2024-03-03 15:04:48 +01:00
if ( params . threshold > 1 ) {
2024-03-03 03:43:44 +01:00
params . threshold = 1 ;
}
}
2024-03-03 15:04:48 +01:00
const fuse = new Fuse ( [ searchInValue ] , params ) ;
2024-03-03 03:43:44 +01:00
// each item in the "list" is searched within "search_item", if any matches it returns the matched "item"
2024-03-03 15:04:48 +01:00
for ( const searchItem of list ) {
const result = fuse . search ( searchItem ) ;
if ( result . length > 0 ) {
console . info ( 'fuzzyCallback Matched: ' + searchItem ) ;
return searchItem ;
2024-03-03 03:43:44 +01:00
}
}
return '' ;
2023-11-24 01:21:50 +01:00
} catch {
console . warn ( 'WARN: Invalid list argument provided for /fuzzy command' ) ;
return '' ;
}
}
2023-11-24 18:06:31 +01:00
function setEphemeralStopStrings ( value ) {
if ( typeof value === 'string' && value . length ) {
2023-11-23 23:51:27 +01:00
try {
2023-11-24 18:06:31 +01:00
const stopStrings = JSON . parse ( value ) ;
2023-11-23 23:51:27 +01:00
if ( Array . isArray ( stopStrings ) ) {
2023-12-07 02:26:29 +01:00
stopStrings . forEach ( stopString => addEphemeralStoppingString ( stopString ) ) ;
2023-11-23 23:51:27 +01:00
}
} catch {
// Do nothing
}
}
2023-11-24 18:06:31 +01:00
}
2023-12-07 12:35:48 +01:00
async function generateRawCallback ( args , value ) {
2023-11-24 18:06:31 +01:00
if ( ! value ) {
console . warn ( 'WARN: No argument provided for /genraw command' ) ;
return ;
}
// Prevent generate recursion
2024-05-19 21:22:32 +02:00
$ ( '#send_textarea' ) . val ( '' ) [ 0 ] . dispatchEvent ( new Event ( 'input' , { bubbles : true } ) ) ;
2023-11-24 18:06:31 +01:00
const lock = isTrueBoolean ( args ? . lock ) ;
2024-03-28 20:51:02 +01:00
const as = args ? . as || 'system' ;
const quietToLoud = as === 'char' ;
2024-03-31 18:42:12 +02:00
const systemPrompt = resolveVariable ( args ? . system ) || '' ;
2024-03-31 20:02:38 +02:00
const length = Number ( resolveVariable ( args ? . length ) ? ? 0 ) || 0 ;
2023-11-23 23:51:27 +01:00
2023-11-24 14:18:49 +01:00
try {
if ( lock ) {
deactivateSendButtons ( ) ;
}
2023-11-25 23:56:55 +01:00
setEphemeralStopStrings ( resolveVariable ( args ? . stop ) ) ;
2024-03-31 20:02:38 +02:00
const result = await generateRaw ( value , '' , isFalseBoolean ( args ? . instruct ) , quietToLoud , systemPrompt , length ) ;
2023-11-24 14:18:49 +01:00
return result ;
} finally {
if ( lock ) {
activateSendButtons ( ) ;
}
2023-11-24 18:06:31 +01:00
flushEphemeralStoppingStrings ( ) ;
2023-11-24 14:18:49 +01:00
}
2023-11-23 21:50:13 +01:00
}
2023-12-07 12:35:48 +01:00
async function generateCallback ( args , value ) {
2023-11-24 14:18:49 +01:00
if ( ! value ) {
2023-11-21 23:39:17 +01:00
console . warn ( 'WARN: No argument provided for /gen command' ) ;
return ;
}
// Prevent generate recursion
2024-05-19 21:22:32 +02:00
$ ( '#send_textarea' ) . val ( '' ) [ 0 ] . dispatchEvent ( new Event ( 'input' , { bubbles : true } ) ) ;
2023-11-24 14:18:49 +01:00
const lock = isTrueBoolean ( args ? . lock ) ;
2024-03-30 17:13:55 +01:00
const as = args ? . as || 'system' ;
const quietToLoud = as === 'char' ;
2024-03-31 20:02:38 +02:00
const length = Number ( resolveVariable ( args ? . length ) ? ? 0 ) || 0 ;
2023-11-21 23:39:17 +01:00
2023-11-24 14:18:49 +01:00
try {
if ( lock ) {
deactivateSendButtons ( ) ;
}
2023-11-25 23:56:55 +01:00
setEphemeralStopStrings ( resolveVariable ( args ? . stop ) ) ;
2024-01-12 18:16:42 +01:00
const name = args ? . name ;
2024-03-31 20:02:38 +02:00
const result = await generateQuietPrompt ( value , quietToLoud , false , '' , name , length ) ;
2023-11-24 14:18:49 +01:00
return result ;
} finally {
if ( lock ) {
activateSendButtons ( ) ;
}
2023-11-24 18:06:31 +01:00
flushEphemeralStoppingStrings ( ) ;
2023-11-24 14:18:49 +01:00
}
2023-11-21 23:39:17 +01:00
}
2023-12-07 12:35:48 +01:00
async function echoCallback ( args , value ) {
2023-12-07 18:37:55 +01:00
const safeValue = DOMPurify . sanitize ( String ( value ) || '' ) ;
2023-12-07 12:35:48 +01:00
if ( safeValue === '' ) {
2023-11-21 23:39:17 +01:00
console . warn ( 'WARN: No argument provided for /echo command' ) ;
return ;
}
2023-12-07 02:26:29 +01:00
const title = args ? . title !== undefined && typeof args ? . title === 'string' ? args . title : undefined ;
2023-12-07 12:35:48 +01:00
const severity = args ? . severity !== undefined && typeof args ? . severity === 'string' ? args . severity : 'info' ;
2023-12-07 02:26:29 +01:00
switch ( severity ) {
case 'error' :
toastr . error ( safeValue , title ) ;
break ;
case 'warning' :
toastr . warning ( safeValue , title ) ;
break ;
case 'success' :
toastr . success ( safeValue , title ) ;
break ;
case 'info' :
default :
toastr . info ( safeValue , title ) ;
break ;
}
2023-12-07 18:37:55 +01:00
return value ;
2023-11-21 23:39:17 +01:00
}
2023-12-07 12:35:48 +01:00
async function addSwipeCallback ( _ , arg ) {
const lastMessage = chat [ chat . length - 1 ] ;
2023-11-22 00:26:17 +01:00
if ( ! lastMessage ) {
2023-12-02 19:04:51 +01:00
toastr . warning ( 'No messages to add swipes to.' ) ;
2024-06-14 23:48:41 +02:00
return '' ;
2023-11-22 00:26:17 +01:00
}
if ( ! arg ) {
console . warn ( 'WARN: No argument provided for /addswipe command' ) ;
2024-06-14 23:48:41 +02:00
return '' ;
2023-11-22 00:26:17 +01:00
}
if ( lastMessage . is _user ) {
2023-12-02 19:04:51 +01:00
toastr . warning ( 'Can\'t add swipes to user messages.' ) ;
2024-06-14 23:48:41 +02:00
return '' ;
2023-11-22 00:26:17 +01:00
}
if ( lastMessage . is _system ) {
2023-12-02 19:04:51 +01:00
toastr . warning ( 'Can\'t add swipes to system messages.' ) ;
2024-06-14 23:48:41 +02:00
return '' ;
2023-11-22 00:26:17 +01:00
}
if ( lastMessage . extra ? . image ) {
2023-12-02 19:04:51 +01:00
toastr . warning ( 'Can\'t add swipes to message containing an image.' ) ;
2024-06-14 23:48:41 +02:00
return '' ;
2023-11-22 00:26:17 +01:00
}
if ( ! Array . isArray ( lastMessage . swipes ) ) {
2023-12-07 12:35:48 +01:00
lastMessage . swipes = [ lastMessage . mes ] ;
lastMessage . swipe _info = [ { } ] ;
2023-11-22 00:26:17 +01:00
lastMessage . swipe _id = 0 ;
}
lastMessage . swipes . push ( arg ) ;
lastMessage . swipe _info . push ( {
send _date : getMessageTimeStamp ( ) ,
gen _started : null ,
gen _finished : null ,
extra : {
bias : extractMessageBias ( arg ) ,
gen _id : Date . now ( ) ,
api : 'manual' ,
model : 'slash command' ,
2023-12-02 21:06:57 +01:00
} ,
2023-11-23 23:18:07 +01:00
} ) ;
2023-11-22 00:26:17 +01:00
await saveChatConditional ( ) ;
await reloadCurrentChat ( ) ;
2024-06-14 23:48:41 +02:00
return '' ;
2023-11-22 00:26:17 +01:00
}
2023-12-07 12:35:48 +01:00
async function deleteSwipeCallback ( _ , arg ) {
const lastMessage = chat [ chat . length - 1 ] ;
2023-11-21 22:28:11 +01:00
if ( ! lastMessage || ! Array . isArray ( lastMessage . swipes ) || ! lastMessage . swipes . length ) {
2023-12-02 19:04:51 +01:00
toastr . warning ( 'No messages to delete swipes from.' ) ;
2024-06-14 23:48:41 +02:00
return '' ;
2023-11-21 22:28:11 +01:00
}
if ( lastMessage . swipes . length <= 1 ) {
2023-12-02 19:04:51 +01:00
toastr . warning ( 'Can\'t delete the last swipe.' ) ;
2024-06-14 23:48:41 +02:00
return '' ;
2023-11-21 22:28:11 +01:00
}
const swipeId = arg && ! isNaN ( Number ( arg ) ) ? ( Number ( arg ) - 1 ) : lastMessage . swipe _id ;
2023-11-21 22:33:20 +01:00
if ( swipeId < 0 || swipeId >= lastMessage . swipes . length ) {
toastr . warning ( ` Invalid swipe ID: ${ swipeId + 1 } ` ) ;
2024-06-14 23:48:41 +02:00
return '' ;
2023-11-21 22:33:20 +01:00
}
2023-11-21 22:28:11 +01:00
lastMessage . swipes . splice ( swipeId , 1 ) ;
if ( Array . isArray ( lastMessage . swipe _info ) && lastMessage . swipe _info . length ) {
lastMessage . swipe _info . splice ( swipeId , 1 ) ;
}
const newSwipeId = Math . min ( swipeId , lastMessage . swipes . length - 1 ) ;
lastMessage . swipe _id = newSwipeId ;
2023-12-07 12:35:48 +01:00
lastMessage . mes = lastMessage . swipes [ newSwipeId ] ;
2023-11-21 22:28:11 +01:00
await saveChatConditional ( ) ;
await reloadCurrentChat ( ) ;
2024-06-14 23:48:41 +02:00
return '' ;
2023-11-21 22:28:11 +01:00
}
2024-06-05 20:37:33 +02:00
async function askCharacter ( args , text ) {
2023-10-16 08:12:12 +02:00
// Prevent generate recursion
2024-05-19 21:22:32 +02:00
$ ( '#send_textarea' ) . val ( '' ) [ 0 ] . dispatchEvent ( new Event ( 'input' , { bubbles : true } ) ) ;
2023-10-16 08:12:12 +02:00
// Not supported in group chats
// TODO: Maybe support group chats?
if ( selected _group ) {
2023-12-02 19:04:51 +01:00
toastr . error ( 'Cannot run this command in a group chat!' ) ;
2024-06-14 23:48:41 +02:00
return '' ;
2023-10-16 08:12:12 +02:00
}
if ( ! text ) {
2023-12-02 20:11:06 +01:00
console . warn ( 'WARN: No text provided for /ask command' ) ;
2024-06-05 20:37:33 +02:00
toastr . warning ( 'No text provided for /ask command' ) ;
2024-06-14 23:48:41 +02:00
return '' ;
2023-10-16 08:12:12 +02:00
}
2024-06-05 20:37:33 +02:00
let name = '' ;
let mesText = '' ;
if ( args ? . name ) {
name = args . name . trim ( ) ;
mesText = text . trim ( ) ;
if ( ! name && ! mesText ) {
toastr . warning ( 'You must specify a name and text to ask.' ) ;
2024-06-14 23:48:41 +02:00
return '' ;
2024-06-05 20:37:33 +02:00
}
2023-10-16 08:12:12 +02:00
}
2024-06-05 20:37:33 +02:00
mesText = getRegexedString ( mesText , regex _placement . SLASH _COMMAND ) ;
2023-10-16 08:12:12 +02:00
const prevChId = this _chid ;
// Find the character
const chId = characters . findIndex ( ( e ) => e . name === name ) ;
2023-12-07 12:35:48 +01:00
if ( ! characters [ chId ] || chId === - 1 ) {
2023-12-02 19:04:51 +01:00
toastr . error ( 'Character not found.' ) ;
2024-06-14 23:48:41 +02:00
return '' ;
2023-10-16 08:12:12 +02:00
}
// Override character and send a user message
2024-06-05 20:37:33 +02:00
setCharacterId ( String ( chId ) ) ;
2023-10-16 08:12:12 +02:00
// TODO: Maybe look up by filename instead of name
2023-12-07 12:35:48 +01:00
const character = characters [ chId ] ;
2023-10-16 08:12:12 +02:00
let force _avatar , original _avatar ;
if ( character && character . avatar !== 'none' ) {
force _avatar = getThumbnailUrl ( 'avatar' , character . avatar ) ;
original _avatar = character . avatar ;
}
else {
force _avatar = default _avatar ;
original _avatar = default _avatar ;
}
setCharacterName ( character . name ) ;
2023-11-27 18:48:49 +01:00
await sendMessageAsUser ( mesText , '' ) ;
2023-10-16 08:12:12 +02:00
const restoreCharacter = ( ) => {
setCharacterId ( prevChId ) ;
2023-12-07 12:35:48 +01:00
setCharacterName ( characters [ prevChId ] . name ) ;
2023-10-16 08:12:12 +02:00
// Only force the new avatar if the character name is the same
// This skips if an error was fired
2023-12-07 12:35:48 +01:00
const lastMessage = chat [ chat . length - 1 ] ;
2023-10-16 08:12:12 +02:00
if ( lastMessage && lastMessage ? . name === character . name ) {
lastMessage . force _avatar = force _avatar ;
lastMessage . original _avatar = original _avatar ;
}
// Kill this callback once the event fires
2023-12-02 20:11:06 +01:00
eventSource . removeListener ( event _types . CHARACTER _MESSAGE _RENDERED , restoreCharacter ) ;
} ;
2023-10-16 08:12:12 +02:00
// Run generate and restore previous character on error
try {
toastr . info ( ` Asking ${ character . name } something... ` ) ;
2023-12-02 20:11:06 +01:00
await Generate ( 'ask_command' ) ;
2023-10-16 08:12:12 +02:00
} catch {
2023-12-02 20:11:06 +01:00
restoreCharacter ( ) ;
2023-10-16 08:12:12 +02:00
}
// Restore previous character once message renders
// Hack for generate
eventSource . on ( event _types . CHARACTER _MESSAGE _RENDERED , restoreCharacter ) ;
2024-06-14 23:48:41 +02:00
return '' ;
2023-10-16 08:12:12 +02:00
}
2023-12-07 12:35:48 +01:00
async function hideMessageCallback ( _ , arg ) {
2023-11-03 22:45:56 +01:00
if ( ! arg ) {
console . warn ( 'WARN: No argument provided for /hide command' ) ;
2024-06-14 23:48:41 +02:00
return '' ;
2023-11-03 22:45:56 +01:00
}
2023-11-14 20:37:37 +01:00
const range = stringToRange ( arg , 0 , chat . length - 1 ) ;
2023-11-03 22:45:56 +01:00
2023-11-14 20:37:37 +01:00
if ( ! range ) {
console . warn ( ` WARN: Invalid range provided for /hide command: ${ arg } ` ) ;
2024-06-14 23:48:41 +02:00
return '' ;
2023-11-03 22:45:56 +01:00
}
2024-04-12 09:03:50 +02:00
await hideChatMessageRange ( range . start , range . end , false ) ;
2024-06-14 23:48:41 +02:00
return '' ;
2023-11-03 22:45:56 +01:00
}
2023-12-07 12:35:48 +01:00
async function unhideMessageCallback ( _ , arg ) {
2023-11-03 22:45:56 +01:00
if ( ! arg ) {
console . warn ( 'WARN: No argument provided for /unhide command' ) ;
2023-11-27 01:18:36 +01:00
return '' ;
2023-11-03 22:45:56 +01:00
}
2023-11-14 20:37:37 +01:00
const range = stringToRange ( arg , 0 , chat . length - 1 ) ;
2023-11-03 22:45:56 +01:00
2023-11-14 20:37:37 +01:00
if ( ! range ) {
console . warn ( ` WARN: Invalid range provided for /unhide command: ${ arg } ` ) ;
2023-11-27 01:18:36 +01:00
return '' ;
2023-11-03 22:45:56 +01:00
}
2024-04-12 09:03:50 +02:00
await hideChatMessageRange ( range . start , range . end , true ) ;
2023-11-27 01:18:36 +01:00
return '' ;
2023-11-03 22:45:56 +01:00
}
2023-12-18 00:39:37 +01:00
/ * *
* Copium for running group actions when the member is offscreen .
* @ param { number } chid - character ID
2023-12-19 22:12:14 +01:00
* @ param { string } action - one of 'enable' , 'disable' , 'up' , 'down' , 'view' , 'remove'
2023-12-18 00:39:37 +01:00
* @ returns { void }
* /
function performGroupMemberAction ( chid , action ) {
const memberSelector = ` .group_member[chid=" ${ chid } "] ` ;
// Do not optimize. Paginator gets recreated on every action
const paginationSelector = '#rm_group_members_pagination' ;
const pageSizeSelector = '#rm_group_members_pagination select' ;
let wasOffscreen = false ;
let paginationValue = null ;
let pageValue = null ;
if ( $ ( memberSelector ) . length === 0 ) {
wasOffscreen = true ;
paginationValue = Number ( $ ( pageSizeSelector ) . val ( ) ) ;
pageValue = $ ( paginationSelector ) . pagination ( 'getCurrentPageNum' ) ;
$ ( pageSizeSelector ) . val ( $ ( pageSizeSelector ) . find ( 'option' ) . last ( ) . val ( ) ) . trigger ( 'change' ) ;
}
$ ( memberSelector ) . find ( ` [data-action=" ${ action } "] ` ) . trigger ( 'click' ) ;
if ( wasOffscreen ) {
$ ( pageSizeSelector ) . val ( paginationValue ) . trigger ( 'change' ) ;
2023-12-19 22:12:14 +01:00
if ( $ ( paginationSelector ) . length ) {
$ ( paginationSelector ) . pagination ( 'go' , pageValue ) ;
}
2023-12-18 00:39:37 +01:00
}
}
2023-12-07 12:35:48 +01:00
async function disableGroupMemberCallback ( _ , arg ) {
2023-10-29 18:35:26 +01:00
if ( ! selected _group ) {
2023-12-02 19:04:51 +01:00
toastr . warning ( 'Cannot run /disable command outside of a group chat.' ) ;
2023-11-27 01:18:36 +01:00
return '' ;
2023-11-11 15:12:02 +01:00
}
2023-11-20 22:49:04 +01:00
const chid = findGroupMemberId ( arg ) ;
if ( chid === undefined ) {
console . warn ( ` WARN: No group member found for argument ${ arg } ` ) ;
2023-11-27 01:18:36 +01:00
return '' ;
2023-10-29 18:35:26 +01:00
}
2023-12-18 00:39:37 +01:00
performGroupMemberAction ( chid , 'disable' ) ;
2023-11-27 01:18:36 +01:00
return '' ;
2023-11-20 22:49:04 +01:00
}
2023-10-29 18:35:26 +01:00
2023-12-07 12:35:48 +01:00
async function enableGroupMemberCallback ( _ , arg ) {
2023-11-20 22:49:04 +01:00
if ( ! selected _group ) {
2023-12-02 19:04:51 +01:00
toastr . warning ( 'Cannot run /enable command outside of a group chat.' ) ;
2023-11-27 01:18:36 +01:00
return '' ;
2023-10-29 18:35:26 +01:00
}
2023-11-20 22:49:04 +01:00
const chid = findGroupMemberId ( arg ) ;
2023-10-29 18:35:26 +01:00
2023-11-20 22:49:04 +01:00
if ( chid === undefined ) {
console . warn ( ` WARN: No group member found for argument ${ arg } ` ) ;
2023-11-27 01:18:36 +01:00
return '' ;
2023-10-29 18:35:26 +01:00
}
2023-12-18 00:39:37 +01:00
performGroupMemberAction ( chid , 'enable' ) ;
2023-11-27 01:18:36 +01:00
return '' ;
2023-11-20 22:49:04 +01:00
}
2023-10-29 18:35:26 +01:00
2023-12-07 12:35:48 +01:00
async function moveGroupMemberUpCallback ( _ , arg ) {
2023-11-21 21:35:59 +01:00
if ( ! selected _group ) {
2023-12-02 19:04:51 +01:00
toastr . warning ( 'Cannot run /memberup command outside of a group chat.' ) ;
2023-11-27 01:18:36 +01:00
return '' ;
2023-11-21 21:35:59 +01:00
}
const chid = findGroupMemberId ( arg ) ;
if ( chid === undefined ) {
console . warn ( ` WARN: No group member found for argument ${ arg } ` ) ;
2023-11-27 01:18:36 +01:00
return '' ;
2023-11-21 21:35:59 +01:00
}
2023-12-18 00:39:37 +01:00
performGroupMemberAction ( chid , 'up' ) ;
2023-11-27 01:18:36 +01:00
return '' ;
2023-11-21 21:35:59 +01:00
}
2023-12-07 12:35:48 +01:00
async function moveGroupMemberDownCallback ( _ , arg ) {
2023-11-21 21:35:59 +01:00
if ( ! selected _group ) {
2023-12-02 19:04:51 +01:00
toastr . warning ( 'Cannot run /memberdown command outside of a group chat.' ) ;
2023-11-27 01:18:36 +01:00
return '' ;
2023-11-21 21:35:59 +01:00
}
const chid = findGroupMemberId ( arg ) ;
if ( chid === undefined ) {
console . warn ( ` WARN: No group member found for argument ${ arg } ` ) ;
2023-11-27 01:18:36 +01:00
return '' ;
2023-11-21 21:35:59 +01:00
}
2023-12-18 00:39:37 +01:00
performGroupMemberAction ( chid , 'down' ) ;
2023-11-27 01:18:36 +01:00
return '' ;
2023-11-21 21:35:59 +01:00
}
2023-12-07 12:35:48 +01:00
async function peekCallback ( _ , arg ) {
2023-11-21 21:35:59 +01:00
if ( ! selected _group ) {
2023-12-02 19:04:51 +01:00
toastr . warning ( 'Cannot run /peek command outside of a group chat.' ) ;
2023-11-27 01:18:36 +01:00
return '' ;
2023-11-21 21:35:59 +01:00
}
if ( is _group _generating ) {
2023-12-02 19:04:51 +01:00
toastr . warning ( 'Cannot run /peek command while the group reply is generating.' ) ;
2023-11-27 01:18:36 +01:00
return '' ;
2023-11-21 21:35:59 +01:00
}
const chid = findGroupMemberId ( arg ) ;
if ( chid === undefined ) {
console . warn ( ` WARN: No group member found for argument ${ arg } ` ) ;
2023-11-27 01:18:36 +01:00
return '' ;
2023-11-21 21:35:59 +01:00
}
2023-12-19 22:12:14 +01:00
performGroupMemberAction ( chid , 'view' ) ;
2023-11-27 01:18:36 +01:00
return '' ;
2023-11-21 21:35:59 +01:00
}
2023-12-07 12:35:48 +01:00
async function removeGroupMemberCallback ( _ , arg ) {
2023-11-21 21:35:59 +01:00
if ( ! selected _group ) {
2023-12-02 19:04:51 +01:00
toastr . warning ( 'Cannot run /memberremove command outside of a group chat.' ) ;
2023-11-27 01:18:36 +01:00
return '' ;
2023-11-21 21:35:59 +01:00
}
if ( is _group _generating ) {
2023-12-02 19:04:51 +01:00
toastr . warning ( 'Cannot run /memberremove command while the group reply is generating.' ) ;
2023-11-27 01:18:36 +01:00
return '' ;
2023-11-21 21:35:59 +01:00
}
const chid = findGroupMemberId ( arg ) ;
if ( chid === undefined ) {
console . warn ( ` WARN: No group member found for argument ${ arg } ` ) ;
2023-11-27 01:18:36 +01:00
return '' ;
2023-11-21 21:35:59 +01:00
}
2023-12-18 00:39:37 +01:00
performGroupMemberAction ( chid , 'remove' ) ;
2023-11-27 01:18:36 +01:00
return '' ;
2023-11-21 21:35:59 +01:00
}
2023-12-07 12:35:48 +01:00
async function addGroupMemberCallback ( _ , arg ) {
2023-11-21 21:35:59 +01:00
if ( ! selected _group ) {
2023-12-02 19:04:51 +01:00
toastr . warning ( 'Cannot run /memberadd command outside of a group chat.' ) ;
2023-11-27 01:18:36 +01:00
return '' ;
2023-11-21 21:35:59 +01:00
}
if ( ! arg ) {
console . warn ( 'WARN: No argument provided for /memberadd command' ) ;
2023-11-27 01:18:36 +01:00
return '' ;
2023-11-21 21:35:59 +01:00
}
arg = arg . trim ( ) ;
const chid = findCharacterIndex ( arg ) ;
if ( chid === - 1 ) {
console . warn ( ` WARN: No character found for argument ${ arg } ` ) ;
2023-11-27 01:18:36 +01:00
return '' ;
2023-11-21 21:35:59 +01:00
}
2023-12-07 12:35:48 +01:00
const character = characters [ chid ] ;
2023-11-21 21:35:59 +01:00
const group = groups . find ( x => x . id === selected _group ) ;
if ( ! group || ! Array . isArray ( group . members ) ) {
console . warn ( ` WARN: No group found for ID ${ selected _group } ` ) ;
2023-11-27 01:18:36 +01:00
return '' ;
2023-11-21 21:35:59 +01:00
}
const avatar = character . avatar ;
if ( group . members . includes ( avatar ) ) {
toastr . warning ( ` ${ character . name } is already a member of this group. ` ) ;
2023-11-27 01:18:36 +01:00
return '' ;
2023-11-21 21:35:59 +01:00
}
group . members . push ( avatar ) ;
await saveGroupChat ( selected _group , true ) ;
// Trigger to reload group UI
$ ( '#rm_button_selected_ch' ) . trigger ( 'click' ) ;
2023-11-27 01:18:36 +01:00
return character . name ;
2023-11-21 21:35:59 +01:00
}
2024-02-07 23:20:36 +01:00
async function triggerGenerationCallback ( args , value ) {
const shouldAwait = isTrueBoolean ( args ? . await ) ;
const outerPromise = new Promise ( ( outerResolve ) => setTimeout ( async ( ) => {
2023-12-02 21:34:46 +01:00
try {
await waitUntilCondition ( ( ) => ! is _send _press && ! is _group _generating , 10000 , 100 ) ;
} catch {
console . warn ( 'Timeout waiting for generation unlock' ) ;
toastr . warning ( 'Cannot run /trigger command while the reply is being generated.' ) ;
return '' ;
}
2023-10-29 18:35:26 +01:00
2023-12-02 21:34:46 +01:00
// Prevent generate recursion
2024-05-19 21:22:32 +02:00
$ ( '#send_textarea' ) . val ( '' ) [ 0 ] . dispatchEvent ( new Event ( 'input' , { bubbles : true } ) ) ;
2023-10-29 18:35:26 +01:00
2023-12-02 21:34:46 +01:00
let chid = undefined ;
2023-10-29 18:35:26 +01:00
2024-02-07 23:20:36 +01:00
if ( selected _group && value ) {
chid = findGroupMemberId ( value ) ;
2023-11-27 10:16:40 +01:00
2023-12-02 21:34:46 +01:00
if ( chid === undefined ) {
2024-02-07 23:20:36 +01:00
console . warn ( ` WARN: No group member found for argument ${ value } ` ) ;
2023-12-02 21:34:46 +01:00
}
2023-11-27 10:16:40 +01:00
}
2023-11-20 22:49:04 +01:00
2024-02-07 23:20:36 +01:00
outerResolve ( new Promise ( innerResolve => setTimeout ( ( ) => innerResolve ( Generate ( 'normal' , { force _chid : chid } ) ) , 100 ) ) ) ;
} , 1 ) ) ;
if ( shouldAwait ) {
const innerPromise = await outerPromise ;
await innerPromise ;
}
2023-12-02 21:34:46 +01:00
2023-11-27 01:18:36 +01:00
return '' ;
2023-10-29 18:35:26 +01:00
}
2024-05-06 00:18:59 +02:00
/ * *
* Find persona by name .
* @ param { string } name Name to search for
* @ returns { string } Persona name
* /
function findPersonaByName ( name ) {
if ( ! name ) {
return null ;
}
for ( const persona of Object . entries ( power _user . personas ) ) {
if ( persona [ 1 ] . toLowerCase ( ) === name . toLowerCase ( ) ) {
return persona [ 0 ] ;
}
}
return null ;
}
2023-10-29 18:35:26 +01:00
2023-12-07 12:35:48 +01:00
async function sendUserMessageCallback ( args , text ) {
2023-07-26 20:00:36 +02:00
if ( ! text ) {
console . warn ( 'WARN: No text provided for /send command' ) ;
return ;
}
text = text . trim ( ) ;
2024-03-27 18:40:34 +01:00
const compact = isTrueBoolean ( args ? . compact ) ;
2023-07-26 20:00:36 +02:00
const bias = extractMessageBias ( text ) ;
2023-11-27 18:48:49 +01:00
const insertAt = Number ( resolveVariable ( args ? . at ) ) ;
2024-05-06 00:18:59 +02:00
if ( 'name' in args ) {
const name = resolveVariable ( args . name ) || '' ;
const avatar = findPersonaByName ( name ) || user _avatar ;
await sendMessageAsUser ( text , bias , insertAt , compact , name , avatar ) ;
}
else {
await sendMessageAsUser ( text , bias , insertAt , compact ) ;
}
2023-11-27 01:18:36 +01:00
return '' ;
2023-07-26 20:00:36 +02:00
}
2023-12-07 12:35:48 +01:00
async function deleteMessagesByNameCallback ( _ , name ) {
2023-07-20 19:32:15 +02:00
if ( ! name ) {
console . warn ( 'WARN: No name provided for /delname command' ) ;
return ;
}
name = name . trim ( ) ;
const messagesToDelete = [ ] ;
chat . forEach ( ( value ) => {
if ( value . name === name ) {
messagesToDelete . push ( value ) ;
}
} ) ;
if ( ! messagesToDelete . length ) {
console . debug ( '/delname: Nothing to delete' ) ;
return ;
}
for ( const message of messagesToDelete ) {
const index = chat . indexOf ( message ) ;
if ( index !== - 1 ) {
console . debug ( ` /delname: Deleting message # ${ index } ` , message ) ;
chat . splice ( index , 1 ) ;
}
}
await saveChatConditional ( ) ;
await reloadCurrentChat ( ) ;
toastr . info ( ` Deleted ${ messagesToDelete . length } messages from ${ name } ` ) ;
2023-11-27 01:18:36 +01:00
return '' ;
2023-07-20 19:32:15 +02:00
}
2023-12-07 12:35:48 +01:00
function findCharacterIndex ( name ) {
2023-07-20 19:32:15 +02:00
const matchTypes = [
( a , b ) => a === b ,
( a , b ) => a . startsWith ( b ) ,
( a , b ) => a . includes ( b ) ,
] ;
2024-02-16 19:24:47 +01:00
const exactAvatarMatch = characters . findIndex ( x => x . avatar === name ) ;
if ( exactAvatarMatch !== - 1 ) {
return exactAvatarMatch ;
}
2023-07-20 19:32:15 +02:00
for ( const matchType of matchTypes ) {
const index = characters . findIndex ( x => matchType ( x . name . toLowerCase ( ) , name . toLowerCase ( ) ) ) ;
if ( index !== - 1 ) {
return index ;
}
}
return - 1 ;
}
2023-12-07 12:35:48 +01:00
async function goToCharacterCallback ( _ , name ) {
2023-07-20 19:32:15 +02:00
if ( ! name ) {
console . warn ( 'WARN: No character name provided for /go command' ) ;
return ;
}
name = name . trim ( ) ;
const characterIndex = findCharacterIndex ( name ) ;
if ( characterIndex !== - 1 ) {
2023-11-27 01:18:36 +01:00
await openChat ( new String ( characterIndex ) ) ;
2024-03-26 17:21:22 +01:00
setActiveCharacter ( characters [ characterIndex ] ? . avatar ) ;
setActiveGroup ( null ) ;
2023-12-07 12:35:48 +01:00
return characters [ characterIndex ] ? . name ;
2023-07-20 19:32:15 +02:00
} else {
2024-01-05 19:12:59 +01:00
const group = groups . find ( it => it . name . toLowerCase ( ) == name . toLowerCase ( ) ) ;
2024-01-05 19:02:44 +01:00
if ( group ) {
await openGroupById ( group . id ) ;
2024-03-26 17:21:22 +01:00
setActiveCharacter ( null ) ;
setActiveGroup ( group . id ) ;
2024-01-05 19:02:44 +01:00
return group . name ;
} else {
console . warn ( ` No matches found for name " ${ name } " ` ) ;
return '' ;
}
2023-07-20 19:32:15 +02:00
}
}
2023-12-07 12:35:48 +01:00
async function openChat ( id ) {
2023-07-20 19:32:15 +02:00
resetSelectedGroup ( ) ;
setCharacterId ( id ) ;
2023-11-27 01:18:36 +01:00
await delay ( 1 ) ;
await reloadCurrentChat ( ) ;
2023-07-20 19:32:15 +02:00
}
2024-02-19 01:17:04 +01:00
function continueChatCallback ( _ , prompt ) {
2023-12-02 21:34:46 +01:00
setTimeout ( async ( ) => {
try {
await waitUntilCondition ( ( ) => ! is _send _press && ! is _group _generating , 10000 , 100 ) ;
} catch {
console . warn ( 'Timeout waiting for generation unlock' ) ;
toastr . warning ( 'Cannot run /continue command while the reply is being generated.' ) ;
}
// Prevent infinite recursion
2024-05-19 21:22:32 +02:00
$ ( '#send_textarea' ) . val ( '' ) [ 0 ] . dispatchEvent ( new Event ( 'input' , { bubbles : true } ) ) ;
2024-02-19 01:17:04 +01:00
$ ( '#option_continue' ) . trigger ( 'click' , { fromSlashCommand : true , additionalPrompt : prompt } ) ;
2023-12-02 21:34:46 +01:00
} , 1 ) ;
2023-12-02 19:12:36 +01:00
return '' ;
2023-07-20 19:32:15 +02:00
}
2023-12-07 12:35:48 +01:00
export async function generateSystemMessage ( _ , prompt ) {
2024-05-19 21:22:32 +02:00
$ ( '#send_textarea' ) . val ( '' ) [ 0 ] . dispatchEvent ( new Event ( 'input' , { bubbles : true } ) ) ;
2023-07-20 19:32:15 +02:00
if ( ! prompt ) {
console . warn ( 'WARN: No prompt provided for /sysgen command' ) ;
toastr . warning ( 'You must provide a prompt for the system message' ) ;
2024-06-14 23:48:41 +02:00
return '' ;
2023-07-20 19:32:15 +02:00
}
// Generate and regex the output if applicable
toastr . info ( 'Please wait' , 'Generating...' ) ;
2024-04-19 13:19:57 +02:00
let message = await generateQuietPrompt ( prompt , false , false ) ;
2023-07-20 19:32:15 +02:00
message = getRegexedString ( message , regex _placement . SLASH _COMMAND ) ;
sendNarratorMessage ( _ , message ) ;
2024-06-14 23:48:41 +02:00
return '' ;
2023-07-20 19:32:15 +02:00
}
2023-12-07 12:35:48 +01:00
function syncCallback ( ) {
2023-07-20 19:32:15 +02:00
$ ( '#sync_name_button' ) . trigger ( 'click' ) ;
2024-06-14 23:48:41 +02:00
return '' ;
2023-07-20 19:32:15 +02:00
}
2023-12-07 12:35:48 +01:00
function bindCallback ( ) {
2023-07-20 19:32:15 +02:00
$ ( '#lock_user_name' ) . trigger ( 'click' ) ;
2024-06-14 23:48:41 +02:00
return '' ;
2023-07-20 19:32:15 +02:00
}
2023-12-07 12:35:48 +01:00
function setStoryModeCallback ( ) {
2023-07-20 19:32:15 +02:00
$ ( '#chat_display' ) . val ( chat _styles . DOCUMENT ) . trigger ( 'change' ) ;
2024-06-14 23:48:41 +02:00
return '' ;
2023-07-20 19:32:15 +02:00
}
2023-12-07 12:35:48 +01:00
function setBubbleModeCallback ( ) {
2023-07-20 19:32:15 +02:00
$ ( '#chat_display' ) . val ( chat _styles . BUBBLES ) . trigger ( 'change' ) ;
2024-06-14 23:48:41 +02:00
return '' ;
2023-07-20 19:32:15 +02:00
}
2023-12-07 12:35:48 +01:00
function setFlatModeCallback ( ) {
2023-07-20 19:32:15 +02:00
$ ( '#chat_display' ) . val ( chat _styles . DEFAULT ) . trigger ( 'change' ) ;
2024-06-14 23:48:41 +02:00
return '' ;
2023-07-20 19:32:15 +02:00
}
2024-06-11 01:00:13 +02:00
/ * *
2024-06-11 08:55:37 +02:00
* Sets a persona name and optionally an avatar .
* @ param { { mode : 'lookup' | 'temp' | 'all' } } namedArgs Named arguments
* @ param { string } name Name to set
2024-06-14 23:48:41 +02:00
* @ returns { string }
2024-06-11 01:00:13 +02:00
* /
function setNameCallback ( { mode = 'all' } , name ) {
2023-07-20 19:32:15 +02:00
if ( ! name ) {
2024-06-11 08:55:37 +02:00
toastr . warning ( 'You must specify a name to change to' ) ;
2024-06-14 23:48:41 +02:00
return '' ;
2023-07-20 19:32:15 +02:00
}
2024-06-11 01:00:13 +02:00
if ( ! [ 'lookup' , 'temp' , 'all' ] . includes ( mode ) ) {
2024-06-11 08:55:37 +02:00
toastr . warning ( 'Mode must be one of "lookup", "temp" or "all"' ) ;
2024-06-14 23:48:41 +02:00
return '' ;
2024-06-11 01:00:13 +02:00
}
2023-07-20 19:32:15 +02:00
name = name . trim ( ) ;
2024-06-11 01:00:13 +02:00
// If the name matches a persona avatar, or a name, auto-select it
if ( [ 'lookup' , 'all' ] . includes ( mode ) ) {
let persona = Object . entries ( power _user . personas ) . find ( ( [ avatar , _ ] ) => avatar === name ) ? . [ 1 ] ;
if ( ! persona ) persona = Object . entries ( power _user . personas ) . find ( ( [ _ , personaName ] ) => personaName . toLowerCase ( ) === name . toLowerCase ( ) ) ? . [ 1 ] ;
if ( persona ) {
autoSelectPersona ( persona ) ;
2024-03-27 14:16:20 +01:00
retriggerFirstMessageOnEmptyChat ( ) ;
2024-06-14 23:48:41 +02:00
return '' ;
2024-06-11 01:00:13 +02:00
} else if ( mode === 'lookup' ) {
toastr . warning ( ` Persona ${ name } not found ` ) ;
2024-06-14 23:48:41 +02:00
return '' ;
2023-07-20 19:32:15 +02:00
}
}
2024-06-11 01:00:13 +02:00
if ( [ 'temp' , 'all' ] . includes ( mode ) ) {
// Otherwise, set just the name
setUserName ( name ) ; //this prevented quickReply usage
retriggerFirstMessageOnEmptyChat ( ) ;
}
2024-06-14 23:48:41 +02:00
return '' ;
2023-07-20 19:32:15 +02:00
}
2023-12-07 12:35:48 +01:00
async function setNarratorName ( _ , text ) {
2023-07-20 19:32:15 +02:00
const name = text || NARRATOR _NAME _DEFAULT ;
2023-12-07 12:35:48 +01:00
chat _metadata [ NARRATOR _NAME _KEY ] = name ;
2023-07-20 19:32:15 +02:00
toastr . info ( ` System narrator name set to ${ name } ` ) ;
2023-08-23 09:32:48 +02:00
await saveChatConditional ( ) ;
2024-06-14 23:48:41 +02:00
return '' ;
2023-07-20 19:32:15 +02:00
}
2023-12-07 12:35:48 +01:00
export async function sendMessageAs ( args , text ) {
2023-07-20 19:32:15 +02:00
if ( ! text ) {
2024-06-14 23:48:41 +02:00
return '' ;
2023-07-20 19:32:15 +02:00
}
2023-11-21 01:54:04 +01:00
let name ;
let mesText ;
2023-07-20 19:32:15 +02:00
2023-11-27 18:48:49 +01:00
if ( args . name ) {
name = args . name . trim ( ) ;
2023-11-21 01:54:04 +01:00
mesText = text . trim ( ) ;
if ( ! name && ! text ) {
toastr . warning ( 'You must specify a name and text to send as' ) ;
2024-06-14 23:48:41 +02:00
return '' ;
2023-11-21 01:54:04 +01:00
}
} else {
2024-04-28 02:53:17 +02:00
const namelessWarningKey = 'sendAsNamelessWarningShown' ;
if ( localStorage . getItem ( namelessWarningKey ) !== 'true' ) {
toastr . warning ( 'To avoid confusion, please use /sendas name="Character Name"' , 'Name defaulted to {{char}}' , { timeOut : 10000 } ) ;
localStorage . setItem ( namelessWarningKey , 'true' ) ;
2023-11-21 01:54:04 +01:00
}
2024-04-28 02:53:17 +02:00
name = name2 ;
2023-11-21 01:54:04 +01:00
}
2023-07-20 19:32:15 +02:00
// Requires a regex check after the slash command is pushed to output
mesText = getRegexedString ( mesText , regex _placement . SLASH _COMMAND , { characterOverride : name } ) ;
// Messages that do nothing but set bias will be hidden from the context
const bias = extractMessageBias ( mesText ) ;
2023-12-21 19:49:03 +01:00
const isSystem = bias && ! removeMacros ( mesText ) . length ;
2024-03-27 18:40:34 +01:00
const compact = isTrueBoolean ( args ? . compact ) ;
2023-07-20 19:32:15 +02:00
const character = characters . find ( x => x . name === name ) ;
let force _avatar , original _avatar ;
if ( character && character . avatar !== 'none' ) {
force _avatar = getThumbnailUrl ( 'avatar' , character . avatar ) ;
original _avatar = character . avatar ;
}
else {
force _avatar = default _avatar ;
original _avatar = default _avatar ;
}
const message = {
name : name ,
is _user : false ,
is _system : isSystem ,
2023-08-22 17:13:03 +02:00
send _date : getMessageTimeStamp ( ) ,
2023-07-20 19:32:15 +02:00
mes : substituteParams ( mesText ) ,
force _avatar : force _avatar ,
original _avatar : original _avatar ,
extra : {
bias : bias . trim ( ) . length ? bias : null ,
gen _id : Date . now ( ) ,
2024-03-27 18:40:34 +01:00
isSmallSys : compact ,
2023-12-02 21:06:57 +01:00
} ,
2023-07-20 19:32:15 +02:00
} ;
2023-11-27 18:48:49 +01:00
const insertAt = Number ( resolveVariable ( args . at ) ) ;
if ( ! isNaN ( insertAt ) && insertAt >= 0 && insertAt <= chat . length ) {
chat . splice ( insertAt , 0 , message ) ;
await saveChatConditional ( ) ;
await eventSource . emit ( event _types . MESSAGE _RECEIVED , insertAt ) ;
await reloadCurrentChat ( ) ;
await eventSource . emit ( event _types . CHARACTER _MESSAGE _RENDERED , insertAt ) ;
} else {
chat . push ( message ) ;
await eventSource . emit ( event _types . MESSAGE _RECEIVED , ( chat . length - 1 ) ) ;
addOneMessage ( message ) ;
await eventSource . emit ( event _types . CHARACTER _MESSAGE _RENDERED , ( chat . length - 1 ) ) ;
await saveChatConditional ( ) ;
}
2024-06-14 23:48:41 +02:00
return '' ;
2023-07-20 19:32:15 +02:00
}
2023-12-07 12:35:48 +01:00
export async function sendNarratorMessage ( args , text ) {
2023-07-20 19:32:15 +02:00
if ( ! text ) {
2024-06-14 23:48:41 +02:00
return '' ;
2023-07-20 19:32:15 +02:00
}
2023-12-07 12:35:48 +01:00
const name = chat _metadata [ NARRATOR _NAME _KEY ] || NARRATOR _NAME _DEFAULT ;
2023-07-20 19:32:15 +02:00
// Messages that do nothing but set bias will be hidden from the context
const bias = extractMessageBias ( text ) ;
2023-12-21 19:49:03 +01:00
const isSystem = bias && ! removeMacros ( text ) . length ;
2024-03-27 18:40:34 +01:00
const compact = isTrueBoolean ( args ? . compact ) ;
2023-07-20 19:32:15 +02:00
const message = {
name : name ,
is _user : false ,
is _system : isSystem ,
2023-08-22 17:13:03 +02:00
send _date : getMessageTimeStamp ( ) ,
2023-07-20 19:32:15 +02:00
mes : substituteParams ( text . trim ( ) ) ,
force _avatar : system _avatar ,
extra : {
type : system _message _types . NARRATOR ,
bias : bias . trim ( ) . length ? bias : null ,
gen _id : Date . now ( ) ,
2024-03-27 18:40:34 +01:00
isSmallSys : compact ,
2023-07-20 19:32:15 +02:00
} ,
} ;
2023-11-27 18:48:49 +01:00
const insertAt = Number ( resolveVariable ( args . at ) ) ;
if ( ! isNaN ( insertAt ) && insertAt >= 0 && insertAt <= chat . length ) {
chat . splice ( insertAt , 0 , message ) ;
await saveChatConditional ( ) ;
await eventSource . emit ( event _types . MESSAGE _SENT , insertAt ) ;
await reloadCurrentChat ( ) ;
await eventSource . emit ( event _types . USER _MESSAGE _RENDERED , insertAt ) ;
} else {
chat . push ( message ) ;
await eventSource . emit ( event _types . MESSAGE _SENT , ( chat . length - 1 ) ) ;
addOneMessage ( message ) ;
await eventSource . emit ( event _types . USER _MESSAGE _RENDERED , ( chat . length - 1 ) ) ;
await saveChatConditional ( ) ;
}
2024-06-14 23:48:41 +02:00
return '' ;
2023-07-20 19:32:15 +02:00
}
2023-12-07 12:35:48 +01:00
export async function promptQuietForLoudResponse ( who , text ) {
2023-09-17 21:00:10 +02:00
let character _id = getContext ( ) . characterId ;
if ( who === 'sys' ) {
2023-12-02 19:04:51 +01:00
text = 'System: ' + text ;
2023-09-17 21:00:10 +02:00
} else if ( who === 'user' ) {
2023-12-02 19:04:51 +01:00
text = name1 + ': ' + text ;
2023-09-17 21:00:10 +02:00
} else if ( who === 'char' ) {
2023-12-07 12:35:48 +01:00
text = characters [ character _id ] . name + ': ' + text ;
2023-09-17 21:00:10 +02:00
} else if ( who === 'raw' ) {
2023-12-02 15:18:49 +01:00
// We don't need to modify the text
2023-09-17 21:00:10 +02:00
}
//text = `${text}${power_user.instruct.enabled ? '' : '\n'}${(power_user.always_force_name2 && who != 'raw') ? characters[character_id].name + ":" : ""}`
2024-04-19 13:19:57 +02:00
let reply = await generateQuietPrompt ( text , true , false ) ;
2023-09-17 21:00:10 +02:00
text = await getRegexedString ( reply , regex _placement . SLASH _COMMAND ) ;
const message = {
2023-12-07 12:35:48 +01:00
name : characters [ character _id ] . name ,
2023-09-17 21:00:10 +02:00
is _user : false ,
is _name : true ,
is _system : false ,
send _date : getMessageTimeStamp ( ) ,
mes : substituteParams ( text . trim ( ) ) ,
extra : {
type : system _message _types . COMMENT ,
gen _id : Date . now ( ) ,
} ,
} ;
chat . push ( message ) ;
await eventSource . emit ( event _types . MESSAGE _SENT , ( chat . length - 1 ) ) ;
addOneMessage ( message ) ;
await eventSource . emit ( event _types . USER _MESSAGE _RENDERED , ( chat . length - 1 ) ) ;
await saveChatConditional ( ) ;
}
2023-12-07 12:35:48 +01:00
async function sendCommentMessage ( args , text ) {
2023-07-20 19:32:15 +02:00
if ( ! text ) {
2024-06-14 23:48:41 +02:00
return '' ;
2023-07-20 19:32:15 +02:00
}
2024-03-27 18:40:34 +01:00
const compact = isTrueBoolean ( args ? . compact ) ;
2023-07-20 19:32:15 +02:00
const message = {
name : COMMENT _NAME _DEFAULT ,
is _user : false ,
2023-08-26 19:26:23 +02:00
is _system : true ,
2023-08-22 17:13:03 +02:00
send _date : getMessageTimeStamp ( ) ,
2023-07-20 19:32:15 +02:00
mes : substituteParams ( text . trim ( ) ) ,
force _avatar : comment _avatar ,
extra : {
type : system _message _types . COMMENT ,
gen _id : Date . now ( ) ,
2024-03-27 18:40:34 +01:00
isSmallSys : compact ,
2023-07-20 19:32:15 +02:00
} ,
} ;
2023-11-27 18:48:49 +01:00
const insertAt = Number ( resolveVariable ( args . at ) ) ;
if ( ! isNaN ( insertAt ) && insertAt >= 0 && insertAt <= chat . length ) {
chat . splice ( insertAt , 0 , message ) ;
await saveChatConditional ( ) ;
await eventSource . emit ( event _types . MESSAGE _SENT , insertAt ) ;
await reloadCurrentChat ( ) ;
await eventSource . emit ( event _types . USER _MESSAGE _RENDERED , insertAt ) ;
} else {
chat . push ( message ) ;
await eventSource . emit ( event _types . MESSAGE _SENT , ( chat . length - 1 ) ) ;
addOneMessage ( message ) ;
await eventSource . emit ( event _types . USER _MESSAGE _RENDERED , ( chat . length - 1 ) ) ;
await saveChatConditional ( ) ;
}
2024-06-14 23:48:41 +02:00
return '' ;
2023-07-20 19:32:15 +02:00
}
2023-11-04 20:35:50 +01:00
/ * *
* Displays a help message from the slash command
* @ param { any } _ Unused
* @ param { string } type Type of help to display
* /
2023-12-07 12:35:48 +01:00
function helpCommandCallback ( _ , type ) {
2023-11-04 20:35:50 +01:00
switch ( type ? . trim ( ) ? . toLowerCase ( ) ) {
2023-07-20 19:32:15 +02:00
case 'slash' :
2023-11-04 20:35:50 +01:00
case 'commands' :
case 'slashes' :
case 'slash commands' :
2023-07-20 19:32:15 +02:00
case '1' :
sendSystemMessage ( system _message _types . SLASH _COMMANDS ) ;
break ;
case 'format' :
2023-11-04 20:35:50 +01:00
case 'formatting' :
case 'formats' :
case 'chat formatting' :
2023-07-20 19:32:15 +02:00
case '2' :
sendSystemMessage ( system _message _types . FORMATTING ) ;
break ;
case 'hotkeys' :
2023-11-04 20:35:50 +01:00
case 'hotkey' :
2023-07-20 19:32:15 +02:00
case '3' :
sendSystemMessage ( system _message _types . HOTKEYS ) ;
break ;
case 'macros' :
2023-11-04 20:35:50 +01:00
case 'macro' :
2023-07-20 19:32:15 +02:00
case '4' :
sendSystemMessage ( system _message _types . MACROS ) ;
break ;
default :
sendSystemMessage ( system _message _types . HELP ) ;
break ;
}
2024-06-14 23:48:41 +02:00
return '' ;
2023-07-20 19:32:15 +02:00
}
2023-08-07 21:21:10 +02:00
$ ( document ) . on ( 'click' , '[data-displayHelp]' , function ( e ) {
e . preventDefault ( ) ;
const page = String ( $ ( this ) . data ( 'displayhelp' ) ) ;
helpCommandCallback ( null , page ) ;
} ) ;
2023-07-20 19:32:15 +02:00
2023-12-07 12:35:48 +01:00
function setBackgroundCallback ( _ , bg ) {
2023-07-20 19:32:15 +02:00
if ( ! bg ) {
2024-04-12 01:21:17 +02:00
// allow reporting of the background name if called without args
// for use in ST Scripts via pipe
return background _settings . name ;
2023-07-20 19:32:15 +02:00
}
2023-11-14 20:43:08 +01:00
2023-07-20 19:32:15 +02:00
console . log ( 'Set background to ' + bg ) ;
2023-12-02 19:04:51 +01:00
const bgElements = Array . from ( document . querySelectorAll ( '.bg_example' ) ) . map ( ( x ) => ( { element : x , bgfile : x . getAttribute ( 'bgfile' ) } ) ) ;
2023-11-14 20:43:08 +01:00
2023-12-07 12:35:48 +01:00
const fuse = new Fuse ( bgElements , { keys : [ 'bgfile' ] } ) ;
2023-11-14 20:43:08 +01:00
const result = fuse . search ( bg ) ;
if ( ! result . length ) {
toastr . error ( ` No background found with name " ${ bg } " ` ) ;
2024-06-14 23:48:41 +02:00
return '' ;
2023-11-14 20:43:08 +01:00
}
2023-12-07 12:35:48 +01:00
const bgElement = result [ 0 ] . item . element ;
2023-11-14 20:43:08 +01:00
if ( bgElement instanceof HTMLElement ) {
bgElement . click ( ) ;
2023-07-20 19:32:15 +02:00
}
2024-06-14 23:48:41 +02:00
return '' ;
2023-07-20 19:32:15 +02:00
}
2024-03-15 15:08:22 +01:00
/ * *
* Sets a model for the current API .
* @ param { object } _ Unused
2024-04-02 23:52:30 +02:00
* @ param { string } model New model name
* @ returns { string } New or existing model name
2024-03-15 15:08:22 +01:00
* /
function modelCallback ( _ , model ) {
const modelSelectMap = [
{ id : 'model_togetherai_select' , api : 'textgenerationwebui' , type : textgen _types . TOGETHERAI } ,
{ id : 'openrouter_model' , api : 'textgenerationwebui' , type : textgen _types . OPENROUTER } ,
{ id : 'model_infermaticai_select' , api : 'textgenerationwebui' , type : textgen _types . INFERMATICAI } ,
{ id : 'model_dreamgen_select' , api : 'textgenerationwebui' , type : textgen _types . DREAMGEN } ,
{ id : 'mancer_model' , api : 'textgenerationwebui' , type : textgen _types . MANCER } ,
2024-05-03 00:40:40 +02:00
{ id : 'vllm_model' , api : 'textgenerationwebui' , type : textgen _types . VLLM } ,
2024-03-15 15:08:22 +01:00
{ id : 'aphrodite_model' , api : 'textgenerationwebui' , type : textgen _types . APHRODITE } ,
{ id : 'ollama_model' , api : 'textgenerationwebui' , type : textgen _types . OLLAMA } ,
{ id : 'model_openai_select' , api : 'openai' , type : chat _completion _sources . OPENAI } ,
{ id : 'model_claude_select' , api : 'openai' , type : chat _completion _sources . CLAUDE } ,
{ id : 'model_windowai_select' , api : 'openai' , type : chat _completion _sources . WINDOWAI } ,
{ id : 'model_openrouter_select' , api : 'openai' , type : chat _completion _sources . OPENROUTER } ,
{ id : 'model_ai21_select' , api : 'openai' , type : chat _completion _sources . AI21 } ,
{ id : 'model_google_select' , api : 'openai' , type : chat _completion _sources . MAKERSUITE } ,
{ id : 'model_mistralai_select' , api : 'openai' , type : chat _completion _sources . MISTRALAI } ,
{ id : 'model_custom_select' , api : 'openai' , type : chat _completion _sources . CUSTOM } ,
2024-04-01 23:20:17 +02:00
{ id : 'model_cohere_select' , api : 'openai' , type : chat _completion _sources . COHERE } ,
2024-04-19 23:09:38 +02:00
{ id : 'model_perplexity_select' , api : 'openai' , type : chat _completion _sources . PERPLEXITY } ,
2024-05-05 17:53:12 +02:00
{ id : 'model_groq_select' , api : 'openai' , type : chat _completion _sources . GROQ } ,
2024-03-15 15:08:22 +01:00
{ id : 'model_novel_select' , api : 'novel' , type : null } ,
{ id : 'horde_model' , api : 'koboldhorde' , type : null } ,
] ;
function getSubType ( ) {
switch ( main _api ) {
case 'textgenerationwebui' :
return textgenerationwebui _settings . type ;
case 'openai' :
return oai _settings . chat _completion _source ;
default :
return null ;
}
}
const apiSubType = getSubType ( ) ;
const modelSelectItem = modelSelectMap . find ( x => x . api == main _api && x . type == apiSubType ) ? . id ;
if ( ! modelSelectItem ) {
toastr . info ( 'Setting a model for your API is not supported or not implemented yet.' ) ;
2024-04-02 23:52:30 +02:00
return '' ;
2024-03-15 15:08:22 +01:00
}
const modelSelectControl = document . getElementById ( modelSelectItem ) ;
if ( ! ( modelSelectControl instanceof HTMLSelectElement ) ) {
toastr . error ( ` Model select control not found: ${ main _api } [ ${ apiSubType } ] ` ) ;
2024-04-02 23:52:30 +02:00
return '' ;
2024-03-15 15:08:22 +01:00
}
const options = Array . from ( modelSelectControl . options ) ;
if ( ! options . length ) {
toastr . warning ( 'No model options found. Check your API settings.' ) ;
2024-04-02 23:52:30 +02:00
return '' ;
}
model = String ( model || '' ) . trim ( ) ;
if ( ! model ) {
return modelSelectControl . value ;
2024-03-15 15:08:22 +01:00
}
2024-04-02 23:52:30 +02:00
console . log ( 'Set model to ' + model ) ;
2024-03-15 15:08:22 +01:00
let newSelectedOption = null ;
const fuse = new Fuse ( options , { keys : [ 'text' , 'value' ] } ) ;
const fuzzySearchResult = fuse . search ( model ) ;
const exactValueMatch = options . find ( x => x . value . trim ( ) . toLowerCase ( ) === model . trim ( ) . toLowerCase ( ) ) ;
const exactTextMatch = options . find ( x => x . text . trim ( ) . toLowerCase ( ) === model . trim ( ) . toLowerCase ( ) ) ;
if ( exactValueMatch ) {
newSelectedOption = exactValueMatch ;
} else if ( exactTextMatch ) {
newSelectedOption = exactTextMatch ;
} else if ( fuzzySearchResult . length ) {
newSelectedOption = fuzzySearchResult [ 0 ] . item ;
}
if ( newSelectedOption ) {
modelSelectControl . value = newSelectedOption . value ;
$ ( modelSelectControl ) . trigger ( 'change' ) ;
toastr . success ( ` Model set to " ${ newSelectedOption . text } " ` ) ;
2024-04-02 23:52:30 +02:00
return newSelectedOption . value ;
2024-03-15 15:08:22 +01:00
} else {
toastr . warning ( ` No model found with name " ${ model } " ` ) ;
2024-04-02 23:52:30 +02:00
return '' ;
2024-03-15 15:08:22 +01:00
}
}
2024-06-16 04:37:42 +02:00
/ * *
* Sets state of prompt entries ( toggles ) either via identifier / uuid or name .
* @ param { object } args Object containing arguments
* @ param { string } args . identifier Select prompt entry using an identifier ( uuid )
* @ param { string } args . name Select prompt entry using name
* @ param { string } targetState The targeted state of the entry / entries
* @ returns { String } empty string
* /
function setPromptEntryCallback ( args , targetState ) {
// needs promptManager to manipulate prompt entries
const promptManager = setupChatCompletionPromptManager ( oai _settings ) ;
const prompts = promptManager . serviceSettings . prompts ;
let identifiersList = [ ] ;
2024-06-16 12:16:56 +02:00
// Check identifiers args
try {
const parsedIdentifiers = JSON . parse ( args . identifier ) ;
identifiersList = identifiersList . concat ( Array . isArray ( parsedIdentifiers ) ? parsedIdentifiers : [ args . identifier ] ) ;
} catch {
identifiersList . push ( args . identifier ) ;
2024-06-16 04:37:42 +02:00
}
2024-06-16 12:16:56 +02:00
2024-06-16 04:37:42 +02:00
// Check if identifiers exists in prompt, else remove from list
if ( identifiersList . length !== 0 ) {
identifiersList = identifiersList . filter ( identifier => {
return prompts . some ( prompt => prompt . identifier === identifier ) ;
} ) ;
}
let nameList = [ ] ;
// Get list of names
2024-06-16 12:16:56 +02:00
try {
const parsedNames = JSON . parse ( args . name ) ;
nameList = nameList . concat ( Array . isArray ( parsedNames ) ? parsedNames : [ args . name ] ) ;
} catch {
nameList . push ( args . name ) ;
2024-06-16 04:37:42 +02:00
}
if ( nameList . length !== 0 ) {
nameList . forEach ( name => {
// one name could potentially have multiple entries, find all identifiers that match given name
let identifiers = [ ] ;
prompts . forEach ( entry => {
if ( entry . name === name ) {
identifiers . push ( entry . identifier ) ;
}
} ) ;
identifiersList = identifiersList . concat ( identifiers ) ;
} ) ;
}
// Remove duplicates to allow consistent 'toggle'
identifiersList = [ ... new Set ( identifiersList ) ] ;
if ( identifiersList . length === 0 ) return '' ;
// logic adapted from PromptManager.js, handleToggle
if ( [ 'toggle' , 't' , '' ] . includes ( targetState . trim ( ) . toLowerCase ( ) ) ) {
identifiersList . forEach ( promptID => {
const promptOrderEntry = promptManager . getPromptOrderEntry ( promptManager . activeCharacter , promptID ) ;
const counts = promptManager . tokenHandler . getCounts ( ) ;
counts [ promptID ] = null ;
promptOrderEntry . enabled = ! promptOrderEntry . enabled ;
} ) ;
}
if ( isTrueBoolean ( targetState ) ) {
identifiersList . forEach ( promptID => {
const promptOrderEntry = promptManager . getPromptOrderEntry ( promptManager . activeCharacter , promptID ) ;
const counts = promptManager . tokenHandler . getCounts ( ) ;
counts [ promptID ] = null ;
promptOrderEntry . enabled = true ;
} ) ;
}
if ( isFalseBoolean ( targetState ) ) {
identifiersList . forEach ( promptID => {
const promptOrderEntry = promptManager . getPromptOrderEntry ( promptManager . activeCharacter , promptID ) ;
const counts = promptManager . tokenHandler . getCounts ( ) ;
counts [ promptID ] = null ;
promptOrderEntry . enabled = false ;
} ) ;
}
// no need to render for each identifier
promptManager . render ( ) ;
promptManager . saveServiceSettings ( ) ;
return '' ;
}
2024-05-12 21:15:05 +02:00
export let isExecutingCommandsFromChatInput = false ;
export let commandsFromChatInputAbortController ;
2023-11-23 21:36:48 +01:00
/ * *
2024-05-12 21:15:05 +02:00
* Show command execution pause / stop buttons next to chat input .
2023-11-23 21:36:48 +01:00
* /
2024-05-12 21:15:05 +02:00
export function activateScriptButtons ( ) {
document . querySelector ( '#form_sheld' ) . classList . add ( 'isExecutingCommandsFromChatInput' ) ;
}
2023-07-20 19:32:15 +02:00
2024-05-12 21:15:05 +02:00
/ * *
* Hide command execution pause / stop buttons next to chat input .
* /
export function deactivateScriptButtons ( ) {
document . querySelector ( '#form_sheld' ) . classList . remove ( 'isExecutingCommandsFromChatInput' ) ;
}
2023-11-23 23:18:07 +01:00
2024-05-12 21:15:05 +02:00
/ * *
* Toggle pause / continue command execution . Only for commands executed via chat input .
* /
export function pauseScriptExecution ( ) {
if ( commandsFromChatInputAbortController ) {
if ( commandsFromChatInputAbortController . signal . paused ) {
commandsFromChatInputAbortController . continue ( 'Clicked pause button' ) ;
document . querySelector ( '#form_sheld' ) . classList . remove ( 'script_paused' ) ;
} else {
commandsFromChatInputAbortController . pause ( 'Clicked pause button' ) ;
document . querySelector ( '#form_sheld' ) . classList . add ( 'script_paused' ) ;
2023-11-23 23:18:07 +01:00
}
}
2024-05-12 21:15:05 +02:00
}
2023-07-20 19:32:15 +02:00
2024-05-12 21:15:05 +02:00
/ * *
* Stop command execution . Only for commands executed via chat input .
* /
export function stopScriptExecution ( ) {
commandsFromChatInputAbortController ? . abort ( 'Clicked stop button' ) ;
}
2023-07-20 19:32:15 +02:00
2024-05-12 21:15:05 +02:00
/ * *
* Clear up command execution progress bar above chat input .
* @ returns Promise < void >
* /
async function clearCommandProgress ( ) {
if ( isExecutingCommandsFromChatInput ) return ;
document . querySelector ( '#send_textarea' ) . style . setProperty ( '--progDone' , '1' ) ;
await delay ( 250 ) ;
if ( isExecutingCommandsFromChatInput ) return ;
document . querySelector ( '#send_textarea' ) . style . transition = 'none' ;
await delay ( 1 ) ;
document . querySelector ( '#send_textarea' ) . style . setProperty ( '--prog' , '0%' ) ;
document . querySelector ( '#send_textarea' ) . style . setProperty ( '--progDone' , '0' ) ;
document . querySelector ( '#form_sheld' ) . classList . remove ( 'script_success' ) ;
document . querySelector ( '#form_sheld' ) . classList . remove ( 'script_error' ) ;
document . querySelector ( '#form_sheld' ) . classList . remove ( 'script_aborted' ) ;
await delay ( 1 ) ;
document . querySelector ( '#send_textarea' ) . style . transition = null ;
}
/ * *
* Debounced version of clearCommandProgress .
* /
const clearCommandProgressDebounced = debounce ( clearCommandProgress ) ;
2023-07-20 19:32:15 +02:00
2024-05-12 21:15:05 +02:00
/ * *
* @ typedef ExecuteSlashCommandsOptions
* @ prop { boolean } [ handleParserErrors ] ( true ) Whether to handle parser errors ( show toast on error ) or throw .
* @ prop { SlashCommandScope } [ scope ] ( null ) The scope to be used when executing the commands .
* @ prop { boolean } [ handleExecutionErrors ] ( false ) Whether to handle execution errors ( show toast on error ) or throw
2024-05-19 12:23:34 +02:00
* @ prop { { [ id : PARSER _FLAG ] : boolean } } [ parserFlags ] ( null ) Parser flags to apply
2024-05-12 21:15:05 +02:00
* @ prop { SlashCommandAbortController } [ abortController ] ( null ) Controller used to abort or pause command execution
* @ prop { ( done : number , total : number ) => void } [ onProgress ] ( null ) Callback to handle progress events
* /
2023-07-20 19:32:15 +02:00
2024-05-12 21:15:05 +02:00
/ * *
* @ typedef ExecuteSlashCommandsOnChatInputOptions
* @ prop { SlashCommandScope } [ scope ] ( null ) The scope to be used when executing the commands .
2024-05-19 12:23:34 +02:00
* @ prop { { [ id : PARSER _FLAG ] : boolean } } [ parserFlags ] ( null ) Parser flags to apply
2024-05-12 21:15:05 +02:00
* @ prop { boolean } [ clearChatInput ] ( false ) Whether to clear the chat input textarea
* /
2023-07-20 19:32:15 +02:00
2024-05-12 21:15:05 +02:00
/ * *
* Execute slash commands while showing progress indicator and pause / stop buttons on
* chat input .
* @ param { string } text Slash command text
* @ param { ExecuteSlashCommandsOnChatInputOptions } options
* /
export async function executeSlashCommandsOnChatInput ( text , options = { } ) {
if ( isExecutingCommandsFromChatInput ) return null ;
2023-07-20 19:32:15 +02:00
2024-05-12 21:15:05 +02:00
options = Object . assign ( {
scope : null ,
parserFlags : null ,
clearChatInput : false ,
} , options ) ;
2024-03-25 13:22:39 +01:00
2024-05-12 21:15:05 +02:00
isExecutingCommandsFromChatInput = true ;
commandsFromChatInputAbortController ? . abort ( 'processCommands was called' ) ;
activateScriptButtons ( ) ;
2023-11-04 12:33:09 +01:00
2024-05-12 21:15:05 +02:00
/**@type {HTMLTextAreaElement}*/
const ta = document . querySelector ( '#send_textarea' ) ;
2023-11-23 21:56:52 +01:00
2024-05-12 21:15:05 +02:00
if ( options . clearChatInput ) {
ta . value = '' ;
2024-05-19 21:22:32 +02:00
ta . dispatchEvent ( new Event ( 'input' , { bubbles : true } ) ) ;
2024-05-12 21:15:05 +02:00
}
2023-11-25 17:18:57 +01:00
2024-05-12 21:15:05 +02:00
document . querySelector ( '#send_textarea' ) . style . setProperty ( '--prog' , '0%' ) ;
document . querySelector ( '#send_textarea' ) . style . setProperty ( '--progDone' , '0' ) ;
document . querySelector ( '#form_sheld' ) . classList . remove ( 'script_success' ) ;
document . querySelector ( '#form_sheld' ) . classList . remove ( 'script_error' ) ;
document . querySelector ( '#form_sheld' ) . classList . remove ( 'script_aborted' ) ;
2023-11-24 00:56:43 +01:00
2024-05-12 21:15:05 +02:00
/**@type {SlashCommandClosureResult} */
let result = null ;
try {
commandsFromChatInputAbortController = new SlashCommandAbortController ( ) ;
result = await executeSlashCommandsWithOptions ( text , {
abortController : commandsFromChatInputAbortController ,
2024-05-19 21:22:32 +02:00
onProgress : ( done , total ) => ta . style . setProperty ( '--prog' , ` ${ done / total * 100 } % ` ) ,
2024-06-14 23:48:41 +02:00
parserFlags : options . parserFlags ,
scope : options . scope ,
2024-05-12 21:15:05 +02:00
} ) ;
if ( commandsFromChatInputAbortController . signal . aborted ) {
document . querySelector ( '#form_sheld' ) . classList . add ( 'script_aborted' ) ;
} else {
document . querySelector ( '#form_sheld' ) . classList . add ( 'script_success' ) ;
2023-11-23 23:18:07 +01:00
}
2024-05-12 21:15:05 +02:00
} catch ( e ) {
document . querySelector ( '#form_sheld' ) . classList . add ( 'script_error' ) ;
result = new SlashCommandClosureResult ( ) ;
result . isError = true ;
result . errorMessage = e . message ;
2024-05-18 14:41:02 +02:00
if ( e . cause !== 'abort' ) {
toastr . error ( e . message ) ;
}
2024-05-12 21:15:05 +02:00
} finally {
2024-05-19 21:22:32 +02:00
delay ( 1000 ) . then ( ( ) => clearCommandProgressDebounced ( ) ) ;
2023-11-23 23:18:07 +01:00
2024-05-12 21:15:05 +02:00
commandsFromChatInputAbortController = null ;
deactivateScriptButtons ( ) ;
isExecutingCommandsFromChatInput = false ;
}
return result ;
}
2024-02-03 01:06:49 +01:00
2024-05-12 21:15:05 +02:00
/ * *
*
* @ param { string } text Slash command text
* @ param { ExecuteSlashCommandsOptions } [ options ]
* @ returns { Promise < SlashCommandClosureResult > }
* /
async function executeSlashCommandsWithOptions ( text , options = { } ) {
if ( ! text ) {
return null ;
}
options = Object . assign ( {
handleParserErrors : true ,
scope : null ,
handleExecutionErrors : false ,
parserFlags : null ,
abortController : null ,
onProgress : null ,
} , options ) ;
2023-11-23 21:56:52 +01:00
2024-05-12 21:15:05 +02:00
let closure ;
try {
2024-05-18 20:48:31 +02:00
closure = parser . parse ( text , true , options . parserFlags , options . abortController ? ? new SlashCommandAbortController ( ) ) ;
2024-05-12 21:15:05 +02:00
closure . scope . parent = options . scope ;
closure . onProgress = options . onProgress ;
} catch ( e ) {
if ( options . handleParserErrors && e instanceof SlashCommandParserError ) {
/**@type {SlashCommandParserError}*/
const ex = e ;
const toast = `
< div > $ { ex . message } < / d i v >
< div > Line : $ { ex . line } Column : $ { ex . column } < / d i v >
< pre style = "text-align:left;" > $ { ex . hint } < / p r e >
` ;
const clickHint = '<p>Click to see details</p>' ;
toastr . error (
` ${ toast } ${ clickHint } ` ,
'SlashCommandParserError' ,
2024-05-19 21:22:32 +02:00
{ escapeHtml : false , timeOut : 10000 , onclick : ( ) => callPopup ( toast , 'text' ) } ,
2024-05-12 21:15:05 +02:00
) ;
const result = new SlashCommandClosureResult ( ) ;
return result ;
} else {
throw e ;
2024-01-26 19:58:44 +01:00
}
2024-05-12 21:15:05 +02:00
}
2024-01-26 19:58:44 +01:00
2024-05-12 21:15:05 +02:00
try {
const result = await closure . execute ( ) ;
2024-05-18 20:48:31 +02:00
if ( result . isAborted && ! result . isQuietlyAborted ) {
2024-05-12 21:15:05 +02:00
toastr . warning ( result . abortReason , 'Command execution aborted' ) ;
2024-05-19 13:34:09 +02:00
closure . abortController . signal . isQuiet = true ;
2023-07-20 19:32:15 +02:00
}
2024-05-12 21:15:05 +02:00
return result ;
} catch ( e ) {
if ( options . handleExecutionErrors ) {
toastr . error ( e . message ) ;
const result = new SlashCommandClosureResult ( ) ;
result . isError = true ;
result . errorMessage = e . message ;
return result ;
} else {
throw e ;
2023-07-20 19:32:15 +02:00
}
}
}
2024-04-19 21:46:49 +02:00
/ * *
2024-05-12 21:15:05 +02:00
* Executes slash commands in the provided text
* @ deprecated Use executeSlashCommandWithOptions instead
* @ param { string } text Slash command text
* @ param { boolean } handleParserErrors Whether to handle parser errors ( show toast on error ) or throw
* @ param { SlashCommandScope } scope The scope to be used when executing the commands .
* @ param { boolean } handleExecutionErrors Whether to handle execution errors ( show toast on error ) or throw
2024-06-14 23:48:41 +02:00
* @ param { { [ id : PARSER _FLAG ] : boolean } } parserFlags Parser flags to apply
2024-05-12 21:15:05 +02:00
* @ param { SlashCommandAbortController } abortController Controller used to abort or pause command execution
* @ param { ( done : number , total : number ) => void } onProgress Callback to handle progress events
* @ returns { Promise < SlashCommandClosureResult > }
2024-04-19 21:46:49 +02:00
* /
2024-05-12 21:15:05 +02:00
async function executeSlashCommands ( text , handleParserErrors = true , scope = null , handleExecutionErrors = false , parserFlags = null , abortController = null , onProgress = null ) {
return executeSlashCommandsWithOptions ( text , {
handleParserErrors ,
scope ,
handleExecutionErrors ,
parserFlags ,
abortController ,
onProgress ,
2023-11-04 00:21:20 +01:00
} ) ;
}
2024-05-12 21:15:05 +02:00
/ * *
*
* @ param { HTMLTextAreaElement } textarea The textarea to receive autocomplete
* @ param { Boolean } isFloating Whether to show the auto complete as a floating window ( e . g . , large QR editor )
* /
export async function setSlashCommandAutoComplete ( textarea , isFloating = false ) {
2024-05-19 21:22:32 +02:00
function canUseNegativeLookbehind ( ) {
try {
new RegExp ( '(?<!_)' ) ;
return true ;
} catch ( e ) {
return false ;
}
}
if ( ! canUseNegativeLookbehind ( ) ) {
console . warn ( 'Cannot use negative lookbehind in this browser' ) ;
return ;
}
2024-05-12 21:15:05 +02:00
const parser = new SlashCommandParser ( ) ;
const ac = new AutoComplete (
textarea ,
( ) => ac . text [ 0 ] == '/' ,
2024-05-19 21:22:32 +02:00
async ( text , index ) => await parser . getNameAt ( text , index ) ,
2024-05-12 21:15:05 +02:00
isFloating ,
) ;
}
/**@type {HTMLTextAreaElement} */
const sendTextarea = document . querySelector ( '#send_textarea' ) ;
setSlashCommandAutoComplete ( sendTextarea ) ;
sendTextarea . addEventListener ( 'input' , ( ) => {
if ( sendTextarea . value [ 0 ] == '/' ) {
sendTextarea . style . fontFamily = 'monospace' ;
} else {
sendTextarea . style . fontFamily = null ;
}
2023-12-02 20:11:06 +01:00
} ) ;