2023-12-11 15:23:21 +01:00
import { saveSettingsDebounced , callPopup , getRequestHeaders , substituteParams , eventSource , event _types , animation _duration } from '../../../script.js' ;
2023-12-02 19:04:51 +01:00
import { getContext , extension _settings } from '../../extensions.js' ;
2023-12-14 17:00:38 +01:00
import { getSortableDelay , escapeHtml , delay } from '../../utils.js' ;
2023-12-02 19:04:51 +01:00
import { executeSlashCommands , registerSlashCommand } from '../../slash-commands.js' ;
import { ContextMenu } from './src/ContextMenu.js' ;
import { MenuItem } from './src/MenuItem.js' ;
import { MenuHeader } from './src/MenuHeader.js' ;
import { loadMovingUIState } from '../../power-user.js' ;
import { dragElement } from '../../RossAscends-mods.js' ;
2023-07-29 23:22:03 +02:00
2023-07-20 19:32:15 +02:00
export { MODULE _NAME } ;
const MODULE _NAME = 'quick-reply' ;
const UPDATE _INTERVAL = 1000 ;
2023-07-29 23:22:03 +02:00
let presets = [ ] ;
let selected _preset = '' ;
2023-07-20 19:32:15 +02:00
const defaultSettings = {
2023-08-29 17:04:10 +02:00
quickReplyEnabled : false ,
2023-07-20 19:32:15 +02:00
numberOfSlots : 5 ,
quickReplySlots : [ ] ,
2023-10-21 15:02:29 +02:00
placeBeforeInputEnabled : false ,
2023-09-08 20:38:31 +02:00
quickActionEnabled : false ,
2023-10-21 15:09:25 +02:00
AutoInputInject : true ,
2023-12-02 20:11:06 +01:00
} ;
2023-07-20 19:32:15 +02:00
2023-07-29 23:22:03 +02:00
//method from worldinfo
async function updateQuickReplyPresetList ( ) {
2023-12-02 19:04:51 +01:00
const result = await fetch ( '/getsettings' , {
method : 'POST' ,
2023-07-29 23:22:03 +02:00
headers : getRequestHeaders ( ) ,
body : JSON . stringify ( { } ) ,
} ) ;
if ( result . ok ) {
var data = await result . json ( ) ;
presets = data . quickReplyPresets ? . length ? data . quickReplyPresets : [ ] ;
2023-08-22 09:37:18 +02:00
console . debug ( 'Quick Reply presets' , presets ) ;
2023-12-02 19:04:51 +01:00
$ ( '#quickReplyPresets' ) . find ( 'option[value!=""]' ) . remove ( ) ;
2023-07-29 23:22:03 +02:00
if ( presets !== undefined ) {
2023-10-21 15:09:25 +02:00
presets . forEach ( ( item ) => {
const option = document . createElement ( 'option' ) ;
option . value = item . name ;
option . innerText = item . name ;
option . selected = selected _preset . includes ( item . name ) ;
2023-12-02 19:04:51 +01:00
$ ( '#quickReplyPresets' ) . append ( option ) ;
2023-07-29 23:22:03 +02:00
} ) ;
}
}
}
async function loadSettings ( type ) {
if ( type === 'init' ) {
2023-12-02 20:11:06 +01:00
await updateQuickReplyPresetList ( ) ;
2023-07-29 23:22:03 +02:00
}
2023-07-20 19:32:15 +02:00
if ( Object . keys ( extension _settings . quickReply ) . length === 0 ) {
Object . assign ( extension _settings . quickReply , defaultSettings ) ;
}
2023-10-21 15:09:25 +02:00
if ( extension _settings . quickReply . AutoInputInject === undefined ) {
extension _settings . quickReply . AutoInputInject = true ;
}
2023-07-20 19:32:15 +02:00
// If the user has an old version of the extension, update it
if ( ! Array . isArray ( extension _settings . quickReply . quickReplySlots ) ) {
extension _settings . quickReply . quickReplySlots = [ ] ;
extension _settings . quickReply . numberOfSlots = defaultSettings . numberOfSlots ;
for ( let i = 1 ; i <= extension _settings . quickReply . numberOfSlots ; i ++ ) {
extension _settings . quickReply . quickReplySlots . push ( {
mes : extension _settings . quickReply [ ` quickReply ${ i } Mes ` ] ,
label : extension _settings . quickReply [ ` quickReply ${ i } Label ` ] ,
enabled : true ,
} ) ;
delete extension _settings . quickReply [ ` quickReply ${ i } Mes ` ] ;
delete extension _settings . quickReply [ ` quickReply ${ i } Label ` ] ;
}
}
initializeEmptySlots ( extension _settings . quickReply . numberOfSlots ) ;
generateQuickReplyElements ( ) ;
for ( let i = 1 ; i <= extension _settings . quickReply . numberOfSlots ; i ++ ) {
$ ( ` #quickReply ${ i } Mes ` ) . val ( extension _settings . quickReply . quickReplySlots [ i - 1 ] ? . mes ) . trigger ( 'input' ) ;
$ ( ` #quickReply ${ i } Label ` ) . val ( extension _settings . quickReply . quickReplySlots [ i - 1 ] ? . label ) . trigger ( 'input' ) ;
}
$ ( '#quickReplyEnabled' ) . prop ( 'checked' , extension _settings . quickReply . quickReplyEnabled ) ;
$ ( '#quickReplyNumberOfSlots' ) . val ( extension _settings . quickReply . numberOfSlots ) ;
2023-10-21 15:02:29 +02:00
$ ( '#placeBeforeInputEnabled' ) . prop ( 'checked' , extension _settings . quickReply . placeBeforeInputEnabled ) ;
2023-09-08 20:38:31 +02:00
$ ( '#quickActionEnabled' ) . prop ( 'checked' , extension _settings . quickReply . quickActionEnabled ) ;
2023-10-21 15:02:29 +02:00
$ ( '#AutoInputInject' ) . prop ( 'checked' , extension _settings . quickReply . AutoInputInject ) ;
2023-07-20 19:32:15 +02:00
}
function onQuickReplyInput ( id ) {
extension _settings . quickReply . quickReplySlots [ id - 1 ] . mes = $ ( ` #quickReply ${ id } Mes ` ) . val ( ) ;
2023-10-21 15:09:25 +02:00
$ ( ` #quickReply ${ id } ` ) . attr ( 'title' , String ( $ ( ` #quickReply ${ id } Mes ` ) . val ( ) ) ) ;
2023-07-20 19:32:15 +02:00
saveSettingsDebounced ( ) ;
}
function onQuickReplyLabelInput ( id ) {
extension _settings . quickReply . quickReplySlots [ id - 1 ] . label = $ ( ` #quickReply ${ id } Label ` ) . val ( ) ;
2023-11-23 21:34:20 +01:00
addQuickReplyBar ( ) ;
2023-11-23 18:42:19 +01:00
saveSettingsDebounced ( ) ;
}
async function onQuickReplyContextMenuChange ( id ) {
2023-12-02 20:11:06 +01:00
extension _settings . quickReply . quickReplySlots [ id - 1 ] . contextMenu = JSON . parse ( $ ( ` #quickReplyContainer > [data-order=" ${ id } "] ` ) . attr ( 'data-contextMenu' ) ) ;
2023-07-20 19:32:15 +02:00
saveSettingsDebounced ( ) ;
}
2023-11-23 18:42:19 +01:00
async function onQuickReplyCtxButtonClick ( id ) {
const editorHtml = $ ( await $ . get ( 'scripts/extensions/quick-reply/contextMenuEditor.html' ) ) ;
2023-12-02 19:04:51 +01:00
const popupResult = callPopup ( editorHtml , 'confirm' , undefined , { okButton : 'Save' , wide : false , large : false , rows : 1 } ) ;
2023-11-23 18:42:19 +01:00
const qr = extension _settings . quickReply . quickReplySlots [ id - 1 ] ;
if ( ! qr . contextMenu ) {
qr . contextMenu = [ ] ;
}
/**@type {HTMLTemplateElement}*/
const tpl = document . querySelector ( '#quickReply_contextMenuEditor_itemTemplate' ) ;
const fillPresetSelect = ( select , item ) => {
2023-11-24 00:32:02 +01:00
[ { name : 'Select a preset' , value : '' } , ... presets ] . forEach ( preset => {
2023-11-23 18:42:19 +01:00
const opt = document . createElement ( 'option' ) ; {
opt . value = preset . value ? ? preset . name ;
opt . textContent = preset . name ;
opt . selected = preset . name == item . preset ;
select . append ( opt ) ;
}
} ) ;
} ;
const addCtxItem = ( item , idx ) => {
const dom = tpl . content . cloneNode ( true ) ;
const ctxItem = dom . querySelector ( '.quickReplyContextMenuEditor_item' ) ;
ctxItem . setAttribute ( 'data-order' , idx ) ;
const select = ctxItem . querySelector ( '.quickReply_contextMenuEditor_preset' ) ;
fillPresetSelect ( select , item ) ;
dom . querySelector ( '.quickReply_contextMenuEditor_chaining' ) . checked = item . chain ;
2023-11-24 00:32:02 +01:00
$ ( '.quickReply_contextMenuEditor_remove' , ctxItem ) . on ( 'click' , ( ) => ctxItem . remove ( ) ) ;
2023-11-23 18:42:19 +01:00
document . querySelector ( '#quickReply_contextMenuEditor_content' ) . append ( ctxItem ) ;
2023-12-02 20:11:06 +01:00
} ;
2023-11-24 00:32:02 +01:00
[ ... qr . contextMenu , { } ] . forEach ( ( item , idx ) => {
2023-12-02 20:11:06 +01:00
addCtxItem ( item , idx ) ;
2023-11-23 18:42:19 +01:00
} ) ;
2023-11-24 00:32:02 +01:00
$ ( '#quickReply_contextMenuEditor_addPreset' ) . on ( 'click' , ( ) => {
2023-11-23 18:42:19 +01:00
addCtxItem ( { } , document . querySelector ( '#quickReply_contextMenuEditor_content' ) . children . length ) ;
} ) ;
$ ( '#quickReply_contextMenuEditor_content' ) . sortable ( {
delay : getSortableDelay ( ) ,
2023-11-24 00:32:02 +01:00
stop : ( ) => { } ,
} ) ;
$ ( '#quickReply_autoExecute_userMessage' ) . prop ( 'checked' , qr . autoExecute _userMessage ? ? false ) ;
$ ( '#quickReply_autoExecute_botMessage' ) . prop ( 'checked' , qr . autoExecute _botMessage ? ? false ) ;
2023-11-24 13:02:04 +01:00
$ ( '#quickReply_autoExecute_chatLoad' ) . prop ( 'checked' , qr . autoExecute _chatLoad ? ? false ) ;
2023-11-26 01:12:31 +01:00
$ ( '#quickReply_autoExecute_appStartup' ) . prop ( 'checked' , qr . autoExecute _appStartup ? ? false ) ;
2023-11-24 12:50:42 +01:00
$ ( '#quickReply_hidden' ) . prop ( 'checked' , qr . hidden ? ? false ) ;
$ ( '#quickReply_hidden' ) . on ( 'input' , ( ) => {
const state = ! ! $ ( '#quickReply_hidden' ) . prop ( 'checked' ) ;
qr . hidden = state ;
saveSettingsDebounced ( ) ;
} ) ;
2023-11-24 00:32:02 +01:00
2023-11-26 01:12:31 +01:00
$ ( '#quickReply_autoExecute_appStartup' ) . on ( 'input' , ( ) => {
const state = ! ! $ ( '#quickReply_autoExecute_appStartup' ) . prop ( 'checked' ) ;
qr . autoExecute _appStartup = state ;
saveSettingsDebounced ( ) ;
} ) ;
2023-11-24 00:32:02 +01:00
$ ( '#quickReply_autoExecute_userMessage' ) . on ( 'input' , ( ) => {
const state = ! ! $ ( '#quickReply_autoExecute_userMessage' ) . prop ( 'checked' ) ;
qr . autoExecute _userMessage = state ;
saveSettingsDebounced ( ) ;
} ) ;
$ ( '#quickReply_autoExecute_botMessage' ) . on ( 'input' , ( ) => {
const state = ! ! $ ( '#quickReply_autoExecute_botMessage' ) . prop ( 'checked' ) ;
qr . autoExecute _botMessage = state ;
saveSettingsDebounced ( ) ;
2023-11-23 18:42:19 +01:00
} ) ;
2023-11-24 13:02:04 +01:00
$ ( '#quickReply_autoExecute_chatLoad' ) . on ( 'input' , ( ) => {
const state = ! ! $ ( '#quickReply_autoExecute_chatLoad' ) . prop ( 'checked' ) ;
qr . autoExecute _chatLoad = state ;
saveSettingsDebounced ( ) ;
} ) ;
2023-12-07 13:20:02 +01:00
$ ( '#quickReply_ui_title' ) . val ( qr . title ? ? '' ) ;
2023-11-23 18:42:19 +01:00
if ( await popupResult ) {
qr . contextMenu = Array . from ( document . querySelectorAll ( '#quickReply_contextMenuEditor_content > .quickReplyContextMenuEditor_item' ) )
2023-11-24 00:32:02 +01:00
. map ( item => ( {
2023-11-23 18:42:19 +01:00
preset : item . querySelector ( '.quickReply_contextMenuEditor_preset' ) . value ,
chain : item . querySelector ( '.quickReply_contextMenuEditor_chaining' ) . checked ,
} ) )
2023-11-24 00:32:02 +01:00
. filter ( item => item . preset ) ;
2023-11-23 18:42:19 +01:00
$ ( ` #quickReplyContainer[data-order=" ${ id } "] ` ) . attr ( 'data-contextMenu' , JSON . stringify ( qr . contextMenu ) ) ;
2023-12-07 13:20:02 +01:00
qr . title = $ ( '#quickReply_ui_title' ) . val ( ) ;
saveSettingsDebounced ( ) ;
2023-11-23 18:42:19 +01:00
updateQuickReplyPreset ( ) ;
onQuickReplyLabelInput ( id ) ;
}
}
2023-07-20 19:32:15 +02:00
async function onQuickReplyEnabledInput ( ) {
2023-12-02 20:11:06 +01:00
let isEnabled = $ ( this ) . prop ( 'checked' ) ;
2023-07-20 19:32:15 +02:00
extension _settings . quickReply . quickReplyEnabled = ! ! isEnabled ;
if ( isEnabled === true ) {
2023-12-02 19:04:51 +01:00
$ ( '#quickReplyBar' ) . show ( ) ;
} else { $ ( '#quickReplyBar' ) . hide ( ) ; }
2023-07-20 19:32:15 +02:00
saveSettingsDebounced ( ) ;
}
2023-09-07 04:27:03 +02:00
// New function to handle input on quickActionEnabled
async function onQuickActionEnabledInput ( ) {
2023-09-08 20:38:31 +02:00
extension _settings . quickReply . quickActionEnabled = ! ! $ ( this ) . prop ( 'checked' ) ;
saveSettingsDebounced ( ) ;
}
2023-10-21 15:02:29 +02:00
async function onPlaceBeforeInputEnabledInput ( ) {
extension _settings . quickReply . placeBeforeInputEnabled = ! ! $ ( this ) . prop ( 'checked' ) ;
saveSettingsDebounced ( ) ;
}
async function onAutoInputInject ( ) {
extension _settings . quickReply . AutoInputInject = ! ! $ ( this ) . prop ( 'checked' ) ;
2023-09-07 04:27:03 +02:00
saveSettingsDebounced ( ) ;
}
2023-07-20 19:32:15 +02:00
async function sendQuickReply ( index ) {
const prompt = extension _settings . quickReply . quickReplySlots [ index ] ? . mes || '' ;
2023-11-24 14:58:00 +01:00
return await performQuickReply ( prompt , index ) ;
2023-11-23 13:21:25 +01:00
}
2023-11-24 14:58:00 +01:00
async function executeQuickReplyByName ( name ) {
if ( ! extension _settings . quickReply . quickReplyEnabled ) {
throw new Error ( 'Quick Reply is disabled' ) ;
}
2023-12-09 18:20:15 +01:00
let qr = extension _settings . quickReply . quickReplySlots . find ( x => x . label == name ) ;
if ( ! qr && name . includes ( '.' ) ) {
const [ presetName , qrName ] = name . split ( '.' ) ;
const preset = presets . find ( x => x . name == presetName ) ;
if ( preset ) {
qr = preset . quickReplySlots . find ( x => x . label == qrName ) ;
}
}
2023-11-24 14:58:00 +01:00
if ( ! qr ) {
throw new Error ( ` Quick Reply " ${ name } " not found ` ) ;
}
return await performQuickReply ( qr . mes ) ;
}
window [ 'executeQuickReplyByName' ] = executeQuickReplyByName ;
2023-11-23 13:21:25 +01:00
async function performQuickReply ( prompt , index ) {
2023-07-20 19:32:15 +02:00
if ( ! prompt ) {
console . warn ( ` Quick reply slot ${ index } is empty! Aborting. ` ) ;
return ;
}
2023-12-02 19:04:51 +01:00
const existingText = $ ( '#send_textarea' ) . val ( ) ;
2023-07-20 19:32:15 +02:00
2023-09-07 04:27:03 +02:00
let newText ;
2023-10-21 15:09:25 +02:00
if ( existingText && extension _settings . quickReply . AutoInputInject ) {
2023-10-21 15:02:29 +02:00
if ( extension _settings . quickReply . placeBeforeInputEnabled ) {
2023-09-08 20:38:31 +02:00
newText = ` ${ prompt } ${ existingText } ` ;
} else {
newText = ` ${ existingText } ${ prompt } ` ;
}
2023-09-07 04:27:03 +02:00
} else {
2023-10-21 15:02:29 +02:00
// If no existing text and placeBeforeInputEnabled false, add prompt only (with a trailing space)
newText = ` ${ prompt } ` ;
2023-09-07 04:27:03 +02:00
}
2023-11-09 02:19:57 +01:00
// the prompt starts with '/' - execute slash commands natively
if ( prompt . startsWith ( '/' ) ) {
2023-11-24 14:58:00 +01:00
const result = await executeSlashCommands ( newText ) ;
2023-12-02 21:34:46 +01:00
return typeof result === 'object' ? result ? . pipe : '' ;
2023-11-09 02:19:57 +01:00
}
2023-11-24 00:56:43 +01:00
newText = substituteParams ( newText ) ;
2023-12-02 19:04:51 +01:00
$ ( '#send_textarea' ) . val ( newText ) ;
2023-09-07 04:27:03 +02:00
// Set the focus back to the textarea
2023-12-02 19:04:51 +01:00
$ ( '#send_textarea' ) . trigger ( 'focus' ) ;
2023-09-07 04:27:03 +02:00
2023-10-21 15:09:25 +02:00
// Only trigger send button if quickActionEnabled is not checked or
2023-11-09 02:19:57 +01:00
if ( ! extension _settings . quickReply . quickActionEnabled ) {
2023-12-02 19:04:51 +01:00
$ ( '#send_but' ) . trigger ( 'click' ) ;
2023-09-07 04:27:03 +02:00
}
2023-07-20 19:32:15 +02:00
}
2023-09-07 23:08:21 +02:00
2023-11-24 00:32:02 +01:00
function buildContextMenu ( qr , chainMes = null , hierarchy = [ ] , labelHierarchy = [ ] ) {
2023-11-23 13:21:25 +01:00
const tree = {
label : qr . label ,
2023-11-24 00:32:02 +01:00
mes : ( chainMes && qr . mes ? ` ${ chainMes } | ` : '' ) + qr . mes ,
2023-11-23 13:21:25 +01:00
children : [ ] ,
} ;
2023-11-24 00:32:02 +01:00
qr . contextMenu ? . forEach ( ctxItem => {
2023-11-23 18:42:19 +01:00
let chain = ctxItem . chain ;
let subName = ctxItem . preset ;
2023-11-24 00:32:02 +01:00
const sub = presets . find ( it => it . name == subName ) ;
2023-11-23 18:42:19 +01:00
if ( sub ) {
// prevent circular references
if ( hierarchy . indexOf ( sub . name ) == - 1 ) {
const nextHierarchy = [ ... hierarchy , sub . name ] ;
const nextLabelHierarchy = [ ... labelHierarchy , tree . label ] ;
tree . children . push ( new MenuHeader ( sub . name ) ) ;
2023-11-24 00:32:02 +01:00
sub . quickReplySlots . forEach ( subQr => {
const subInfo = buildContextMenu ( subQr , chain ? tree . mes : null , nextHierarchy , nextLabelHierarchy ) ;
2023-11-23 18:42:19 +01:00
tree . children . push ( new MenuItem (
subInfo . label ,
subInfo . mes ,
2023-11-24 00:32:02 +01:00
( evt ) => {
2023-11-23 18:42:19 +01:00
evt . stopPropagation ( ) ;
2023-11-24 00:32:02 +01:00
performQuickReply ( subInfo . mes . replace ( /%%parent(-\d+)?%%/g , ( _ , index ) => {
2023-11-23 18:42:19 +01:00
return nextLabelHierarchy . slice ( parseInt ( index ? ? '-1' ) ) [ 0 ] ;
} ) ) ;
} ,
subInfo . children ,
) ) ;
} ) ;
2023-11-23 13:21:25 +01:00
}
2023-11-23 18:42:19 +01:00
}
} ) ;
2023-11-23 13:21:25 +01:00
return tree ;
}
2023-11-27 01:48:05 +01:00
async function doQuickReplyBarPopout ( ) {
//shared elements
2023-12-02 20:11:06 +01:00
const newQuickRepliesDiv = '<div id="quickReplies"></div>' ;
const popoutButtonClone = $ ( '#quickReplyPopoutButton' ) ;
2023-11-27 01:48:05 +01:00
2023-12-02 19:04:51 +01:00
if ( $ ( '#quickReplyBarPopout' ) . length === 0 ) {
2023-12-02 20:11:06 +01:00
console . debug ( 'did not see popout yet, creating' ) ;
2023-11-27 01:48:05 +01:00
const template = $ ( '#zoomed_avatar_template' ) . html ( ) ;
const controlBarHtml = ` <div class="panelControlBar flex-container">
< div id = "quickReplyBarPopoutheader" class = "fa-solid fa-grip drag-grabber hoverglow" > < / d i v >
< div id = "quickReplyBarPopoutClose" class = "fa-solid fa-circle-xmark hoverglow" > < / d i v >
2023-12-02 20:11:06 +01:00
< / d i v > ` ;
2023-11-27 01:48:05 +01:00
const newElement = $ ( template ) ;
2023-12-02 20:11:06 +01:00
let quickRepliesClone = $ ( '#quickReplies' ) . html ( ) ;
2023-11-27 01:48:05 +01:00
newElement . attr ( 'id' , 'quickReplyBarPopout' )
. removeClass ( 'zoomed_avatar' )
. addClass ( 'draggable scrollY' )
. empty ( )
. append ( controlBarHtml )
2023-12-02 20:11:06 +01:00
. append ( newQuickRepliesDiv ) ;
2023-11-27 01:48:05 +01:00
//empty original bar
2023-12-02 20:11:06 +01:00
$ ( '#quickReplyBar' ) . empty ( ) ;
2023-11-27 01:48:05 +01:00
//add clone in popout
$ ( 'body' ) . append ( newElement ) ;
2023-12-02 20:11:06 +01:00
$ ( '#quickReplies' ) . append ( quickRepliesClone ) . css ( 'margin-top' , '1em' ) ;
2023-11-27 01:48:05 +01:00
$ ( '.quickReplyButton' ) . on ( 'click' , function ( ) {
let index = $ ( this ) . data ( 'index' ) ;
sendQuickReply ( index ) ;
} ) ;
2023-11-27 23:02:20 +01:00
$ ( '.quickReplyButton > .ctx-expander' ) . on ( 'click' , function ( evt ) {
evt . stopPropagation ( ) ;
let index = $ ( this . closest ( '.quickReplyButton' ) ) . data ( 'index' ) ;
const qr = extension _settings . quickReply . quickReplySlots [ index ] ;
if ( qr . contextMenu ? . length ) {
evt . preventDefault ( ) ;
const tree = buildContextMenu ( qr ) ;
const menu = new ContextMenu ( tree . children ) ;
menu . show ( evt ) ;
}
2023-12-02 20:11:06 +01:00
} ) ;
2023-11-27 23:02:20 +01:00
$ ( '.quickReplyButton' ) . on ( 'contextmenu' , function ( evt ) {
let index = $ ( this ) . data ( 'index' ) ;
const qr = extension _settings . quickReply . quickReplySlots [ index ] ;
if ( qr . contextMenu ? . length ) {
evt . preventDefault ( ) ;
const tree = buildContextMenu ( qr ) ;
const menu = new ContextMenu ( tree . children ) ;
menu . show ( evt ) ;
}
} ) ;
2023-11-27 01:48:05 +01:00
loadMovingUIState ( ) ;
2023-12-11 15:23:21 +01:00
$ ( '#quickReplyBarPopout' ) . fadeIn ( animation _duration ) ;
2023-12-02 20:11:06 +01:00
dragElement ( newElement ) ;
2023-11-27 01:48:05 +01:00
$ ( '#quickReplyBarPopoutClose' ) . off ( 'click' ) . on ( 'click' , function ( ) {
2023-12-02 20:11:06 +01:00
console . debug ( 'saw existing popout, removing' ) ;
let quickRepliesClone = $ ( '#quickReplies' ) . html ( ) ;
$ ( '#quickReplyBar' ) . append ( newQuickRepliesDiv ) ;
$ ( '#quickReplies' ) . prepend ( quickRepliesClone ) ;
2023-12-11 15:23:21 +01:00
$ ( '#quickReplyBar' ) . append ( popoutButtonClone ) . fadeIn ( animation _duration ) ;
$ ( '#quickReplyBarPopout' ) . fadeOut ( animation _duration , ( ) => { $ ( '#quickReplyBarPopout' ) . remove ( ) ; } ) ;
2023-11-27 01:48:05 +01:00
$ ( '.quickReplyButton' ) . on ( 'click' , function ( ) {
let index = $ ( this ) . data ( 'index' ) ;
sendQuickReply ( index ) ;
} ) ;
2023-11-27 23:02:20 +01:00
$ ( '.quickReplyButton > .ctx-expander' ) . on ( 'click' , function ( evt ) {
evt . stopPropagation ( ) ;
let index = $ ( this . closest ( '.quickReplyButton' ) ) . data ( 'index' ) ;
const qr = extension _settings . quickReply . quickReplySlots [ index ] ;
if ( qr . contextMenu ? . length ) {
evt . preventDefault ( ) ;
const tree = buildContextMenu ( qr ) ;
const menu = new ContextMenu ( tree . children ) ;
menu . show ( evt ) ;
}
2023-12-02 20:11:06 +01:00
} ) ;
2023-11-27 23:02:20 +01:00
$ ( '.quickReplyButton' ) . on ( 'contextmenu' , function ( evt ) {
let index = $ ( this ) . data ( 'index' ) ;
const qr = extension _settings . quickReply . quickReplySlots [ index ] ;
if ( qr . contextMenu ? . length ) {
evt . preventDefault ( ) ;
const tree = buildContextMenu ( qr ) ;
const menu = new ContextMenu ( tree . children ) ;
menu . show ( evt ) ;
}
} ) ;
2023-12-02 20:11:06 +01:00
$ ( '#quickReplyPopoutButton' ) . off ( 'click' ) . on ( 'click' , doQuickReplyBarPopout ) ;
} ) ;
2023-11-27 01:48:05 +01:00
}
}
2023-07-20 19:32:15 +02:00
function addQuickReplyBar ( ) {
let quickReplyButtonHtml = '' ;
2023-11-27 01:48:05 +01:00
var targetContainer ;
2023-12-02 19:04:51 +01:00
if ( $ ( '#quickReplyBarPopout' ) . length !== 0 ) {
2023-12-02 20:11:06 +01:00
targetContainer = 'popout' ;
2023-11-27 01:48:05 +01:00
} else {
2023-12-02 20:11:06 +01:00
targetContainer = 'bar' ;
2023-12-02 19:04:51 +01:00
$ ( '#quickReplyBar' ) . remove ( ) ;
2023-11-27 01:48:05 +01:00
}
2023-07-20 19:32:15 +02:00
for ( let i = 0 ; i < extension _settings . quickReply . numberOfSlots ; i ++ ) {
2023-11-24 12:50:42 +01:00
const qr = extension _settings . quickReply . quickReplySlots [ i ] ;
const quickReplyMes = qr ? . mes || '' ;
const quickReplyLabel = qr ? . label || '' ;
const hidden = qr ? . hidden ? ? false ;
2023-11-23 21:34:20 +01:00
let expander = '' ;
2023-11-23 18:42:19 +01:00
if ( extension _settings . quickReply . quickReplySlots [ i ] ? . contextMenu ? . length ) {
2023-11-23 21:34:20 +01:00
expander = '<span class="ctx-expander" title="Open context menu">⋮</span>' ;
2023-11-23 13:21:25 +01:00
}
2023-12-07 13:20:02 +01:00
quickReplyButtonHtml += ` <div title=" ${ escapeHtml ( qr . title || quickReplyMes ) } " class="quickReplyButton ${ hidden ? 'displayNone' : '' } " data-index=" ${ i } " id="quickReply ${ i + 1 } "> ${ DOMPurify . sanitize ( quickReplyLabel ) } ${ expander } </div> ` ;
2023-07-20 19:32:15 +02:00
}
const quickReplyBarFullHtml = `
< div id = "quickReplyBar" class = "flex-container flexGap5" >
< div id = "quickReplies" >
$ { quickReplyButtonHtml }
< / d i v >
2023-11-27 01:48:05 +01:00
< div id = "quickReplyPopoutButton" class = "fa-solid fa-window-restore menu_button" > < / d i v >
2023-07-20 19:32:15 +02:00
< / d i v >
` ;
2023-11-29 03:37:18 +01:00
2023-11-27 01:48:05 +01:00
if ( targetContainer === 'bar' ) {
$ ( '#send_form' ) . prepend ( quickReplyBarFullHtml ) ;
} else {
2023-12-02 20:11:06 +01:00
$ ( '#quickReplies' ) . empty ( ) . append ( quickReplyButtonHtml ) ;
2023-11-27 01:48:05 +01:00
}
2023-07-20 19:32:15 +02:00
$ ( '.quickReplyButton' ) . on ( 'click' , function ( ) {
let index = $ ( this ) . data ( 'index' ) ;
sendQuickReply ( index ) ;
} ) ;
2023-12-02 20:11:06 +01:00
$ ( '#quickReplyPopoutButton' ) . off ( 'click' ) . on ( 'click' , doQuickReplyBarPopout ) ;
2023-11-23 21:34:20 +01:00
$ ( '.quickReplyButton > .ctx-expander' ) . on ( 'click' , function ( evt ) {
evt . stopPropagation ( ) ;
let index = $ ( this . closest ( '.quickReplyButton' ) ) . data ( 'index' ) ;
const qr = extension _settings . quickReply . quickReplySlots [ index ] ;
if ( qr . contextMenu ? . length ) {
evt . preventDefault ( ) ;
const tree = buildContextMenu ( qr ) ;
const menu = new ContextMenu ( tree . children ) ;
menu . show ( evt ) ;
}
2023-12-02 20:11:06 +01:00
} ) ;
2023-11-23 13:21:25 +01:00
$ ( '.quickReplyButton' ) . on ( 'contextmenu' , function ( evt ) {
let index = $ ( this ) . data ( 'index' ) ;
const qr = extension _settings . quickReply . quickReplySlots [ index ] ;
2023-11-23 18:42:19 +01:00
if ( qr . contextMenu ? . length ) {
evt . preventDefault ( ) ;
const tree = buildContextMenu ( qr ) ;
const menu = new ContextMenu ( tree . children ) ;
menu . show ( evt ) ;
}
2023-11-23 13:21:25 +01:00
} ) ;
2023-07-20 19:32:15 +02:00
}
async function moduleWorker ( ) {
if ( extension _settings . quickReply . quickReplyEnabled === true ) {
$ ( '#quickReplyBar' ) . toggle ( getContext ( ) . onlineStatus !== 'no_connection' ) ;
}
2023-07-29 23:22:03 +02:00
if ( extension _settings . quickReply . selectedPreset ) {
selected _preset = extension _settings . quickReply . selectedPreset ;
}
}
async function saveQuickReplyPreset ( ) {
const name = await callPopup ( 'Enter a name for the Quick Reply Preset:' , 'input' ) ;
if ( ! name ) {
return ;
}
const quickReplyPreset = {
name : name ,
quickReplyEnabled : extension _settings . quickReply . quickReplyEnabled ,
quickReplySlots : extension _settings . quickReply . quickReplySlots ,
numberOfSlots : extension _settings . quickReply . numberOfSlots ,
2023-10-31 11:27:40 +01:00
AutoInputInject : extension _settings . quickReply . AutoInputInject ,
selectedPreset : name ,
2023-12-02 20:11:06 +01:00
} ;
2023-07-29 23:22:03 +02:00
const response = await fetch ( '/savequickreply' , {
method : 'POST' ,
headers : getRequestHeaders ( ) ,
2023-12-02 21:06:57 +01:00
body : JSON . stringify ( quickReplyPreset ) ,
2023-07-29 23:22:03 +02:00
} ) ;
if ( response . ok ) {
const quickReplyPresetIndex = presets . findIndex ( x => x . name == name ) ;
if ( quickReplyPresetIndex == - 1 ) {
presets . push ( quickReplyPreset ) ;
const option = document . createElement ( 'option' ) ;
option . selected = true ;
option . value = name ;
option . innerText = name ;
$ ( '#quickReplyPresets' ) . append ( option ) ;
}
else {
presets [ quickReplyPresetIndex ] = quickReplyPreset ;
2023-10-21 15:09:25 +02:00
$ ( ` #quickReplyPresets option[value=" ${ name } "] ` ) . prop ( 'selected' , true ) ;
2023-07-29 23:22:03 +02:00
}
saveSettingsDebounced ( ) ;
} else {
2023-12-02 20:11:06 +01:00
toastr . warning ( 'Failed to save Quick Reply Preset.' ) ;
2023-07-29 23:22:03 +02:00
}
2023-07-20 19:32:15 +02:00
}
2023-11-23 11:49:15 +01:00
//just a copy of save function with the name hardcoded to currently selected preset
async function updateQuickReplyPreset ( ) {
2023-12-02 20:11:06 +01:00
const name = $ ( '#quickReplyPresets' ) . val ( ) ;
2023-11-23 11:49:15 +01:00
if ( ! name ) {
return ;
}
const quickReplyPreset = {
name : name ,
quickReplyEnabled : extension _settings . quickReply . quickReplyEnabled ,
quickReplySlots : extension _settings . quickReply . quickReplySlots ,
numberOfSlots : extension _settings . quickReply . numberOfSlots ,
AutoInputInject : extension _settings . quickReply . AutoInputInject ,
selectedPreset : name ,
2023-12-02 20:11:06 +01:00
} ;
2023-11-23 11:49:15 +01:00
const response = await fetch ( '/savequickreply' , {
method : 'POST' ,
headers : getRequestHeaders ( ) ,
2023-12-02 21:06:57 +01:00
body : JSON . stringify ( quickReplyPreset ) ,
2023-11-23 11:49:15 +01:00
} ) ;
if ( response . ok ) {
const quickReplyPresetIndex = presets . findIndex ( x => x . name == name ) ;
if ( quickReplyPresetIndex == - 1 ) {
presets . push ( quickReplyPreset ) ;
const option = document . createElement ( 'option' ) ;
option . selected = true ;
option . value = name ;
option . innerText = name ;
$ ( '#quickReplyPresets' ) . append ( option ) ;
}
else {
presets [ quickReplyPresetIndex ] = quickReplyPreset ;
$ ( ` #quickReplyPresets option[value=" ${ name } "] ` ) . prop ( 'selected' , true ) ;
}
saveSettingsDebounced ( ) ;
} else {
2023-12-02 20:11:06 +01:00
toastr . warning ( 'Failed to save Quick Reply Preset.' ) ;
2023-11-23 11:49:15 +01:00
}
}
2023-07-20 19:32:15 +02:00
async function onQuickReplyNumberOfSlotsInput ( ) {
const $input = $ ( '#quickReplyNumberOfSlots' ) ;
let numberOfSlots = Number ( $input . val ( ) ) ;
if ( isNaN ( numberOfSlots ) ) {
numberOfSlots = defaultSettings . numberOfSlots ;
}
// Clamp min and max values (from input attributes)
if ( numberOfSlots < Number ( $input . attr ( 'min' ) ) ) {
numberOfSlots = Number ( $input . attr ( 'min' ) ) ;
} else if ( numberOfSlots > Number ( $input . attr ( 'max' ) ) ) {
numberOfSlots = Number ( $input . attr ( 'max' ) ) ;
}
extension _settings . quickReply . numberOfSlots = numberOfSlots ;
extension _settings . quickReply . quickReplySlots . length = numberOfSlots ;
// Initialize new slots
initializeEmptySlots ( numberOfSlots ) ;
await loadSettings ( ) ;
addQuickReplyBar ( ) ;
moduleWorker ( ) ;
saveSettingsDebounced ( ) ;
}
function initializeEmptySlots ( numberOfSlots ) {
for ( let i = 0 ; i < numberOfSlots ; i ++ ) {
if ( ! extension _settings . quickReply . quickReplySlots [ i ] ) {
extension _settings . quickReply . quickReplySlots [ i ] = {
mes : '' ,
label : '' ,
enabled : true ,
} ;
}
}
}
function generateQuickReplyElements ( ) {
let quickReplyHtml = '' ;
for ( let i = 1 ; i <= extension _settings . quickReply . numberOfSlots ; i ++ ) {
quickReplyHtml += `
2023-11-23 18:42:19 +01:00
< div class = "flex-container alignitemscenter" data - order = "${i}" >
2023-11-23 11:49:15 +01:00
< span class = "drag-handle ui-sortable-handle" > ☰ < / s p a n >
2023-10-21 15:17:17 +02:00
< input class = "text_pole wide30p" id = "quickReply${i}Label" placeholder = "(Button label)" >
2023-11-24 00:39:39 +01:00
< span class = "menu_button menu_button_icon" id = "quickReply${i}CtxButton" title = "Additional options: context menu, auto-execution" > ⋮ < / s p a n >
2023-12-10 15:48:25 +01:00
< span class = "menu_button menu_button_icon editor_maximize fa-solid fa-maximize" data - tab = "true" data - for = "quickReply${i}Mes" id = "quickReply${i}ExpandButton" title = "Expand the editor" > < / s p a n >
2023-12-07 13:29:00 +01:00
< textarea id = "quickReply${i}Mes" placeholder = "(Custom message or /command)" class = "text_pole widthUnset flex1" rows = "2" > < / t e x t a r e a >
2023-07-20 19:32:15 +02:00
< / d i v >
` ;
}
$ ( '#quickReplyContainer' ) . empty ( ) . append ( quickReplyHtml ) ;
for ( let i = 1 ; i <= extension _settings . quickReply . numberOfSlots ; i ++ ) {
2023-12-04 14:33:28 +01:00
$ ( ` #quickReply ${ i } Mes ` ) . on ( 'input' , function ( ) { onQuickReplyInput ( this . closest ( '[data-order]' ) . getAttribute ( 'data-order' ) ) ; } ) ;
$ ( ` #quickReply ${ i } Label ` ) . on ( 'input' , function ( ) { onQuickReplyLabelInput ( this . closest ( '[data-order]' ) . getAttribute ( 'data-order' ) ) ; } ) ;
$ ( ` #quickReply ${ i } CtxButton ` ) . on ( 'click' , function ( ) { onQuickReplyCtxButtonClick ( this . closest ( '[data-order]' ) . getAttribute ( 'data-order' ) ) ; } ) ;
2023-11-24 00:32:02 +01:00
$ ( ` #quickReplyContainer > [data-order=" ${ i } "] ` ) . attr ( 'data-contextMenu' , JSON . stringify ( extension _settings . quickReply . quickReplySlots [ i - 1 ] ? . contextMenu ? ? [ ] ) ) ;
2023-07-20 19:32:15 +02:00
}
}
2023-07-29 23:22:03 +02:00
async function applyQuickReplyPreset ( name ) {
const quickReplyPreset = presets . find ( x => x . name == name ) ;
if ( ! quickReplyPreset ) {
2023-12-02 20:11:06 +01:00
toastr . warning ( ` error, QR preset ' ${ name } ' not found. Confirm you are using proper case sensitivity! ` ) ;
2023-07-29 23:22:03 +02:00
return ;
}
extension _settings . quickReply = quickReplyPreset ;
extension _settings . quickReply . selectedPreset = name ;
2023-12-02 20:11:06 +01:00
saveSettingsDebounced ( ) ;
loadSettings ( 'init' ) ;
2023-07-29 23:22:03 +02:00
addQuickReplyBar ( ) ;
moduleWorker ( ) ;
2023-10-21 15:09:25 +02:00
$ ( ` #quickReplyPresets option[value=" ${ name } "] ` ) . prop ( 'selected' , true ) ;
2023-07-29 23:22:03 +02:00
console . debug ( 'QR Preset applied: ' + name ) ;
2023-08-03 07:44:23 +02:00
}
async function doQRPresetSwitch ( _ , text ) {
2023-12-02 20:11:06 +01:00
text = String ( text ) ;
applyQuickReplyPreset ( text ) ;
2023-08-03 07:44:23 +02:00
}
async function doQR ( _ , text ) {
if ( ! text ) {
2023-12-02 20:11:06 +01:00
toastr . warning ( 'must specify which QR # to use' ) ;
return ;
2023-08-03 07:44:23 +02:00
}
2023-12-02 20:11:06 +01:00
text = Number ( text ) ;
2023-08-22 09:37:18 +02:00
//use scale starting with 0
2023-08-03 07:44:23 +02:00
//ex: user inputs "/qr 2" >> qr with data-index 1 (but 2nd item displayed) gets triggered
2023-12-02 20:11:06 +01:00
let QRnum = Number ( text - 1 ) ;
if ( QRnum <= 0 ) { QRnum = 0 ; }
2023-12-02 19:04:51 +01:00
const whichQR = $ ( '#quickReplies' ) . find ( ` [data-index=' ${ QRnum } '] ` ) ;
2023-12-02 20:11:06 +01:00
whichQR . trigger ( 'click' ) ;
2023-07-29 23:22:03 +02:00
}
2023-11-23 11:49:15 +01:00
function saveQROrder ( ) {
//update html-level order data to match new sort
2023-12-02 20:11:06 +01:00
let i = 1 ;
2023-11-23 11:49:15 +01:00
$ ( '#quickReplyContainer' ) . children ( ) . each ( function ( ) {
2023-12-04 14:28:58 +01:00
const oldOrder = $ ( this ) . attr ( 'data-order' ) ;
2023-12-02 20:11:06 +01:00
$ ( this ) . attr ( 'data-order' , i ) ;
$ ( this ) . find ( 'input' ) . attr ( 'id' , ` quickReply ${ i } Label ` ) ;
$ ( this ) . find ( 'textarea' ) . attr ( 'id' , ` quickReply ${ i } Mes ` ) ;
2023-12-04 14:28:58 +01:00
$ ( this ) . find ( ` #quickReply ${ oldOrder } CtxButton ` ) . attr ( 'id' , ` quickReply ${ i } CtxButton ` ) ;
2023-12-07 14:29:40 +01:00
$ ( this ) . find ( ` #quickReply ${ oldOrder } ExpandButton ` ) . attr ( { 'data-for' : ` quickReply ${ i } Mes ` , 'id' : ` quickReply ${ i } ExpandButton ` } ) ;
2023-12-02 20:11:06 +01:00
i ++ ;
2023-11-23 11:49:15 +01:00
} ) ;
//rebuild the extension_Settings array based on new order
2023-12-02 20:11:06 +01:00
i = 1 ;
2023-11-23 11:49:15 +01:00
$ ( '#quickReplyContainer' ) . children ( ) . each ( function ( ) {
2023-12-02 20:11:06 +01:00
onQuickReplyContextMenuChange ( i ) ;
onQuickReplyLabelInput ( i ) ;
onQuickReplyInput ( i ) ;
i ++ ;
2023-11-23 11:49:15 +01:00
} ) ;
}
2023-12-14 17:00:38 +01:00
async function qrCreateCallback ( args , mes ) {
const qr = {
label : args . label ? ? '' ,
2023-12-14 19:09:33 +01:00
mes : ( mes ? ? '' )
. replace ( /\\\|/g , '|' )
. replace ( /\\\{/g , '{' )
. replace ( /\\\}/g , '}' )
2023-12-15 00:14:22 +01:00
,
2023-12-14 17:00:38 +01:00
title : args . title ? ? '' ,
autoExecute _chatLoad : JSON . parse ( args . load ? ? false ) ,
autoExecute _userMessage : JSON . parse ( args . user ? ? false ) ,
autoExecute _botMessage : JSON . parse ( args . bot ? ? false ) ,
autoExecute _appStartup : JSON . parse ( args . startup ? ? false ) ,
hidden : JSON . parse ( args . hidden ? ? false ) ,
} ;
2023-12-15 00:14:22 +01:00
const setName = args . set ? ? selected _preset ;
const preset = presets . find ( x => x . name == setName ) ;
if ( ! preset ) {
toastr . warning ( 'Confirm you are using proper case sensitivity!' , ` QR preset ' ${ setName } ' not found ` ) ;
return '' ;
}
2023-12-14 17:00:38 +01:00
preset . quickReplySlots . push ( qr ) ;
preset . numberOfSlots ++ ;
2023-12-15 00:14:22 +01:00
await fetch ( '/savequickreply' , {
2023-12-14 17:00:38 +01:00
method : 'POST' ,
headers : getRequestHeaders ( ) ,
body : JSON . stringify ( preset ) ,
} ) ;
saveSettingsDebounced ( ) ;
await delay ( 400 ) ;
applyQuickReplyPreset ( selected _preset ) ;
2023-12-15 00:14:22 +01:00
return '' ;
2023-12-14 17:00:38 +01:00
}
async function qrUpdateCallback ( args , mes ) {
2023-12-15 00:14:22 +01:00
const setName = args . set ? ? selected _preset ;
const preset = presets . find ( x => x . name == setName ) ;
if ( ! preset ) {
toastr . warning ( 'Confirm you are using proper case sensitivity!' , ` QR preset ' ${ setName } ' not found ` ) ;
return '' ;
}
2023-12-14 17:00:38 +01:00
const idx = preset . quickReplySlots . findIndex ( x => x . label == args . label ) ;
const oqr = preset . quickReplySlots [ idx ] ;
const qr = {
label : args . newlabel ? ? oqr . label ? ? '' ,
2023-12-14 19:09:33 +01:00
mes : ( mes ? ? oqr . mes )
. replace ( '\\|' , '|' )
. replace ( '\\{' , '{' )
. replace ( '\\}' , '}' )
2023-12-15 00:14:22 +01:00
,
2023-12-14 17:00:38 +01:00
title : args . title ? ? oqr . title ? ? '' ,
autoExecute _chatLoad : JSON . parse ( args . load ? ? oqr . autoExecute _chatLoad ? ? false ) ,
autoExecute _userMessage : JSON . parse ( args . user ? ? oqr . autoExecute _userMessage ? ? false ) ,
autoExecute _botMessage : JSON . parse ( args . bot ? ? oqr . autoExecute _botMessage ? ? false ) ,
autoExecute _appStartup : JSON . parse ( args . startup ? ? oqr . autoExecute _appStartup ? ? false ) ,
hidden : JSON . parse ( args . hidden ? ? oqr . hidden ? ? false ) ,
} ;
preset . quickReplySlots [ idx ] = qr ;
2023-12-15 00:14:22 +01:00
await fetch ( '/savequickreply' , {
2023-12-14 17:00:38 +01:00
method : 'POST' ,
headers : getRequestHeaders ( ) ,
body : JSON . stringify ( preset ) ,
} ) ;
saveSettingsDebounced ( ) ;
await delay ( 400 ) ;
applyQuickReplyPreset ( selected _preset ) ;
2023-12-15 00:14:22 +01:00
return '' ;
2023-12-14 17:00:38 +01:00
}
async function qrDeleteCallback ( args , label ) {
2023-12-15 00:14:22 +01:00
const setName = args . set ? ? selected _preset ;
const preset = presets . find ( x => x . name == setName ) ;
if ( ! preset ) {
toastr . warning ( 'Confirm you are using proper case sensitivity!' , ` QR preset ' ${ setName } ' not found ` ) ;
return '' ;
}
2023-12-14 17:00:38 +01:00
const idx = preset . quickReplySlots . findIndex ( x => x . label == label ) ;
preset . quickReplySlots . splice ( idx , 1 ) ;
preset . numberOfSlots -- ;
2023-12-15 00:14:22 +01:00
await fetch ( '/savequickreply' , {
2023-12-14 17:00:38 +01:00
method : 'POST' ,
headers : getRequestHeaders ( ) ,
body : JSON . stringify ( preset ) ,
} ) ;
saveSettingsDebounced ( ) ;
await delay ( 400 ) ;
applyQuickReplyPreset ( selected _preset ) ;
2023-12-15 00:14:22 +01:00
return '' ;
2023-12-14 17:00:38 +01:00
}
2023-12-14 19:25:19 +01:00
async function qrContextAddCallback ( args , presetName ) {
2023-12-15 00:14:22 +01:00
const setName = args . set ? ? selected _preset ;
const preset = presets . find ( x => x . name == setName ) ;
if ( ! preset ) {
toastr . warning ( 'Confirm you are using proper case sensitivity!' , ` QR preset ' ${ setName } ' not found ` ) ;
return '' ;
}
2023-12-14 19:25:19 +01:00
const idx = preset . quickReplySlots . findIndex ( x => x . label == args . label ) ;
const oqr = preset . quickReplySlots [ idx ] ;
if ( ! oqr . contextMenu ) {
oqr . contextMenu = [ ] ;
}
2023-12-15 00:14:22 +01:00
let item = oqr . contextMenu . find ( it => it . preset == presetName ) ;
2023-12-14 19:25:19 +01:00
if ( item ) {
item . chain = JSON . parse ( args . chain ? ? 'null' ) ? ? item . chain ? ? false ;
} else {
2023-12-15 00:14:22 +01:00
oqr . contextMenu . push ( { preset : presetName , chain : JSON . parse ( args . chain ? ? 'false' ) } ) ;
2023-12-14 19:25:19 +01:00
}
2023-12-15 00:14:22 +01:00
await fetch ( '/savequickreply' , {
2023-12-14 19:25:19 +01:00
method : 'POST' ,
headers : getRequestHeaders ( ) ,
body : JSON . stringify ( preset ) ,
} ) ;
saveSettingsDebounced ( ) ;
await delay ( 400 ) ;
applyQuickReplyPreset ( selected _preset ) ;
2023-12-15 00:14:22 +01:00
return '' ;
2023-12-14 19:25:19 +01:00
}
async function qrContextDeleteCallback ( args , presetName ) {
2023-12-15 00:14:22 +01:00
const setName = args . set ? ? selected _preset ;
const preset = presets . find ( x => x . name == setName ) ;
if ( ! preset ) {
toastr . warning ( 'Confirm you are using proper case sensitivity!' , ` QR preset ' ${ setName } ' not found ` ) ;
return '' ;
}
2023-12-14 19:25:19 +01:00
const idx = preset . quickReplySlots . findIndex ( x => x . label == args . label ) ;
const oqr = preset . quickReplySlots [ idx ] ;
if ( ! oqr . contextMenu ) return ;
2023-12-15 00:14:22 +01:00
const ctxIdx = oqr . contextMenu . findIndex ( it => it . preset == presetName ) ;
2023-12-14 19:25:19 +01:00
if ( ctxIdx > - 1 ) {
oqr . contextMenu . splice ( ctxIdx , 1 ) ;
}
2023-12-15 00:14:22 +01:00
await fetch ( '/savequickreply' , {
2023-12-14 19:25:19 +01:00
method : 'POST' ,
headers : getRequestHeaders ( ) ,
body : JSON . stringify ( preset ) ,
} ) ;
saveSettingsDebounced ( ) ;
await delay ( 400 ) ;
applyQuickReplyPreset ( selected _preset ) ;
2023-12-15 00:14:22 +01:00
return '' ;
2023-12-14 19:25:19 +01:00
}
async function qrContextClearCallback ( args , label ) {
2023-12-15 00:14:22 +01:00
const setName = args . set ? ? selected _preset ;
const preset = presets . find ( x => x . name == setName ) ;
if ( ! preset ) {
toastr . warning ( 'Confirm you are using proper case sensitivity!' , ` QR preset ' ${ setName } ' not found ` ) ;
return '' ;
}
2023-12-14 19:25:19 +01:00
const idx = preset . quickReplySlots . findIndex ( x => x . label == label ) ;
const oqr = preset . quickReplySlots [ idx ] ;
oqr . contextMenu = [ ] ;
2023-12-15 00:14:22 +01:00
await fetch ( '/savequickreply' , {
2023-12-14 19:25:19 +01:00
method : 'POST' ,
headers : getRequestHeaders ( ) ,
body : JSON . stringify ( preset ) ,
} ) ;
saveSettingsDebounced ( ) ;
await delay ( 400 ) ;
applyQuickReplyPreset ( selected _preset ) ;
2023-12-15 00:14:22 +01:00
return '' ;
2023-12-14 19:25:19 +01:00
}
2023-12-14 19:51:55 +01:00
async function qrPresetAddCallback ( args , name ) {
const quickReplyPreset = {
name : name ,
quickReplyEnabled : JSON . parse ( args . enabled ? ? null ) ? ? true ,
quickActionEnabled : JSON . parse ( args . nosend ? ? null ) ? ? false ,
placeBeforeInputEnabled : JSON . parse ( args . before ? ? null ) ? ? false ,
quickReplySlots : [ ] ,
numberOfSlots : Number ( args . slots ? ? '0' ) ,
AutoInputInject : JSON . parse ( args . inject ? ? 'false' ) ,
} ;
2023-12-15 00:14:22 +01:00
await fetch ( '/savequickreply' , {
2023-12-14 19:51:55 +01:00
method : 'POST' ,
headers : getRequestHeaders ( ) ,
body : JSON . stringify ( quickReplyPreset ) ,
} ) ;
await updateQuickReplyPresetList ( ) ;
}
2023-12-14 20:08:27 +01:00
async function qrPresetUpdateCallback ( args , name ) {
2023-12-15 00:14:22 +01:00
const preset = presets . find ( it => it . name == name ) ;
2023-12-14 20:08:27 +01:00
const quickReplyPreset = {
name : preset . name ,
quickReplyEnabled : JSON . parse ( args . enabled ? ? null ) ? ? preset . quickReplyEnabled ,
quickActionEnabled : JSON . parse ( args . nosend ? ? null ) ? ? preset . quickActionEnabled ,
placeBeforeInputEnabled : JSON . parse ( args . before ? ? null ) ? ? preset . placeBeforeInputEnabled ,
quickReplySlots : preset . quickReplySlots ,
numberOfSlots : Number ( args . slots ? ? preset . numberOfSlots ) ,
AutoInputInject : JSON . parse ( args . inject ? ? 'null' ) ? ? preset . AutoInputInject ,
} ;
Object . assign ( preset , quickReplyPreset ) ;
2023-12-15 00:14:22 +01:00
await fetch ( '/savequickreply' , {
2023-12-14 20:08:27 +01:00
method : 'POST' ,
headers : getRequestHeaders ( ) ,
body : JSON . stringify ( quickReplyPreset ) ,
} ) ;
}
2023-11-27 01:18:36 +01:00
let onMessageSentExecuting = false ;
let onMessageReceivedExecuting = false ;
let onChatChangedExecuting = false ;
2023-11-24 13:02:04 +01:00
/ * *
* Executes quick replies on message received .
* @ param { number } index New message index
* @ returns { Promise < void > }
* /
2023-11-24 00:56:43 +01:00
async function onMessageReceived ( index ) {
2023-11-24 12:32:27 +01:00
if ( ! extension _settings . quickReply . quickReplyEnabled ) return ;
2023-11-27 01:18:36 +01:00
if ( onMessageReceivedExecuting ) return ;
try {
onMessageReceivedExecuting = true ;
for ( let i = 0 ; i < extension _settings . quickReply . numberOfSlots ; i ++ ) {
const qr = extension _settings . quickReply . quickReplySlots [ i ] ;
if ( qr ? . autoExecute _botMessage ) {
const message = getContext ( ) . chat [ index ] ;
if ( message ? . mes && message ? . mes !== '...' ) {
await sendQuickReply ( i ) ;
}
2023-11-24 00:56:43 +01:00
}
2023-11-24 00:32:02 +01:00
}
2023-11-27 01:18:36 +01:00
} finally {
onMessageReceivedExecuting = false ;
2023-11-24 00:32:02 +01:00
}
}
2023-11-24 13:02:04 +01:00
/ * *
* Executes quick replies on message sent .
* @ param { number } index New message index
* @ returns { Promise < void > }
* /
2023-11-24 00:56:43 +01:00
async function onMessageSent ( index ) {
2023-11-24 12:32:27 +01:00
if ( ! extension _settings . quickReply . quickReplyEnabled ) return ;
2023-11-27 01:18:36 +01:00
if ( onMessageSentExecuting ) return ;
try {
onMessageSentExecuting = true ;
for ( let i = 0 ; i < extension _settings . quickReply . numberOfSlots ; i ++ ) {
const qr = extension _settings . quickReply . quickReplySlots [ i ] ;
if ( qr ? . autoExecute _userMessage ) {
const message = getContext ( ) . chat [ index ] ;
if ( message ? . mes && message ? . mes !== '...' ) {
await sendQuickReply ( i ) ;
}
2023-11-24 00:56:43 +01:00
}
2023-11-24 00:32:02 +01:00
}
2023-11-27 01:18:36 +01:00
} finally {
onMessageSentExecuting = false ;
2023-11-24 00:32:02 +01:00
}
}
2023-11-24 13:02:04 +01:00
/ * *
* Executes quick replies on chat changed .
* @ param { string } chatId New chat id
* @ returns { Promise < void > }
* /
async function onChatChanged ( chatId ) {
if ( ! extension _settings . quickReply . quickReplyEnabled ) return ;
2023-11-27 01:18:36 +01:00
if ( onChatChangedExecuting ) return ;
try {
onChatChangedExecuting = true ;
for ( let i = 0 ; i < extension _settings . quickReply . numberOfSlots ; i ++ ) {
const qr = extension _settings . quickReply . quickReplySlots [ i ] ;
if ( qr ? . autoExecute _chatLoad && chatId ) {
await sendQuickReply ( i ) ;
}
2023-11-24 13:02:04 +01:00
}
2023-11-27 01:18:36 +01:00
} finally {
onChatChangedExecuting = false ;
2023-11-24 13:02:04 +01:00
}
}
2023-11-26 01:12:31 +01:00
/ * *
* Executes quick replies on app ready .
* @ returns { Promise < void > }
* /
async function onAppReady ( ) {
if ( ! extension _settings . quickReply . quickReplyEnabled ) return ;
for ( let i = 0 ; i < extension _settings . quickReply . numberOfSlots ; i ++ ) {
const qr = extension _settings . quickReply . quickReplySlots [ i ] ;
if ( qr ? . autoExecute _appStartup ) {
await sendQuickReply ( i ) ;
}
}
}
2023-07-20 19:32:15 +02:00
jQuery ( async ( ) => {
moduleWorker ( ) ;
setInterval ( moduleWorker , UPDATE _INTERVAL ) ;
const settingsHtml = `
< div class = "quickReplySettings" >
< div class = "inline-drawer" >
< div class = "inline-drawer-toggle inline-drawer-header" >
< b > Quick Reply < / b >
< div class = "inline-drawer-icon fa-solid fa-circle-chevron-down down" > < / d i v >
< / d i v >
< div class = "inline-drawer-content" >
2023-10-07 18:25:36 +02:00
< div >
< label class = "checkbox_label" >
2023-07-29 23:22:03 +02:00
< input id = "quickReplyEnabled" type = "checkbox" / >
2023-10-21 15:09:25 +02:00
Enable Quick Replies
2023-07-29 23:22:03 +02:00
< / l a b e l >
2023-10-07 18:25:36 +02:00
< label class = "checkbox_label" >
2023-09-07 04:27:03 +02:00
< input id = "quickActionEnabled" type = "checkbox" / >
2023-10-21 15:09:25 +02:00
Disable Send / Insert In User Input
2023-09-07 04:27:03 +02:00
< / l a b e l >
2023-10-07 18:25:36 +02:00
< label class = "checkbox_label marginBot10" >
2023-10-21 15:02:29 +02:00
< input id = "placeBeforeInputEnabled" type = "checkbox" / >
2023-10-21 15:09:25 +02:00
Place Quick - reply before the Input
2023-10-21 15:02:29 +02:00
< / l a b e l >
< label class = "checkbox_label marginBot10" >
< input id = "AutoInputInject" type = "checkbox" / >
2023-10-21 15:09:25 +02:00
Inject user input automatically < br > ( If disabled , use { { input } } macro for manual injection )
2023-09-08 20:38:31 +02:00
< / l a b e l >
2023-10-21 15:17:17 +02:00
< label for = "quickReplyPresets" > Quick Reply presets : < / l a b e l >
2023-07-29 23:22:03 +02:00
< div class = "flex-container flexnowrap wide100p" >
2023-10-21 15:17:17 +02:00
< select id = "quickReplyPresets" name = "quickreply-preset" class = "flex1 text_pole" >
2023-07-29 23:22:03 +02:00
< / s e l e c t >
2023-10-21 15:17:17 +02:00
< div id = "quickReplyPresetSaveButton" class = "menu_button menu_button_icon" >
< div class = "fa-solid fa-save" > < / d i v >
2023-11-23 11:49:15 +01:00
< span > Save New < / s p a n >
< / d i v >
< div id = "quickReplyPresetUpdateButton" class = "menu_button menu_button_icon" >
< span > Update < / s p a n >
2023-10-21 15:17:17 +02:00
< / d i v >
2023-07-29 23:22:03 +02:00
< / d i v >
< label for = "quickReplyNumberOfSlots" > Number of slots : < / l a b e l >
< / d i v >
2023-07-20 19:32:15 +02:00
< div class = "flex-container flexGap5 flexnowrap" >
< input id = "quickReplyNumberOfSlots" class = "text_pole" type = "number" min = "1" max = "100" value = "" / >
< div class = "menu_button menu_button_icon" id = "quickReplyNumberOfSlotsApply" >
< div class = "fa-solid fa-check" > < / d i v >
< span > Apply < / s p a n >
< / d i v >
< / d i v >
< small > < i > Customize your Quick Replies : < / i > < / s m a l l > < b r >
< div id = "quickReplyContainer" >
< / d i v >
< / d i v >
< / d i v > ` ;
$ ( '#extensions_settings2' ) . append ( settingsHtml ) ;
2023-10-21 15:09:25 +02:00
2023-09-07 04:27:03 +02:00
// Add event handler for quickActionEnabled
$ ( '#quickActionEnabled' ) . on ( 'input' , onQuickActionEnabledInput ) ;
2023-10-21 15:02:29 +02:00
$ ( '#placeBeforeInputEnabled' ) . on ( 'input' , onPlaceBeforeInputEnabledInput ) ;
$ ( '#AutoInputInject' ) . on ( 'input' , onAutoInputInject ) ;
2023-07-20 19:32:15 +02:00
$ ( '#quickReplyEnabled' ) . on ( 'input' , onQuickReplyEnabledInput ) ;
$ ( '#quickReplyNumberOfSlotsApply' ) . on ( 'click' , onQuickReplyNumberOfSlotsInput ) ;
2023-12-02 19:04:51 +01:00
$ ( '#quickReplyPresetSaveButton' ) . on ( 'click' , saveQuickReplyPreset ) ;
$ ( '#quickReplyPresetUpdateButton' ) . on ( 'click' , updateQuickReplyPreset ) ;
2023-11-23 11:49:15 +01:00
$ ( '#quickReplyContainer' ) . sortable ( {
delay : getSortableDelay ( ) ,
stop : saveQROrder ,
} ) ;
2023-07-20 19:32:15 +02:00
2023-12-02 19:04:51 +01:00
$ ( '#quickReplyPresets' ) . on ( 'change' , async function ( ) {
2023-07-29 23:22:03 +02:00
const quickReplyPresetSelected = $ ( this ) . find ( ':selected' ) . val ( ) ;
extension _settings . quickReplyPreset = quickReplyPresetSelected ;
applyQuickReplyPreset ( quickReplyPresetSelected ) ;
saveSettingsDebounced ( ) ;
} ) ;
await loadSettings ( 'init' ) ;
2023-07-20 19:32:15 +02:00
addQuickReplyBar ( ) ;
2023-11-24 00:32:02 +01:00
2023-12-07 01:12:49 +01:00
eventSource . on ( event _types . CHARACTER _MESSAGE _RENDERED , onMessageReceived ) ;
eventSource . on ( event _types . USER _MESSAGE _RENDERED , onMessageSent ) ;
2023-11-24 13:02:04 +01:00
eventSource . on ( event _types . CHAT _CHANGED , onChatChanged ) ;
2023-11-26 01:12:31 +01:00
eventSource . on ( event _types . APP _READY , onAppReady ) ;
2023-07-20 19:32:15 +02:00
} ) ;
2023-10-21 15:09:25 +02:00
jQuery ( ( ) => {
2023-10-07 18:25:36 +02:00
registerSlashCommand ( 'qr' , doQR , [ ] , '<span class="monospace">(number)</span> – activates the specified Quick Reply' , true , true ) ;
registerSlashCommand ( 'qrset' , doQRPresetSwitch , [ ] , '<span class="monospace">(name)</span> – swaps to the specified Quick Reply Preset' , true , true ) ;
2023-12-14 17:00:38 +01:00
const qrArgs = `
label - string - text on the button , e . g . , label = MyButton
set - string - name of the QR set , e . g . , set = PresetName1
hidden - bool - whether the button should be hidden , e . g . , hidden = true
startup - bool - auto execute on app startup , e . g . , startup = true
user - bool - auto execute on user message , e . g . , user = true
bot - bool - auto execute on AI message , e . g . , bot = true
load - bool - auto execute on chat load , e . g . , load = true
title - bool - title / tooltip to be shown on button , e . g . , title = "My Fancy Button"
` .trim();
const qrUpdateArgs = `
2023-12-14 17:11:03 +01:00
newlabel - string - new text fort the button , e . g . newlabel = MyRenamedButton
2023-12-14 17:00:38 +01:00
$ { qrArgs }
` .trim();
2023-12-15 00:22:24 +01:00
registerSlashCommand ( 'qr-create' , qrCreateCallback , [ ] , ` <span class="monospace" style="white-space:pre-line;">(arguments [message]) \n arguments: \n ${ qrArgs } </span> – creates a new Quick Reply, example: <tt>/qr-create set=MyPreset label=MyButton /echo 123</tt> ` , true , true ) ;
registerSlashCommand ( 'qr-update' , qrUpdateCallback , [ ] , ` <span class="monospace" style="white-space:pre-line;">(arguments [message]) \n arguments: \n ${ qrUpdateArgs } </span> – updates Quick Reply, example: <tt>/qr-update set=MyPreset label=MyButton newlabel=MyRenamedButton /echo 123</tt> ` , true , true ) ;
2023-12-15 00:14:22 +01:00
registerSlashCommand ( 'qr-delete' , qrDeleteCallback , [ ] , '<span class="monospace">(set=string [label])</span> – deletes Quick Reply' , true , true ) ;
registerSlashCommand ( 'qr-contextadd' , qrContextAddCallback , [ ] , '<span class="monospace">(set=string label=string chain=bool [preset name])</span> – add context menu preset to a QR, example: <tt>/qr-contextadd set=MyPreset label=MyButton chain=true MyOtherPreset</tt>' , true , true ) ;
registerSlashCommand ( 'qr-contextdel' , qrContextDeleteCallback , [ ] , '<span class="monospace">(set=string label=string [preset name])</span> – remove context menu preset from a QR, example: <tt>/qr-contextdel set=MyPreset label=MyButton MyOtherPreset</tt>' , true , true ) ;
registerSlashCommand ( 'qr-contextclear' , qrContextClearCallback , [ ] , '<span class="monospace">(set=string [label])</span> – remove all context menu presets from a QR, example: <tt>/qr-contextclear set=MyPreset MyButton</tt>' , true , true ) ;
2023-12-14 19:51:55 +01:00
const presetArgs = `
enabled - bool - enable or disable the preset
nosend - bool - disable send / insert in user input ( invalid for slash commands )
before - bool - place QR before user input
slots - int - number of slots
inject - bool - inject user input automatically ( if disabled use { { input } } )
` .trim();
2023-12-15 00:22:24 +01:00
registerSlashCommand ( 'qr-presetadd' , qrPresetAddCallback , [ ] , ` <span class="monospace" style="white-space:pre-line;">(arguments [label]) \n arguments: \n ${ presetArgs } </span> – create a new preset (overrides existing ones), example: <tt>/qr-presetadd slots=3 MyNewPreset</tt> ` , true , true ) ;
registerSlashCommand ( 'qr-presetupdate' , qrPresetUpdateCallback , [ ] , ` <span class="monospace" style="white-space:pre-line;">(arguments [label]) \n arguments: \n ${ presetArgs } </span> – update an existing preset, example: <tt>/qr-presetupdate enabled=false MyPreset</tt> ` , true , true ) ;
2023-12-02 20:11:06 +01:00
} ) ;