2023-07-20 19:32:15 +02:00
import {
shuffle ,
onlyUnique ,
debounce ,
delay ,
isDataURL ,
createThumbnail ,
2023-07-30 22:10:37 +02:00
extractAllWords ,
2023-08-21 20:10:11 +02:00
saveBase64AsFile ,
PAGINATION _TEMPLATE ,
2023-10-08 10:34:39 +02:00
getBase64Async ,
2023-07-20 19:32:15 +02:00
} from './utils.js' ;
2023-12-02 19:04:51 +01:00
import { RA _CountCharTokens , humanizedDateTime , dragElement , favsToHotswap , getMessageTimeStamp } from './RossAscends-mods.js' ;
2023-08-19 01:53:05 +02:00
import { loadMovingUIState , sortEntitiesList } from './power-user.js' ;
2023-07-20 19:32:15 +02:00
import {
chat ,
sendSystemMessage ,
printMessages ,
substituteParams ,
characters ,
default _avatar ,
addOneMessage ,
callPopup ,
clearChat ,
Generate ,
select _rm _info ,
setCharacterId ,
setCharacterName ,
setEditedMessageId ,
is _send _press ,
name1 ,
resetChatState ,
setSendButtonState ,
getCharacters ,
system _message _types ,
online _status ,
talkativeness _default ,
selectRightMenuWithAnimation ,
default _ch _mes ,
deleteLastMessage ,
showSwipeButtons ,
hideSwipeButtons ,
chat _metadata ,
updateChatMetadata ,
isStreamingEnabled ,
getThumbnailUrl ,
getRequestHeaders ,
setMenuType ,
menu _type ,
select _selected _character ,
cancelTtsPlay ,
displayPastChats ,
sendMessageAsUser ,
getBiasStrings ,
saveChatConditional ,
deactivateSendButtons ,
activateSendButtons ,
eventSource ,
event _types ,
getCurrentChatId ,
setScenarioOverride ,
getCropPopup ,
2023-08-19 02:13:40 +02:00
system _avatar ,
2023-08-23 20:32:38 +02:00
isChatSaving ,
2023-08-24 20:23:35 +02:00
setExternalAbortController ,
2023-10-25 23:09:22 +02:00
baseChatReplace ,
2023-10-25 23:39:11 +02:00
depth _prompt _depth _default ,
2023-11-04 22:25:22 +01:00
loadItemizedPrompts ,
2023-12-11 15:23:21 +01:00
animation _duration ,
2023-12-02 19:04:51 +01:00
} from '../script.js' ;
2023-12-02 17:25:30 +01:00
import { appendTagToList , createTagMapFromList , getTagsList , applyTagsOnCharacterSelect , tag _map } from './tags.js' ;
2023-08-19 20:08:35 +02:00
import { FILTER _TYPES , FilterHelper } from './filters.js' ;
2023-07-20 19:32:15 +02:00
export {
selected _group ,
is _group _automode _enabled ,
2023-11-21 23:48:35 +01:00
hideMutedSprites ,
2023-07-20 19:32:15 +02:00
is _group _generating ,
group _generation _id ,
groups ,
saveGroupChat ,
generateGroupWrapper ,
deleteGroup ,
getGroupAvatar ,
getGroups ,
regenerateGroup ,
resetSelectedGroup ,
select _group _chats ,
2023-08-19 18:45:20 +02:00
getGroupChatNames ,
2023-12-02 20:11:06 +01:00
} ;
2023-07-20 19:32:15 +02:00
let is _group _generating = false ; // Group generation flag
let is _group _automode _enabled = false ;
2023-11-21 23:48:35 +01:00
let hideMutedSprites = true ;
2023-07-20 19:32:15 +02:00
let groups = [ ] ;
let selected _group = null ;
let group _generation _id = null ;
let fav _grp _checked = false ;
2023-08-19 01:53:05 +02:00
let openGroupId = null ;
let newGroupMembers = [ ] ;
2023-07-20 19:32:15 +02:00
export const group _activation _strategy = {
NATURAL : 0 ,
LIST : 1 ,
} ;
2023-10-25 21:39:31 +02:00
export const group _generation _mode = {
SWAP : 0 ,
APPEND : 1 ,
2023-12-02 20:11:06 +01:00
} ;
2023-10-25 21:39:31 +02:00
2023-12-16 01:52:48 +01:00
const DEFAULT _AUTO _MODE _DELAY = 5 ;
2023-08-18 22:13:15 +02:00
export const groupCandidatesFilter = new FilterHelper ( debounce ( printGroupCandidates , 100 ) ) ;
2023-12-16 01:52:48 +01:00
let autoModeWorker = null ;
2023-08-19 01:53:05 +02:00
const saveGroupDebounced = debounce ( async ( group , reload ) => await _save ( group , reload ) , 500 ) ;
2023-08-18 22:13:15 +02:00
2023-12-16 01:52:48 +01:00
function setAutoModeWorker ( ) {
clearInterval ( autoModeWorker ) ;
const autoModeDelay = groups . find ( x => x . id === selected _group ) ? . auto _mode _delay ? ? DEFAULT _AUTO _MODE _DELAY ;
autoModeWorker = setInterval ( groupChatAutoModeWorker , autoModeDelay * 1000 ) ;
}
2023-07-20 19:32:15 +02:00
async function _save ( group , reload = true ) {
2023-12-06 18:40:58 +01:00
await fetch ( '/api/groups/edit' , {
2023-12-02 19:04:51 +01:00
method : 'POST' ,
2023-07-20 19:32:15 +02:00
headers : getRequestHeaders ( ) ,
body : JSON . stringify ( group ) ,
} ) ;
if ( reload ) {
await getCharacters ( ) ;
}
}
// Group chats
async function regenerateGroup ( ) {
let generationId = getLastMessageGenerationId ( ) ;
while ( chat . length > 0 ) {
const lastMes = chat [ chat . length - 1 ] ;
const this _generationId = lastMes . extra ? . gen _id ;
// for new generations after the update
if ( ( generationId && this _generationId ) && generationId !== this _generationId ) {
break ;
}
// legacy for generations before the update
else if ( lastMes . is _user || lastMes . is _system ) {
break ;
}
await deleteLastMessage ( ) ;
}
2023-08-24 20:23:35 +02:00
const abortController = new AbortController ( ) ;
setExternalAbortController ( abortController ) ;
generateGroupWrapper ( false , 'normal' , { signal : abortController . signal } ) ;
2023-07-20 19:32:15 +02:00
}
async function loadGroupChat ( chatId ) {
2023-12-06 04:34:52 +01:00
const response = await fetch ( '/api/chats/group/get' , {
2023-12-02 19:04:51 +01:00
method : 'POST' ,
2023-07-20 19:32:15 +02:00
headers : getRequestHeaders ( ) ,
body : JSON . stringify ( { id : chatId } ) ,
} ) ;
if ( response . ok ) {
const data = await response . json ( ) ;
return data ;
}
return [ ] ;
}
export async function getGroupChat ( groupId ) {
const group = groups . find ( ( x ) => x . id === groupId ) ;
const chat _id = group . chat _id ;
const data = await loadGroupChat ( chat _id ) ;
2023-11-04 22:25:22 +01:00
await loadItemizedPrompts ( getCurrentChatId ( ) ) ;
2023-07-20 19:32:15 +02:00
if ( Array . isArray ( data ) && data . length ) {
data [ 0 ] . is _group = true ;
for ( let key of data ) {
chat . push ( key ) ;
}
2023-09-08 11:10:41 +02:00
await printMessages ( ) ;
2023-07-20 19:32:15 +02:00
} else {
sendSystemMessage ( system _message _types . GROUP , '' , { isSmallSys : true } ) ;
if ( group && Array . isArray ( group . members ) ) {
for ( let member of group . members ) {
const character = characters . find ( x => x . avatar === member || x . name === member ) ;
if ( ! character ) {
continue ;
}
const mes = getFirstCharacterMessage ( character ) ;
chat . push ( mes ) ;
addOneMessage ( mes ) ;
}
}
2023-08-19 14:58:17 +02:00
await saveGroupChat ( groupId , false ) ;
2023-07-20 19:32:15 +02:00
}
if ( group ) {
let metadata = group . chat _metadata ? ? { } ;
updateChatMetadata ( metadata , true ) ;
}
2023-10-29 22:23:42 +01:00
await eventSource . emit ( event _types . CHAT _CHANGED , getCurrentChatId ( ) ) ;
2023-07-20 19:32:15 +02:00
}
2023-11-20 22:49:04 +01:00
/ * *
* Finds the character ID for a group member .
* @ param { string } arg 1 - based member index or character name
* @ returns { number } 0 - based character ID
* /
export function findGroupMemberId ( arg ) {
arg = arg ? . trim ( ) ;
if ( ! arg ) {
console . warn ( 'WARN: No argument provided for findGroupMemberId' ) ;
return ;
}
const group = groups . find ( x => x . id == selected _group ) ;
if ( ! group || ! Array . isArray ( group . members ) ) {
console . warn ( 'WARN: No group found for selected group ID' ) ;
return ;
}
// Index is 1-based
const index = parseInt ( arg ) - 1 ;
const searchByName = isNaN ( index ) ;
if ( searchByName ) {
const memberNames = group . members . map ( x => ( { name : characters . find ( y => y . avatar === x ) ? . name , index : characters . findIndex ( y => y . avatar === x ) } ) ) ;
const fuse = new Fuse ( memberNames , { keys : [ 'name' ] } ) ;
const result = fuse . search ( arg ) ;
if ( ! result . length ) {
console . warn ( ` WARN: No group member found with name ${ arg } ` ) ;
return ;
}
const chid = result [ 0 ] . item . index ;
if ( chid === - 1 ) {
console . warn ( ` WARN: No character found for group member ${ arg } ` ) ;
return ;
}
console . log ( ` Triggering group member ${ chid } ( ${ arg } ) from search result ` , result [ 0 ] ) ;
return chid ;
} else {
const memberAvatar = group . members [ index ] ;
if ( memberAvatar === undefined ) {
console . warn ( ` WARN: No group member found at index ${ index } ` ) ;
return ;
}
const chid = characters . findIndex ( x => x . avatar === memberAvatar ) ;
if ( chid === - 1 ) {
console . warn ( ` WARN: No character found for group member ${ memberAvatar } at index ${ index } ` ) ;
return ;
}
console . log ( ` Triggering group member ${ memberAvatar } at index ${ index } ` ) ;
return chid ;
}
}
2023-10-25 23:39:11 +02:00
/ * *
* Gets depth prompts for group members .
* @ param { string } groupId Group ID
2023-10-26 01:10:14 +02:00
* @ param { number } characterId Current Character ID
2023-10-25 23:39:11 +02:00
* @ returns { { depth : number , text : string } [ ] } Array of depth prompts
* /
2023-10-26 01:10:14 +02:00
export function getGroupDepthPrompts ( groupId , characterId ) {
2023-10-25 23:39:11 +02:00
if ( ! groupId ) {
return [ ] ;
}
console . debug ( 'getGroupDepthPrompts entered for group: ' , groupId ) ;
const group = groups . find ( x => x . id === groupId ) ;
if ( ! group || ! Array . isArray ( group . members ) || ! group . members . length ) {
return [ ] ;
}
if ( group . generation _mode === group _generation _mode . SWAP ) {
return [ ] ;
}
const depthPrompts = [ ] ;
for ( const member of group . members ) {
2023-10-26 01:10:14 +02:00
const index = characters . findIndex ( x => x . avatar === member ) ;
const character = characters [ index ] ;
if ( index === - 1 || ! character ) {
console . debug ( ` Skipping missing member: ${ member } ` ) ;
2023-10-25 23:39:11 +02:00
continue ;
}
2023-10-26 01:10:14 +02:00
if ( group . disabled _members . includes ( member ) && characterId !== index ) {
console . debug ( ` Skipping disabled group member: ${ member } ` ) ;
2023-10-25 23:39:11 +02:00
continue ;
}
const depthPromptText = baseChatReplace ( character . data ? . extensions ? . depth _prompt ? . prompt ? . trim ( ) , name1 , character . name ) || '' ;
const depthPromptDepth = character . data ? . extensions ? . depth _prompt ? . depth ? ? depth _prompt _depth _default ;
if ( depthPromptText ) {
depthPrompts . push ( { text : depthPromptText , depth : depthPromptDepth } ) ;
}
}
return depthPrompts ;
}
2023-10-25 23:09:22 +02:00
/ * *
* Combines group members info a single string . Only for groups with generation mode set to APPEND .
* @ param { string } groupId Group ID
2023-10-26 01:10:14 +02:00
* @ param { number } characterId Current Character ID
2023-11-08 15:28:55 +01:00
* @ returns { { description : string , personality : string , scenario : string , mesExamples : string } } Group character cards combined
2023-10-25 23:09:22 +02:00
* /
2023-10-26 01:10:14 +02:00
export function getGroupCharacterCards ( groupId , characterId ) {
2023-10-25 23:09:22 +02:00
console . debug ( 'getGroupCharacterCards entered for group: ' , groupId ) ;
const group = groups . find ( x => x . id === groupId ) ;
if ( ! group || group ? . generation _mode !== group _generation _mode . APPEND || ! Array . isArray ( group . members ) || ! group . members . length ) {
return null ;
}
const scenarioOverride = chat _metadata [ 'scenario' ] ;
let descriptions = [ ] ;
let personalities = [ ] ;
let scenarios = [ ] ;
2023-11-08 15:28:55 +01:00
let mesExamplesArray = [ ] ;
2023-10-25 23:09:22 +02:00
for ( const member of group . members ) {
2023-10-26 01:10:14 +02:00
const index = characters . findIndex ( x => x . avatar === member ) ;
const character = characters [ index ] ;
if ( index === - 1 || ! character ) {
console . debug ( ` Skipping missing member: ${ member } ` ) ;
2023-10-25 23:09:22 +02:00
continue ;
}
2023-10-26 01:10:14 +02:00
if ( group . disabled _members . includes ( member ) && characterId !== index ) {
console . debug ( ` Skipping disabled group member: ${ member } ` ) ;
2023-10-25 23:09:22 +02:00
continue ;
}
descriptions . push ( baseChatReplace ( character . description . trim ( ) , name1 , character . name ) ) ;
personalities . push ( baseChatReplace ( character . personality . trim ( ) , name1 , character . name ) ) ;
scenarios . push ( baseChatReplace ( character . scenario . trim ( ) , name1 , character . name ) ) ;
2023-11-08 15:28:55 +01:00
mesExamplesArray . push ( baseChatReplace ( character . mes _example . trim ( ) , name1 , character . name ) ) ;
2023-10-25 23:09:22 +02:00
}
const description = descriptions . join ( '\n' ) ;
const personality = personalities . join ( '\n' ) ;
const scenario = scenarioOverride ? . trim ( ) || scenarios . join ( '\n' ) ;
2023-11-08 15:28:55 +01:00
const mesExamples = mesExamplesArray . join ( '\n' ) ;
2023-10-25 23:09:22 +02:00
2023-11-08 15:28:55 +01:00
return { description , personality , scenario , mesExamples } ;
2023-10-25 23:09:22 +02:00
}
2023-07-20 19:32:15 +02:00
function getFirstCharacterMessage ( character ) {
let messageText = character . first _mes ;
// if there are alternate greetings, pick one at random
if ( Array . isArray ( character . data ? . alternate _greetings ) ) {
const messageTexts = [ character . first _mes , ... character . data . alternate _greetings ] . filter ( x => x ) ;
messageText = messageTexts [ Math . floor ( Math . random ( ) * messageTexts . length ) ] ;
}
const mes = { } ;
2023-12-02 19:04:51 +01:00
mes [ 'is_user' ] = false ;
mes [ 'is_system' ] = false ;
mes [ 'name' ] = character . name ;
mes [ 'send_date' ] = getMessageTimeStamp ( ) ;
mes [ 'original_avatar' ] = character . avatar ;
mes [ 'extra' ] = { 'gen_id' : Date . now ( ) * Math . random ( ) * 1000000 } ;
mes [ 'mes' ] = messageText
2023-07-20 19:32:15 +02:00
? substituteParams ( messageText . trim ( ) , name1 , character . name )
: default _ch _mes ;
2023-12-02 19:04:51 +01:00
mes [ 'force_avatar' ] =
character . avatar != 'none'
2023-07-20 19:32:15 +02:00
? getThumbnailUrl ( 'avatar' , character . avatar )
: default _avatar ;
return mes ;
}
function resetSelectedGroup ( ) {
selected _group = null ;
is _group _generating = false ;
}
async function saveGroupChat ( groupId , shouldSaveGroup ) {
const group = groups . find ( x => x . id == groupId ) ;
const chat _id = group . chat _id ;
group [ 'date_last_chat' ] = Date . now ( ) ;
2023-12-06 04:34:52 +01:00
const response = await fetch ( '/api/chats/group/save' , {
2023-12-02 19:04:51 +01:00
method : 'POST' ,
2023-07-20 19:32:15 +02:00
headers : getRequestHeaders ( ) ,
body : JSON . stringify ( { id : chat _id , chat : [ ... chat ] } ) ,
} ) ;
if ( shouldSaveGroup && response . ok ) {
2023-08-19 01:53:05 +02:00
await editGroup ( groupId , false , false ) ;
2023-07-20 19:32:15 +02:00
}
}
export async function renameGroupMember ( oldAvatar , newAvatar , newName ) {
// Scan every group for our renamed character
for ( const group of groups ) {
try {
// Try finding the member by old avatar link
const memberIndex = group . members . findIndex ( x => x == oldAvatar ) ;
// Character was not present in the group...
if ( memberIndex == - 1 ) {
continue ;
}
// Replace group member avatar id and save the changes
group . members [ memberIndex ] = newAvatar ;
2023-08-19 01:53:05 +02:00
await editGroup ( group . id , true , false ) ;
2023-12-02 20:11:06 +01:00
console . log ( ` Renamed character ${ newName } in group: ${ group . name } ` ) ;
2023-07-20 19:32:15 +02:00
// Load all chats from this group
for ( const chatId of group . chats ) {
const messages = await loadGroupChat ( chatId ) ;
// Only save the chat if there were any changes to the chat content
let hadChanges = false ;
// Chat shouldn't be empty
if ( Array . isArray ( messages ) && messages . length ) {
// Iterate over every chat message
for ( const message of messages ) {
// Only look at character messages
if ( message . is _user || message . is _system ) {
continue ;
}
// Message belonged to the old-named character:
// Update name, avatar thumbnail URL and original avatar link
if ( message . force _avatar && message . force _avatar . indexOf ( encodeURIComponent ( oldAvatar ) ) !== - 1 ) {
message . name = newName ;
message . force _avatar = message . force _avatar . replace ( encodeURIComponent ( oldAvatar ) , encodeURIComponent ( newAvatar ) ) ;
message . original _avatar = newAvatar ;
hadChanges = true ;
}
}
if ( hadChanges ) {
2023-12-06 04:34:52 +01:00
const saveChatResponse = await fetch ( '/api/chats/group/save' , {
2023-12-02 19:04:51 +01:00
method : 'POST' ,
2023-07-20 19:32:15 +02:00
headers : getRequestHeaders ( ) ,
body : JSON . stringify ( { id : chatId , chat : [ ... messages ] } ) ,
} ) ;
if ( saveChatResponse . ok ) {
console . log ( ` Renamed character ${ newName } in group chat: ${ chatId } ` ) ;
}
}
}
}
}
catch ( error ) {
console . log ( ` An error during renaming the character ${ newName } in group: ${ group . name } ` ) ;
console . error ( error ) ;
}
}
}
async function getGroups ( ) {
2023-12-06 18:40:58 +01:00
const response = await fetch ( '/api/groups/all' , {
2023-12-02 19:04:51 +01:00
method : 'POST' ,
2023-12-02 21:06:57 +01:00
headers : getRequestHeaders ( ) ,
2023-07-20 19:32:15 +02:00
} ) ;
if ( response . ok ) {
const data = await response . json ( ) ;
groups = data . sort ( ( a , b ) => a . id - b . id ) ;
// Convert groups to new format
for ( const group of groups ) {
2023-08-19 21:22:24 +02:00
if ( typeof group . id === 'number' ) {
group . id = String ( group . id ) ;
}
2023-07-20 19:32:15 +02:00
if ( group . disabled _members == undefined ) {
group . disabled _members = [ ] ;
}
if ( group . chat _id == undefined ) {
group . chat _id = group . id ;
group . chats = [ group . id ] ;
group . members = group . members
. map ( x => characters . find ( y => y . name == x ) ? . avatar )
. filter ( x => x )
2023-12-02 20:11:06 +01:00
. filter ( onlyUnique ) ;
2023-07-20 19:32:15 +02:00
}
if ( group . past _metadata == undefined ) {
group . past _metadata = { } ;
}
if ( typeof group . chat _id === 'number' ) {
group . chat _id = String ( group . chat _id ) ;
}
if ( Array . isArray ( group . chats ) && group . chats . some ( x => typeof x === 'number' ) ) {
group . chats = group . chats . map ( x => String ( x ) ) ;
}
}
}
}
2023-08-18 22:13:15 +02:00
export function getGroupBlock ( group ) {
2023-12-02 19:04:51 +01:00
const template = $ ( '#group_list_template .group_select' ) . clone ( ) ;
template . data ( 'id' , group . id ) ;
template . attr ( 'grid' , group . id ) ;
template . find ( '.ch_name' ) . text ( group . name ) ;
template . find ( '.group_fav_icon' ) . css ( 'display' , 'none' ) ;
2023-08-20 11:37:38 +02:00
template . addClass ( group . fav ? 'is_fav' : '' ) ;
2023-12-02 19:04:51 +01:00
template . find ( '.ch_fav' ) . val ( group . fav ) ;
2023-08-18 22:13:15 +02:00
2023-08-20 11:37:38 +02:00
// Display inline tags
const tags = getTagsList ( group . id ) ;
const tagsElement = template . find ( '.tags' ) ;
tags . forEach ( tag => appendTagToList ( tagsElement , tag , { } ) ) ;
const avatar = getGroupAvatar ( group ) ;
if ( avatar ) {
2023-12-02 19:04:51 +01:00
$ ( template ) . find ( '.avatar' ) . replaceWith ( avatar ) ;
2023-08-20 11:37:38 +02:00
}
2023-08-18 22:13:15 +02:00
2023-08-20 11:37:38 +02:00
return template ;
2023-07-20 19:32:15 +02:00
}
2023-08-18 22:13:15 +02:00
2023-07-20 19:32:15 +02:00
function updateGroupAvatar ( group ) {
2023-12-02 19:04:51 +01:00
$ ( '#group_avatar_preview' ) . empty ( ) . append ( getGroupAvatar ( group ) ) ;
2023-08-19 02:13:40 +02:00
2023-12-02 19:04:51 +01:00
$ ( '.group_select' ) . each ( function ( ) {
if ( $ ( this ) . data ( 'id' ) == group . id ) {
$ ( this ) . find ( '.avatar' ) . replaceWith ( getGroupAvatar ( group ) ) ;
2023-07-20 19:32:15 +02:00
}
} ) ;
}
2023-08-20 05:53:34 +02:00
// check if isDataURLor if it's a valid local file url
function isValidImageUrl ( url ) {
// check if empty dict
if ( Object . keys ( url ) . length === 0 ) {
return false ;
}
2023-12-02 19:04:51 +01:00
return isDataURL ( url ) || ( url && url . startsWith ( 'user' ) ) ;
2023-08-20 05:53:34 +02:00
}
2023-07-20 19:32:15 +02:00
function getGroupAvatar ( group ) {
if ( ! group ) {
return $ ( ` <div class="avatar"><img src=" ${ default _avatar } "></div> ` ) ;
}
2023-08-20 11:37:38 +02:00
// if isDataURL or if it's a valid local file url
2023-08-20 05:53:34 +02:00
if ( isValidImageUrl ( group . avatar _url ) ) {
2023-07-20 19:32:15 +02:00
return $ ( ` <div class="avatar"><img src=" ${ group . avatar _url } "></div> ` ) ;
}
const memberAvatars = [ ] ;
if ( group && Array . isArray ( group . members ) && group . members . length ) {
for ( const member of group . members ) {
const charIndex = characters . findIndex ( x => x . avatar === member ) ;
2023-12-02 19:04:51 +01:00
if ( charIndex !== - 1 && characters [ charIndex ] . avatar !== 'none' ) {
2023-07-20 19:32:15 +02:00
const avatar = getThumbnailUrl ( 'avatar' , characters [ charIndex ] . avatar ) ;
memberAvatars . push ( avatar ) ;
}
if ( memberAvatars . length === 4 ) {
break ;
}
}
}
const avatarCount = memberAvatars . length ;
if ( avatarCount >= 1 && avatarCount <= 4 ) {
const groupAvatar = $ ( ` #group_avatars_template .collage_ ${ avatarCount } ` ) . clone ( ) ;
for ( let i = 0 ; i < avatarCount ; i ++ ) {
2023-12-02 19:04:51 +01:00
groupAvatar . find ( ` .img_ ${ i + 1 } ` ) . attr ( 'src' , memberAvatars [ i ] ) ;
2023-07-20 19:32:15 +02:00
}
return groupAvatar ;
}
// default avatar
2023-12-02 19:04:51 +01:00
const groupAvatar = $ ( '#group_avatars_template .collage_1' ) . clone ( ) ;
groupAvatar . find ( '.img_1' ) . attr ( 'src' , group . avatar _url || system _avatar ) ;
2023-07-20 19:32:15 +02:00
return groupAvatar ;
}
2023-08-19 18:45:20 +02:00
function getGroupChatNames ( groupId ) {
const group = groups . find ( x => x . id === groupId ) ;
if ( ! group ) {
return [ ] ;
}
const names = [ ] ;
for ( const chatId of group . chats ) {
names . push ( chatId ) ;
}
return names ;
}
2023-07-20 19:32:15 +02:00
async function generateGroupWrapper ( by _auto _mode , type = null , params = { } ) {
2023-12-11 19:03:31 +01:00
function throwIfAborted ( ) {
if ( params . signal instanceof AbortSignal && params . signal . aborted ) {
throw new Error ( 'AbortSignal was fired. Group generation stopped' ) ;
}
}
2023-12-02 19:04:51 +01:00
if ( online _status === 'no_connection' ) {
2023-07-20 19:32:15 +02:00
is _group _generating = false ;
setSendButtonState ( false ) ;
2023-12-07 23:46:15 +01:00
return Promise . resolve ( ) ;
2023-07-20 19:32:15 +02:00
}
if ( is _group _generating ) {
2023-12-07 23:46:15 +01:00
return Promise . resolve ( ) ;
2023-07-20 19:32:15 +02:00
}
// Auto-navigate back to group menu
2023-12-02 19:04:51 +01:00
if ( menu _type !== 'group_edit' ) {
2023-07-20 19:32:15 +02:00
select _group _chats ( selected _group ) ;
await delay ( 1 ) ;
}
const group = groups . find ( ( x ) => x . id === selected _group ) ;
2023-12-02 19:04:51 +01:00
let typingIndicator = $ ( '#chat .typing_indicator' ) ;
2023-12-11 18:00:42 +01:00
let textResult = '' ;
2023-07-20 19:32:15 +02:00
if ( ! group || ! Array . isArray ( group . members ) || ! group . members . length ) {
sendSystemMessage ( system _message _types . EMPTY , '' , { isSmallSys : true } ) ;
2023-12-07 23:46:15 +01:00
return Promise . resolve ( ) ;
2023-07-20 19:32:15 +02:00
}
try {
2023-12-11 19:03:31 +01:00
throwIfAborted ( ) ;
2023-07-20 19:32:15 +02:00
hideSwipeButtons ( ) ;
is _group _generating = true ;
setCharacterName ( '' ) ;
setCharacterId ( undefined ) ;
2023-12-02 19:04:51 +01:00
const userInput = String ( $ ( '#send_textarea' ) . val ( ) ) ;
2023-07-20 19:32:15 +02:00
if ( typingIndicator . length === 0 && ! isStreamingEnabled ( ) ) {
typingIndicator = $ (
2023-12-02 21:06:57 +01:00
'#typing_indicator_template .typing_indicator' ,
2023-07-20 19:32:15 +02:00
) . clone ( ) ;
typingIndicator . hide ( ) ;
2023-12-02 19:04:51 +01:00
$ ( '#chat' ) . append ( typingIndicator ) ;
2023-07-20 19:32:15 +02:00
}
// id of this specific batch for regeneration purposes
group _generation _id = Date . now ( ) ;
const lastMessage = chat [ chat . length - 1 ] ;
2023-12-02 19:04:51 +01:00
let activationText = '' ;
2023-07-20 19:32:15 +02:00
let isUserInput = false ;
if ( userInput ? . length && ! by _auto _mode ) {
isUserInput = true ;
activationText = userInput ;
} else {
if ( lastMessage && ! lastMessage . is _system ) {
activationText = lastMessage . mes ;
}
}
const activationStrategy = Number ( group . activation _strategy ? ? group _activation _strategy . NATURAL ) ;
const enabledMembers = group . members . filter ( x => ! group . disabled _members . includes ( x ) ) ;
let activatedMembers = [ ] ;
if ( params && typeof params . force _chid == 'number' ) {
activatedMembers = [ params . force _chid ] ;
2023-12-02 19:04:51 +01:00
} else if ( type === 'quiet' ) {
2023-07-20 19:32:15 +02:00
activatedMembers = activateSwipe ( group . members ) ;
if ( activatedMembers . length === 0 ) {
activatedMembers = activateListOrder ( group . members . slice ( 0 , 1 ) ) ;
}
}
2023-12-02 19:04:51 +01:00
else if ( type === 'swipe' || type === 'continue' ) {
2023-07-20 19:32:15 +02:00
activatedMembers = activateSwipe ( group . members ) ;
if ( activatedMembers . length === 0 ) {
toastr . warning ( 'Deleted group member swiped. To get a reply, add them back to the group.' ) ;
throw new Error ( 'Deleted group member swiped' ) ;
}
}
2023-12-02 19:04:51 +01:00
else if ( type === 'impersonate' ) {
$ ( '#send_textarea' ) . attr ( 'disabled' , true ) ;
2023-07-20 19:32:15 +02:00
activatedMembers = activateImpersonate ( group . members ) ;
}
else if ( activationStrategy === group _activation _strategy . NATURAL ) {
activatedMembers = activateNaturalOrder ( enabledMembers , activationText , lastMessage , group . allow _self _responses , isUserInput ) ;
}
else if ( activationStrategy === group _activation _strategy . LIST ) {
activatedMembers = activateListOrder ( enabledMembers ) ;
}
if ( activatedMembers . length === 0 ) {
2023-09-02 16:28:03 +02:00
//toastr.warning('All group members are disabled. Enable at least one to get a reply.');
2023-07-20 19:32:15 +02:00
// Send user message as is
const bias = getBiasStrings ( userInput , type ) ;
await sendMessageAsUser ( userInput , bias . messageBias ) ;
await saveChatConditional ( ) ;
$ ( '#send_textarea' ) . val ( '' ) . trigger ( 'input' ) ;
}
// now the real generation begins: cycle through every activated character
for ( const chId of activatedMembers ) {
2023-12-11 19:03:31 +01:00
throwIfAborted ( ) ;
2023-07-20 19:32:15 +02:00
deactivateSendButtons ( ) ;
2023-12-02 19:04:51 +01:00
const generateType = type == 'swipe' || type == 'impersonate' || type == 'quiet' || type == 'continue' ? type : 'group_chat' ;
2023-07-20 19:32:15 +02:00
setCharacterId ( chId ) ;
2023-12-02 20:11:06 +01:00
setCharacterName ( characters [ chId ] . name ) ;
2023-07-20 19:32:15 +02:00
2023-12-02 19:04:51 +01:00
if ( type !== 'swipe' && type !== 'impersonate' && ! isStreamingEnabled ( ) ) {
2023-07-20 19:32:15 +02:00
// update indicator and scroll down
typingIndicator
2023-12-02 19:04:51 +01:00
. find ( '.typing_indicator_name' )
2023-07-20 19:32:15 +02:00
. text ( characters [ chId ] . name ) ;
2023-09-08 15:36:00 +02:00
typingIndicator . show ( ) ;
2023-07-20 19:32:15 +02:00
}
2023-12-11 17:23:00 +01:00
// Wait for generation to finish
const generateFinished = await Generate ( generateType , { automatic _trigger : by _auto _mode , ... ( params || { } ) } ) ;
2023-12-11 18:00:42 +01:00
textResult = await generateFinished ;
2023-07-20 19:32:15 +02:00
}
} finally {
2023-09-08 15:36:00 +02:00
typingIndicator . hide ( ) ;
2023-07-20 19:32:15 +02:00
is _group _generating = false ;
2023-12-02 19:04:51 +01:00
$ ( '#send_textarea' ) . attr ( 'disabled' , false ) ;
2023-07-20 19:32:15 +02:00
setSendButtonState ( false ) ;
setCharacterId ( undefined ) ;
setCharacterName ( '' ) ;
activateSendButtons ( ) ;
showSwipeButtons ( ) ;
}
2023-12-07 23:46:15 +01:00
2023-12-11 18:00:42 +01:00
return Promise . resolve ( textResult ) ;
2023-07-20 19:32:15 +02:00
}
function getLastMessageGenerationId ( ) {
let generationId = null ;
if ( chat . length > 0 ) {
const lastMes = chat [ chat . length - 1 ] ;
if ( ! lastMes . is _user && ! lastMes . is _system && lastMes . extra ) {
generationId = lastMes . extra . gen _id ;
}
}
return generationId ;
}
function activateImpersonate ( members ) {
const randomIndex = Math . floor ( Math . random ( ) * members . length ) ;
const activatedMembers = [ members [ randomIndex ] ] ;
const memberIds = activatedMembers
. map ( ( x ) => characters . findIndex ( ( y ) => y . avatar === x ) )
. filter ( ( x ) => x !== - 1 ) ;
return memberIds ;
}
2023-12-10 23:02:49 +01:00
/ * *
* Activates a group member based on the last message .
* @ param { string [ ] } members Array of group member avatar ids
* @ returns { number [ ] } Array of character ids
* /
2023-07-20 19:32:15 +02:00
function activateSwipe ( members ) {
let activatedNames = [ ] ;
2023-12-11 14:08:20 +01:00
const lastMessage = chat [ chat . length - 1 ] ;
2023-07-20 19:32:15 +02:00
2023-12-11 14:08:20 +01:00
if ( lastMessage . is _user || lastMessage . is _system || lastMessage . extra ? . type === system _message _types . NARRATOR ) {
2023-12-10 23:02:49 +01:00
for ( const message of chat . slice ( ) . reverse ( ) ) {
2023-12-11 14:08:20 +01:00
if ( message . is _user || message . is _system || message . extra ? . type === system _message _types . NARRATOR ) {
2023-12-10 23:02:49 +01:00
continue ;
}
if ( message . original _avatar ) {
activatedNames . push ( message . original _avatar ) ;
break ;
}
}
if ( activatedNames . length === 0 ) {
activatedNames . push ( shuffle ( members . slice ( ) ) [ 0 ] ) ;
}
}
2023-07-20 19:32:15 +02:00
// pre-update group chat swipe
2023-12-11 14:08:20 +01:00
if ( ! lastMessage . original _avatar ) {
const matches = characters . filter ( x => x . name == lastMessage . name ) ;
2023-07-20 19:32:15 +02:00
for ( const match of matches ) {
if ( members . includes ( match . avatar ) ) {
activatedNames . push ( match . avatar ) ;
break ;
}
}
}
else {
2023-12-11 14:08:20 +01:00
activatedNames . push ( lastMessage . original _avatar ) ;
2023-07-20 19:32:15 +02:00
}
const memberIds = activatedNames
. map ( ( x ) => characters . findIndex ( ( y ) => y . avatar === x ) )
. filter ( ( x ) => x !== - 1 ) ;
return memberIds ;
}
function activateListOrder ( members ) {
let activatedMembers = members . filter ( onlyUnique ) ;
// map to character ids
const memberIds = activatedMembers
. map ( ( x ) => characters . findIndex ( ( y ) => y . avatar === x ) )
. filter ( ( x ) => x !== - 1 ) ;
return memberIds ;
}
function activateNaturalOrder ( members , input , lastMessage , allowSelfResponses , isUserInput ) {
let activatedMembers = [ ] ;
// prevents the same character from speaking twice
let bannedUser = ! isUserInput && lastMessage && ! lastMessage . is _user && lastMessage . name ;
// ...unless allowed to do so
if ( allowSelfResponses ) {
bannedUser = undefined ;
}
// find mentions (excluding self)
if ( input && input . length ) {
for ( let inputWord of extractAllWords ( input ) ) {
for ( let member of members ) {
2023-12-02 20:11:06 +01:00
const character = characters . find ( x => x . avatar === member ) ;
2023-07-20 19:32:15 +02:00
if ( ! character || character . name === bannedUser ) {
continue ;
}
if ( extractAllWords ( character . name ) . includes ( inputWord ) ) {
activatedMembers . push ( member ) ;
break ;
}
}
}
}
// activation by talkativeness (in shuffled order, except banned)
const shuffledMembers = shuffle ( [ ... members ] ) ;
for ( let member of shuffledMembers ) {
const character = characters . find ( ( x ) => x . avatar === member ) ;
if ( ! character || character . name === bannedUser ) {
continue ;
}
const rollValue = Math . random ( ) ;
let talkativeness = Number ( character . talkativeness ) ;
talkativeness = Number . isNaN ( talkativeness )
? talkativeness _default
: talkativeness ;
if ( talkativeness >= rollValue ) {
activatedMembers . push ( member ) ;
}
}
// pick 1 at random if no one was activated
let retries = 0 ;
while ( activatedMembers . length === 0 && ++ retries <= members . length ) {
const randomIndex = Math . floor ( Math . random ( ) * members . length ) ;
const character = characters . find ( ( x ) => x . avatar === members [ randomIndex ] ) ;
if ( ! character ) {
continue ;
}
activatedMembers . push ( members [ randomIndex ] ) ;
}
// de-duplicate array of character avatars
activatedMembers = activatedMembers . filter ( onlyUnique ) ;
// map to character ids
const memberIds = activatedMembers
. map ( ( x ) => characters . findIndex ( ( y ) => y . avatar === x ) )
. filter ( ( x ) => x !== - 1 ) ;
return memberIds ;
}
async function deleteGroup ( id ) {
2023-09-09 21:15:47 +02:00
const group = groups . find ( ( x ) => x . id === id ) ;
2023-12-06 18:40:58 +01:00
const response = await fetch ( '/api/groups/delete' , {
2023-12-02 19:04:51 +01:00
method : 'POST' ,
2023-07-20 19:32:15 +02:00
headers : getRequestHeaders ( ) ,
body : JSON . stringify ( { id : id } ) ,
} ) ;
2023-09-09 21:15:47 +02:00
if ( group && Array . isArray ( group . chats ) ) {
for ( const chatId of group . chats ) {
await eventSource . emit ( event _types . GROUP _CHAT _DELETED , chatId ) ;
}
}
2023-07-20 19:32:15 +02:00
if ( response . ok ) {
2023-11-04 22:25:22 +01:00
await clearChat ( ) ;
2023-07-20 19:32:15 +02:00
selected _group = null ;
delete tag _map [ id ] ;
resetChatState ( ) ;
2023-09-08 11:10:41 +02:00
await printMessages ( ) ;
2023-07-20 19:32:15 +02:00
await getCharacters ( ) ;
2023-12-02 19:04:51 +01:00
select _rm _info ( 'group_delete' , id ) ;
2023-07-20 19:32:15 +02:00
2023-12-02 19:04:51 +01:00
$ ( '#rm_button_selected_ch' ) . children ( 'h2' ) . text ( '' ) ;
2023-07-20 19:32:15 +02:00
}
}
export async function editGroup ( id , immediately , reload = true ) {
let group = groups . find ( ( x ) => x . id === id ) ;
if ( ! group ) {
return ;
}
group [ 'chat_metadata' ] = chat _metadata ;
if ( immediately ) {
return await _save ( group , reload ) ;
}
2023-08-19 01:53:05 +02:00
saveGroupDebounced ( group , reload ) ;
2023-07-20 19:32:15 +02:00
}
let groupAutoModeAbortController = null ;
async function groupChatAutoModeWorker ( ) {
2023-12-02 19:04:51 +01:00
if ( ! is _group _automode _enabled || online _status === 'no_connection' ) {
2023-07-20 19:32:15 +02:00
return ;
}
if ( ! selected _group || is _send _press || is _group _generating ) {
return ;
}
const group = groups . find ( ( x ) => x . id === selected _group ) ;
if ( ! group || ! Array . isArray ( group . members ) || ! group . members . length ) {
return ;
}
groupAutoModeAbortController = new AbortController ( ) ;
await generateGroupWrapper ( true , 'auto' , { signal : groupAutoModeAbortController . signal } ) ;
}
async function modifyGroupMember ( chat _id , groupMember , isDelete ) {
2023-12-02 19:04:51 +01:00
const id = groupMember . data ( 'id' ) ;
2023-08-19 01:53:05 +02:00
const thisGroup = groups . find ( ( x ) => x . id == chat _id ) ;
const membersArray = thisGroup ? . members ? ? newGroupMembers ;
2023-07-20 19:32:15 +02:00
if ( isDelete ) {
2023-08-19 01:53:05 +02:00
const index = membersArray . findIndex ( ( x ) => x === id ) ;
if ( index !== - 1 ) {
membersArray . splice ( membersArray . indexOf ( id ) , 1 ) ;
}
2023-07-20 19:32:15 +02:00
} else {
2023-08-19 01:53:05 +02:00
membersArray . unshift ( id ) ;
2023-07-20 19:32:15 +02:00
}
2023-08-19 01:53:05 +02:00
if ( openGroupId ) {
await editGroup ( openGroupId , false , false ) ;
updateGroupAvatar ( thisGroup ) ;
2023-07-20 19:32:15 +02:00
}
2023-08-19 01:53:05 +02:00
printGroupCandidates ( ) ;
printGroupMembers ( ) ;
const groupHasMembers = getGroupCharacters ( { doFilter : false , onlyMembers : true } ) . length > 0 ;
2023-12-02 19:04:51 +01:00
$ ( '#rm_group_submit' ) . prop ( 'disabled' , ! groupHasMembers ) ;
2023-07-20 19:32:15 +02:00
}
async function reorderGroupMember ( chat _id , groupMember , direction ) {
2023-12-02 19:04:51 +01:00
const id = groupMember . data ( 'id' ) ;
2023-08-19 02:13:40 +02:00
const thisGroup = groups . find ( ( x ) => x . id == chat _id ) ;
const memberArray = thisGroup ? . members ? ? newGroupMembers ;
2023-08-19 01:53:05 +02:00
const indexOf = memberArray . indexOf ( id ) ;
if ( direction == 'down' ) {
const next = memberArray [ indexOf + 1 ] ;
if ( next ) {
memberArray [ indexOf + 1 ] = memberArray [ indexOf ] ;
memberArray [ indexOf ] = next ;
}
}
if ( direction == 'up' ) {
const prev = memberArray [ indexOf - 1 ] ;
if ( prev ) {
memberArray [ indexOf - 1 ] = memberArray [ indexOf ] ;
memberArray [ indexOf ] = prev ;
}
}
printGroupMembers ( ) ;
2023-07-20 19:32:15 +02:00
// Existing groups need to modify members list
2023-08-19 01:53:05 +02:00
if ( openGroupId ) {
await editGroup ( chat _id , false , false ) ;
2023-08-19 02:13:40 +02:00
updateGroupAvatar ( thisGroup ) ;
2023-08-19 01:53:05 +02:00
}
}
async function onGroupActivationStrategyInput ( e ) {
if ( openGroupId ) {
let _thisGroup = groups . find ( ( x ) => x . id == openGroupId ) ;
_thisGroup . activation _strategy = Number ( e . target . value ) ;
await editGroup ( openGroupId , false , false ) ;
}
}
2023-10-25 21:39:31 +02:00
async function onGroupGenerationModeInput ( e ) {
if ( openGroupId ) {
let _thisGroup = groups . find ( ( x ) => x . id == openGroupId ) ;
_thisGroup . generation _mode = Number ( e . target . value ) ;
await editGroup ( openGroupId , false , false ) ;
}
}
2023-12-16 01:52:48 +01:00
async function onGroupAutoModeDelayInput ( e ) {
if ( openGroupId ) {
let _thisGroup = groups . find ( ( x ) => x . id == openGroupId ) ;
_thisGroup . auto _mode _delay = Number ( e . target . value ) ;
await editGroup ( openGroupId , false , false ) ;
setAutoModeWorker ( ) ;
}
}
2023-08-19 01:53:05 +02:00
async function onGroupNameInput ( ) {
if ( openGroupId ) {
let _thisGroup = groups . find ( ( x ) => x . id == openGroupId ) ;
_thisGroup . name = $ ( this ) . val ( ) ;
2023-12-02 19:04:51 +01:00
$ ( '#rm_button_selected_ch' ) . children ( 'h2' ) . text ( _thisGroup . name ) ;
2023-08-19 01:53:05 +02:00
await editGroup ( openGroupId ) ;
}
}
function isGroupMember ( group , avatarId ) {
if ( group && Array . isArray ( group . members ) ) {
return group . members . includes ( avatarId ) ;
} else {
return newGroupMembers . includes ( avatarId ) ;
}
}
function getGroupCharacters ( { doFilter , onlyMembers } = { } ) {
function sortMembersFn ( a , b ) {
const membersArray = thisGroup ? . members ? ? newGroupMembers ;
const aIndex = membersArray . indexOf ( a . item . avatar ) ;
const bIndex = membersArray . indexOf ( b . item . avatar ) ;
return aIndex - bIndex ;
}
const thisGroup = openGroupId && groups . find ( ( x ) => x . id == openGroupId ) ;
2023-08-19 02:22:10 +02:00
let candidates = characters
. filter ( ( x ) => isGroupMember ( thisGroup , x . avatar ) == onlyMembers )
. map ( ( x , index ) => ( { item : x , id : index , type : 'character' } ) ) ;
2023-08-19 01:53:05 +02:00
if ( onlyMembers ) {
candidates . sort ( sortMembersFn ) ;
} else {
sortEntitiesList ( candidates ) ;
}
if ( doFilter ) {
candidates = groupCandidatesFilter . applyFilters ( candidates ) ;
}
return candidates ;
}
function printGroupCandidates ( ) {
2023-08-19 15:11:09 +02:00
const storageKey = 'GroupCandidates_PerPage' ;
2023-12-02 19:04:51 +01:00
$ ( '#rm_group_add_members_pagination' ) . pagination ( {
2023-08-19 01:53:05 +02:00
dataSource : getGroupCharacters ( { doFilter : true , onlyMembers : false } ) ,
pageRange : 1 ,
position : 'top' ,
showPageNumbers : false ,
prevText : '<' ,
nextText : '>' ,
2023-08-21 20:10:11 +02:00
formatNavigator : PAGINATION _TEMPLATE ,
2023-08-19 01:53:05 +02:00
showNavigator : true ,
2023-08-19 15:11:09 +02:00
showSizeChanger : true ,
pageSize : Number ( localStorage . getItem ( storageKey ) ) || 5 ,
2023-12-18 00:39:37 +01:00
sizeChangerOptions : [ 5 , 10 , 25 , 50 , 100 , 200 , 500 , 1000 ] ,
2023-08-19 15:11:09 +02:00
afterSizeSelectorChange : function ( e ) {
localStorage . setItem ( storageKey , e . target . value ) ;
} ,
2023-08-19 01:53:05 +02:00
callback : function ( data ) {
2023-12-02 19:04:51 +01:00
$ ( '#rm_group_add_members' ) . empty ( ) ;
2023-08-19 01:53:05 +02:00
for ( const i of data ) {
2023-12-02 19:04:51 +01:00
$ ( '#rm_group_add_members' ) . append ( getGroupCharacterBlock ( i . item ) ) ;
2023-07-20 19:32:15 +02:00
}
2023-08-19 01:53:05 +02:00
} ,
} ) ;
}
function printGroupMembers ( ) {
2023-08-19 15:11:09 +02:00
const storageKey = 'GroupMembers_PerPage' ;
2023-12-02 19:04:51 +01:00
$ ( '.rm_group_members_pagination' ) . each ( function ( ) {
2023-10-15 18:42:23 +02:00
$ ( this ) . pagination ( {
dataSource : getGroupCharacters ( { doFilter : false , onlyMembers : true } ) ,
pageRange : 1 ,
position : 'top' ,
showPageNumbers : false ,
prevText : '<' ,
nextText : '>' ,
formatNavigator : PAGINATION _TEMPLATE ,
showNavigator : true ,
showSizeChanger : true ,
pageSize : Number ( localStorage . getItem ( storageKey ) ) || 5 ,
2023-12-18 00:39:37 +01:00
sizeChangerOptions : [ 5 , 10 , 25 , 50 , 100 , 200 , 500 , 1000 ] ,
2023-10-15 18:42:23 +02:00
afterSizeSelectorChange : function ( e ) {
localStorage . setItem ( storageKey , e . target . value ) ;
} ,
callback : function ( data ) {
2023-12-02 19:04:51 +01:00
$ ( '.rm_group_members' ) . empty ( ) ;
2023-10-15 18:42:23 +02:00
for ( const i of data ) {
2023-12-02 19:04:51 +01:00
$ ( '.rm_group_members' ) . append ( getGroupCharacterBlock ( i . item ) ) ;
2023-10-15 18:42:23 +02:00
}
} ,
} ) ;
2023-08-19 01:53:05 +02:00
} ) ;
}
2023-07-20 19:32:15 +02:00
2023-08-19 01:53:05 +02:00
function getGroupCharacterBlock ( character ) {
const avatar = getThumbnailUrl ( 'avatar' , character . avatar ) ;
2023-12-02 19:04:51 +01:00
const template = $ ( '#group_member_template .group_member' ) . clone ( ) ;
2023-08-19 01:53:05 +02:00
const isFav = character . fav || character . fav == 'true' ;
2023-12-02 19:04:51 +01:00
template . data ( 'id' , character . avatar ) ;
template . find ( '.avatar img' ) . attr ( { 'src' : avatar , 'title' : character . avatar } ) ;
template . find ( '.ch_name' ) . text ( character . name ) ;
template . attr ( 'chid' , characters . indexOf ( character ) ) ;
2023-08-19 01:53:05 +02:00
template . find ( '.ch_fav' ) . val ( isFav ) ;
template . toggleClass ( 'is_fav' , isFav ) ;
template . toggleClass ( 'disabled' , isGroupMemberDisabled ( character . avatar ) ) ;
// Display inline tags
const tags = getTagsList ( character . avatar ) ;
const tagsElement = template . find ( '.tags' ) ;
tags . forEach ( tag => appendTagToList ( tagsElement , tag , { } ) ) ;
if ( ! openGroupId ) {
template . find ( '[data-action="speak"]' ) . hide ( ) ;
template . find ( '[data-action="enable"]' ) . hide ( ) ;
template . find ( '[data-action="disable"]' ) . hide ( ) ;
}
return template ;
}
function isGroupMemberDisabled ( avatarId ) {
const thisGroup = openGroupId && groups . find ( ( x ) => x . id == openGroupId ) ;
return Boolean ( thisGroup && thisGroup . disabled _members . includes ( avatarId ) ) ;
}
function onDeleteGroupClick ( ) {
if ( is _group _generating ) {
toastr . warning ( 'Not so fast! Wait for the characters to stop typing before deleting the group.' ) ;
return ;
2023-07-20 19:32:15 +02:00
}
2023-08-19 01:53:05 +02:00
2023-12-02 19:04:51 +01:00
$ ( '#dialogue_popup' ) . data ( 'group_id' , openGroupId ) ;
callPopup ( '<h3>Delete the group?</h3><p>This will also delete all your chats with that group. If you want to delete a single conversation, select a "View past chats" option in the lower left menu.</p>' , 'del_group' ) ;
2023-08-19 01:53:05 +02:00
}
async function onFavoriteGroupClick ( ) {
updateFavButtonState ( ! fav _grp _checked ) ;
if ( openGroupId ) {
let _thisGroup = groups . find ( ( x ) => x . id == openGroupId ) ;
_thisGroup . fav = fav _grp _checked ;
await editGroup ( openGroupId , false , false ) ;
favsToHotswap ( ) ;
}
}
async function onGroupSelfResponsesClick ( ) {
if ( openGroupId ) {
let _thisGroup = groups . find ( ( x ) => x . id == openGroupId ) ;
2023-12-02 19:04:51 +01:00
const value = $ ( this ) . prop ( 'checked' ) ;
2023-08-19 01:53:05 +02:00
_thisGroup . allow _self _responses = value ;
await editGroup ( openGroupId , false , false ) ;
2023-07-20 19:32:15 +02:00
}
}
2023-11-21 23:48:35 +01:00
async function onHideMutedSpritesClick ( value ) {
if ( openGroupId ) {
let _thisGroup = groups . find ( ( x ) => x . id == openGroupId ) ;
_thisGroup . hideMutedSprites = value ;
2023-12-02 20:11:06 +01:00
console . log ( ` _thisGroup.hideMutedSprites = ${ _thisGroup . hideMutedSprites } ` ) ;
2023-11-21 23:48:35 +01:00
await editGroup ( openGroupId , false , false ) ;
}
}
2023-07-20 19:32:15 +02:00
function select _group _chats ( groupId , skipAnimation ) {
2023-08-19 01:53:05 +02:00
openGroupId = groupId ;
newGroupMembers = [ ] ;
const group = openGroupId && groups . find ( ( x ) => x . id == openGroupId ) ;
2023-12-02 19:04:51 +01:00
const groupName = group ? . name ? ? '' ;
2023-08-19 01:53:05 +02:00
const replyStrategy = Number ( group ? . activation _strategy ? ? group _activation _strategy . NATURAL ) ;
2023-10-25 23:09:22 +02:00
const generationMode = Number ( group ? . generation _mode ? ? group _generation _mode . SWAP ) ;
2023-08-19 01:53:05 +02:00
2023-12-02 16:15:47 +01:00
setMenuType ( group ? 'group_edit' : 'group_create' ) ;
2023-12-02 19:04:51 +01:00
$ ( '#group_avatar_preview' ) . empty ( ) . append ( getGroupAvatar ( group ) ) ;
$ ( '#rm_group_restore_avatar' ) . toggle ( ! ! group && isValidImageUrl ( group . avatar _url ) ) ;
$ ( '#rm_group_filter' ) . val ( '' ) . trigger ( 'input' ) ;
$ ( '#rm_group_activation_strategy' ) . val ( replyStrategy ) ;
2023-10-25 21:39:31 +02:00
$ ( ` #rm_group_activation_strategy option[value=" ${ replyStrategy } "] ` ) . prop ( 'selected' , true ) ;
2023-12-02 19:04:51 +01:00
$ ( '#rm_group_generation_mode' ) . val ( generationMode ) ;
2023-10-25 21:39:31 +02:00
$ ( ` #rm_group_generation_mode option[value=" ${ generationMode } "] ` ) . prop ( 'selected' , true ) ;
2023-12-02 19:04:51 +01:00
$ ( '#rm_group_chat_name' ) . val ( groupName ) ;
2023-07-20 19:32:15 +02:00
if ( ! skipAnimation ) {
selectRightMenuWithAnimation ( 'rm_group_chats_block' ) ;
}
// render characters list
2023-08-19 01:53:05 +02:00
printGroupCandidates ( ) ;
printGroupMembers ( ) ;
2023-07-20 19:32:15 +02:00
2023-12-02 19:04:51 +01:00
const groupHasMembers = ! ! $ ( '#rm_group_members' ) . children ( ) . length ;
$ ( '#rm_group_submit' ) . prop ( 'disabled' , ! groupHasMembers ) ;
$ ( '#rm_group_allow_self_responses' ) . prop ( 'checked' , group && group . allow _self _responses ) ;
$ ( '#rm_group_hidemutedsprites' ) . prop ( 'checked' , group && group . hideMutedSprites ) ;
2023-12-16 01:52:48 +01:00
$ ( '#rm_group_automode_delay' ) . val ( group ? . auto _mode _delay ? ? DEFAULT _AUTO _MODE _DELAY ) ;
2023-07-20 19:32:15 +02:00
// bottom buttons
2023-08-19 01:53:05 +02:00
if ( openGroupId ) {
2023-12-02 19:04:51 +01:00
$ ( '#rm_group_submit' ) . hide ( ) ;
$ ( '#rm_group_delete' ) . show ( ) ;
$ ( '#rm_group_scenario' ) . show ( ) ;
2023-10-16 22:03:42 +02:00
$ ( '#group-metadata-controls .chat_lorebook_button' ) . removeClass ( 'disabled' ) . prop ( 'disabled' , false ) ;
2023-07-20 19:32:15 +02:00
} else {
2023-12-02 19:04:51 +01:00
$ ( '#rm_group_submit' ) . show ( ) ;
if ( $ ( '#groupAddMemberListToggle .inline-drawer-content' ) . css ( 'display' ) !== 'block' ) {
$ ( '#groupAddMemberListToggle' ) . trigger ( 'click' ) ;
2023-07-20 19:32:15 +02:00
}
2023-12-02 19:04:51 +01:00
$ ( '#rm_group_delete' ) . hide ( ) ;
$ ( '#rm_group_scenario' ) . hide ( ) ;
2023-10-16 22:03:42 +02:00
$ ( '#group-metadata-controls .chat_lorebook_button' ) . addClass ( 'disabled' ) . prop ( 'disabled' , true ) ;
2023-07-20 19:32:15 +02:00
}
updateFavButtonState ( group ? . fav ? ? false ) ;
2023-12-16 01:52:48 +01:00
setAutoModeWorker ( ) ;
2023-07-20 19:32:15 +02:00
// top bar
if ( group ) {
2023-12-02 19:04:51 +01:00
$ ( '#rm_group_automode_label' ) . show ( ) ;
$ ( '#rm_button_selected_ch' ) . children ( 'h2' ) . text ( groupName ) ;
2023-07-20 19:32:15 +02:00
}
else {
2023-12-02 19:04:51 +01:00
$ ( '#rm_group_automode_label' ) . hide ( ) ;
2023-07-20 19:32:15 +02:00
}
2023-08-20 11:37:38 +02:00
eventSource . emit ( 'groupSelected' , { detail : { id : openGroupId , group : group } } ) ;
2023-08-19 01:53:05 +02:00
}
2023-07-20 19:32:15 +02:00
2023-08-20 06:15:57 +02:00
/ * *
* Handles the upload and processing of a group avatar .
* The selected image is read , cropped using a popup , processed into a thumbnail ,
* and then uploaded to the server .
2023-08-20 11:37:38 +02:00
*
2023-08-20 06:15:57 +02:00
* @ param { Event } event - The event triggered by selecting a file input , containing the image file to upload .
2023-08-20 11:37:38 +02:00
*
2023-08-20 06:15:57 +02:00
* @ returns { Promise < void > } - A promise that resolves when the processing and upload is complete .
* /
2023-08-19 01:53:05 +02:00
async function uploadGroupAvatar ( event ) {
const file = event . target . files [ 0 ] ;
2023-07-20 19:32:15 +02:00
2023-08-19 01:53:05 +02:00
if ( ! file ) {
return ;
}
2023-07-20 19:32:15 +02:00
2023-10-08 10:34:39 +02:00
const result = await getBase64Async ( file ) ;
2023-07-20 19:32:15 +02:00
2023-08-19 01:53:05 +02:00
$ ( '#dialogue_popup' ) . addClass ( 'large_dialogue_popup wide_dialogue_popup' ) ;
2023-07-20 19:32:15 +02:00
2023-10-08 10:34:39 +02:00
const croppedImage = await callPopup ( getCropPopup ( result ) , 'avatarToCrop' ) ;
2023-07-20 19:32:15 +02:00
2023-08-19 01:53:05 +02:00
if ( ! croppedImage ) {
return ;
}
2023-07-20 19:32:15 +02:00
2023-10-08 10:34:39 +02:00
let thumbnail = await createThumbnail ( croppedImage , 200 , 300 ) ;
2023-08-20 05:53:34 +02:00
//remove data:image/whatever;base64
2023-12-02 19:04:51 +01:00
thumbnail = thumbnail . replace ( /^data:image\/[a-z]+;base64,/ , '' ) ;
2023-08-20 07:41:58 +02:00
let _thisGroup = groups . find ( ( x ) => x . id == openGroupId ) ;
// filename should be group id + human readable timestamp
2023-10-08 10:34:39 +02:00
const filename = _thisGroup ? ` ${ _thisGroup . id } _ ${ humanizedDateTime ( ) } ` : humanizedDateTime ( ) ;
let thumbnailUrl = await saveBase64AsFile ( thumbnail , String ( openGroupId ? ? '' ) , filename , 'jpg' ) ;
2023-08-19 01:53:05 +02:00
if ( ! openGroupId ) {
2023-08-20 05:53:34 +02:00
$ ( '#group_avatar_preview img' ) . attr ( 'src' , thumbnailUrl ) ;
2023-08-19 01:53:05 +02:00
$ ( '#rm_group_restore_avatar' ) . show ( ) ;
return ;
2023-07-20 19:32:15 +02:00
}
2023-08-20 05:53:34 +02:00
_thisGroup . avatar _url = thumbnailUrl ;
2023-12-02 19:04:51 +01:00
$ ( '#group_avatar_preview' ) . empty ( ) . append ( getGroupAvatar ( _thisGroup ) ) ;
$ ( '#rm_group_restore_avatar' ) . show ( ) ;
2023-08-19 01:53:05 +02:00
await editGroup ( openGroupId , true , true ) ;
}
2023-07-20 19:32:15 +02:00
2023-08-19 01:53:05 +02:00
async function restoreGroupAvatar ( ) {
const confirm = await callPopup ( '<h3>Are you sure you want to restore the group avatar?</h3> Your custom image will be deleted, and a collage will be used instead.' , 'confirm' ) ;
2023-07-20 19:32:15 +02:00
2023-08-19 01:53:05 +02:00
if ( ! confirm ) {
return ;
}
2023-07-20 19:32:15 +02:00
2023-08-19 01:53:05 +02:00
if ( ! openGroupId ) {
2023-12-02 19:04:51 +01:00
$ ( '#group_avatar_preview img' ) . attr ( 'src' , default _avatar ) ;
$ ( '#rm_group_restore_avatar' ) . hide ( ) ;
2023-08-19 01:53:05 +02:00
return ;
2023-07-20 19:32:15 +02:00
}
2023-08-19 01:53:05 +02:00
let _thisGroup = groups . find ( ( x ) => x . id == openGroupId ) ;
_thisGroup . avatar _url = '' ;
2023-12-02 19:04:51 +01:00
$ ( '#group_avatar_preview' ) . empty ( ) . append ( getGroupAvatar ( _thisGroup ) ) ;
$ ( '#rm_group_restore_avatar' ) . hide ( ) ;
2023-08-19 01:53:05 +02:00
await editGroup ( openGroupId , true , true ) ;
}
2023-07-20 19:32:15 +02:00
2023-08-19 01:53:05 +02:00
async function onGroupActionClick ( event ) {
event . stopPropagation ( ) ;
const action = $ ( this ) . data ( 'action' ) ;
const member = $ ( this ) . closest ( '.group_member' ) ;
2023-07-20 19:32:15 +02:00
2023-08-19 01:53:05 +02:00
if ( action === 'remove' ) {
await modifyGroupMember ( openGroupId , member , true ) ;
}
2023-07-20 19:32:15 +02:00
2023-08-19 01:53:05 +02:00
if ( action === 'add' ) {
await modifyGroupMember ( openGroupId , member , false ) ;
}
2023-07-20 19:32:15 +02:00
2023-08-19 01:53:05 +02:00
if ( action === 'enable' ) {
member . removeClass ( 'disabled' ) ;
const _thisGroup = groups . find ( x => x . id === openGroupId ) ;
const index = _thisGroup . disabled _members . indexOf ( member . data ( 'id' ) ) ;
if ( index !== - 1 ) {
_thisGroup . disabled _members . splice ( index , 1 ) ;
2023-07-20 19:32:15 +02:00
}
2023-08-19 01:53:05 +02:00
await editGroup ( openGroupId , false , false ) ;
}
2023-07-20 19:32:15 +02:00
2023-08-19 01:53:05 +02:00
if ( action === 'disable' ) {
member . addClass ( 'disabled' ) ;
const _thisGroup = groups . find ( x => x . id === openGroupId ) ;
_thisGroup . disabled _members . push ( member . data ( 'id' ) ) ;
await editGroup ( openGroupId , false , false ) ;
}
2023-07-20 19:32:15 +02:00
2023-08-19 01:53:05 +02:00
if ( action === 'up' || action === 'down' ) {
await reorderGroupMember ( openGroupId , member , action ) ;
}
2023-07-20 19:32:15 +02:00
2023-08-19 01:53:05 +02:00
if ( action === 'view' ) {
openCharacterDefinition ( member ) ;
}
2023-07-20 19:32:15 +02:00
2023-08-19 01:53:05 +02:00
if ( action === 'speak' ) {
const chid = Number ( member . attr ( 'chid' ) ) ;
if ( Number . isInteger ( chid ) ) {
Generate ( 'normal' , { force _chid : chid } ) ;
}
}
2023-06-04 17:45:03 +02:00
2023-08-19 01:53:05 +02:00
await eventSource . emit ( event _types . GROUP _UPDATED ) ;
2023-07-20 19:32:15 +02:00
}
function updateFavButtonState ( state ) {
fav _grp _checked = state ;
2023-12-02 19:04:51 +01:00
$ ( '#rm_group_fav' ) . val ( fav _grp _checked ) ;
$ ( '#group_favorite_button' ) . toggleClass ( 'fav_on' , fav _grp _checked ) ;
$ ( '#group_favorite_button' ) . toggleClass ( 'fav_off' , ! fav _grp _checked ) ;
2023-07-20 19:32:15 +02:00
}
2023-08-19 20:44:15 +02:00
export async function openGroupById ( groupId ) {
2023-08-23 20:32:38 +02:00
if ( isChatSaving ) {
2023-12-02 19:04:51 +01:00
toastr . info ( 'Please wait until the chat is saved before switching characters.' , 'Your chat is still saving...' ) ;
2023-08-23 20:32:38 +02:00
return ;
}
2023-08-20 12:15:02 +02:00
if ( ! groups . find ( x => x . id === groupId ) ) {
console . log ( 'Group not found' , groupId ) ;
return ;
}
2023-07-20 19:32:15 +02:00
if ( ! is _send _press && ! is _group _generating ) {
if ( selected _group !== groupId ) {
2023-11-04 22:25:22 +01:00
await clearChat ( ) ;
2023-07-20 19:32:15 +02:00
cancelTtsPlay ( ) ;
selected _group = groupId ;
setCharacterId ( undefined ) ;
setCharacterName ( '' ) ;
setEditedMessageId ( undefined ) ;
updateChatMetadata ( { } , true ) ;
chat . length = 0 ;
await getGroupChat ( groupId ) ;
}
select _group _chats ( groupId ) ;
}
}
function openCharacterDefinition ( characterSelect ) {
if ( is _group _generating ) {
2023-12-02 19:04:51 +01:00
toastr . warning ( 'Can\'t peek a character while group reply is being generated' ) ;
console . warn ( 'Can\'t peek a character def while group reply is being generated' ) ;
2023-07-20 19:32:15 +02:00
return ;
}
const chid = characterSelect . attr ( 'chid' ) ;
if ( chid === null || chid === undefined ) {
return ;
}
setCharacterId ( chid ) ;
select _selected _character ( chid ) ;
// Gentle nudge to recalculate tokens
RA _CountCharTokens ( ) ;
// Do a little tomfoolery to spoof the tag selector
applyTagsOnCharacterSelect . call ( characterSelect ) ;
}
function filterGroupMembers ( ) {
2023-08-22 13:30:49 +02:00
const searchValue = String ( $ ( this ) . val ( ) ) . toLowerCase ( ) ;
2023-08-19 20:08:35 +02:00
groupCandidatesFilter . setFilterData ( FILTER _TYPES . SEARCH , searchValue ) ;
2023-07-20 19:32:15 +02:00
}
async function createGroup ( ) {
2023-12-02 19:04:51 +01:00
let name = $ ( '#rm_group_chat_name' ) . val ( ) ;
let allowSelfResponses = ! ! $ ( '#rm_group_allow_self_responses' ) . prop ( 'checked' ) ;
2023-10-25 21:39:31 +02:00
let activationStrategy = Number ( $ ( '#rm_group_activation_strategy' ) . find ( ':selected' ) . val ( ) ) ? ? group _activation _strategy . NATURAL ;
let generationMode = Number ( $ ( '#rm_group_generation_mode' ) . find ( ':selected' ) . val ( ) ) ? ? group _generation _mode . SWAP ;
2023-12-16 01:52:48 +01:00
let autoModeDelay = Number ( $ ( '#rm_group_automode_delay' ) . val ( ) ) ? ? DEFAULT _AUTO _MODE _DELAY ;
2023-08-19 01:53:05 +02:00
const members = newGroupMembers ;
2023-12-02 19:04:51 +01:00
const memberNames = characters . filter ( x => members . includes ( x . avatar ) ) . map ( x => x . name ) . join ( ', ' ) ;
2023-07-20 19:32:15 +02:00
if ( ! name ) {
name = ` Group: ${ memberNames } ` ;
}
const avatar _url = $ ( '#group_avatar_preview img' ) . attr ( 'src' ) ;
const chatName = humanizedDateTime ( ) ;
const chats = [ chatName ] ;
2023-12-06 18:40:58 +01:00
const createGroupResponse = await fetch ( '/api/groups/create' , {
2023-12-02 19:04:51 +01:00
method : 'POST' ,
2023-07-20 19:32:15 +02:00
headers : getRequestHeaders ( ) ,
body : JSON . stringify ( {
name : name ,
members : members ,
2023-08-20 05:53:34 +02:00
avatar _url : isValidImageUrl ( avatar _url ) ? avatar _url : default _avatar ,
2023-10-25 21:39:31 +02:00
allow _self _responses : allowSelfResponses ,
2023-11-21 23:48:35 +01:00
hideMutedSprites : hideMutedSprites ,
2023-10-25 21:39:31 +02:00
activation _strategy : activationStrategy ,
generation _mode : generationMode ,
2023-07-20 19:32:15 +02:00
disabled _members : [ ] ,
chat _metadata : { } ,
fav : fav _grp _checked ,
chat _id : chatName ,
chats : chats ,
2023-12-16 01:52:48 +01:00
auto _mode _delay : autoModeDelay ,
2023-07-20 19:32:15 +02:00
} ) ,
} ) ;
if ( createGroupResponse . ok ) {
2023-08-19 01:53:05 +02:00
newGroupMembers = [ ] ;
2023-07-20 19:32:15 +02:00
const data = await createGroupResponse . json ( ) ;
2023-12-02 19:04:51 +01:00
createTagMapFromList ( '#groupTagList' , data . id ) ;
2023-07-20 19:32:15 +02:00
await getCharacters ( ) ;
select _rm _info ( 'group_create' , data . id ) ;
}
}
export async function createNewGroupChat ( groupId ) {
const group = groups . find ( x => x . id === groupId ) ;
if ( ! group ) {
return ;
}
const oldChatName = group . chat _id ;
const newChatName = humanizedDateTime ( ) ;
if ( typeof group . past _metadata !== 'object' ) {
group . past _metadata = { } ;
}
2023-11-04 22:25:22 +01:00
await clearChat ( ) ;
2023-07-20 19:32:15 +02:00
chat . length = 0 ;
if ( oldChatName ) {
group . past _metadata [ oldChatName ] = Object . assign ( { } , chat _metadata ) ;
}
group . chats . push ( newChatName ) ;
group . chat _id = newChatName ;
group . chat _metadata = { } ;
updateChatMetadata ( group . chat _metadata , true ) ;
2023-08-22 15:11:07 +02:00
await editGroup ( group . id , true , false ) ;
2023-07-20 19:32:15 +02:00
await getGroupChat ( group . id ) ;
}
export async function getGroupPastChats ( groupId ) {
const group = groups . find ( x => x . id === groupId ) ;
if ( ! group ) {
return [ ] ;
}
const chats = [ ] ;
try {
for ( const chatId of group . chats ) {
const messages = await loadGroupChat ( chatId ) ;
2023-12-02 19:04:51 +01:00
let this _chat _file _size = ( JSON . stringify ( messages ) . length / 1024 ) . toFixed ( 2 ) + 'kb' ;
2023-07-20 19:32:15 +02:00
let chat _items = messages . length ;
const lastMessage = messages . length ? messages [ messages . length - 1 ] . mes : '[The chat is empty]' ;
const lastMessageDate = messages . length ? ( messages [ messages . length - 1 ] . send _date || Date . now ( ) ) : Date . now ( ) ;
chats . push ( {
'file_name' : chatId ,
'mes' : lastMessage ,
'last_mes' : lastMessageDate ,
'file_size' : this _chat _file _size ,
'chat_items' : chat _items ,
} ) ;
}
} catch ( err ) {
console . error ( err ) ;
}
2023-12-02 15:12:38 +01:00
return chats ;
2023-07-20 19:32:15 +02:00
}
export async function openGroupChat ( groupId , chatId ) {
const group = groups . find ( x => x . id === groupId ) ;
if ( ! group || ! group . chats . includes ( chatId ) ) {
return ;
}
2023-11-04 22:25:22 +01:00
await clearChat ( ) ;
2023-07-20 19:32:15 +02:00
chat . length = 0 ;
const previousChat = group . chat _id ;
group . past _metadata [ previousChat ] = Object . assign ( { } , chat _metadata ) ;
group . chat _id = chatId ;
group . chat _metadata = group . past _metadata [ chatId ] || { } ;
group [ 'date_last_chat' ] = Date . now ( ) ;
updateChatMetadata ( group . chat _metadata , true ) ;
2023-08-19 01:53:05 +02:00
await editGroup ( groupId , true , false ) ;
2023-07-20 19:32:15 +02:00
await getGroupChat ( groupId ) ;
}
export async function renameGroupChat ( groupId , oldChatId , newChatId ) {
const group = groups . find ( x => x . id === groupId ) ;
if ( ! group || ! group . chats . includes ( oldChatId ) ) {
return ;
}
if ( group . chat _id === oldChatId ) {
group . chat _id = newChatId ;
}
group . chats . splice ( group . chats . indexOf ( oldChatId ) , 1 ) ;
group . chats . push ( newChatId ) ;
group . past _metadata [ newChatId ] = ( group . past _metadata [ oldChatId ] || { } ) ;
delete group . past _metadata [ oldChatId ] ;
await editGroup ( groupId , true , true ) ;
}
export async function deleteGroupChat ( groupId , chatId ) {
const group = groups . find ( x => x . id === groupId ) ;
if ( ! group || ! group . chats . includes ( chatId ) ) {
return ;
}
group . chats . splice ( group . chats . indexOf ( chatId ) , 1 ) ;
group . chat _metadata = { } ;
group . chat _id = '' ;
delete group . past _metadata [ chatId ] ;
updateChatMetadata ( group . chat _metadata , true ) ;
2023-12-06 04:34:52 +01:00
const response = await fetch ( '/api/chats/group/delete' , {
2023-07-20 19:32:15 +02:00
method : 'POST' ,
headers : getRequestHeaders ( ) ,
body : JSON . stringify ( { id : chatId } ) ,
} ) ;
if ( response . ok ) {
if ( group . chats . length ) {
await openGroupChat ( groupId , group . chats [ 0 ] ) ;
} else {
await createNewGroupChat ( groupId ) ;
}
2023-09-09 21:15:47 +02:00
await eventSource . emit ( event _types . GROUP _CHAT _DELETED , chatId ) ;
2023-07-20 19:32:15 +02:00
}
}
export async function importGroupChat ( formData ) {
await jQuery . ajax ( {
2023-12-02 19:04:51 +01:00
type : 'POST' ,
2023-12-06 04:34:52 +01:00
url : '/api/chats/group/import' ,
2023-07-20 19:32:15 +02:00
data : formData ,
beforeSend : function ( ) {
} ,
cache : false ,
contentType : false ,
processData : false ,
success : async function ( data ) {
if ( data . res ) {
const chatId = data . res ;
const group = groups . find ( x => x . id == selected _group ) ;
if ( group ) {
group . chats . push ( chatId ) ;
await editGroup ( selected _group , true , true ) ;
await displayPastChats ( ) ;
}
}
} ,
error : function ( ) {
2023-12-02 19:04:51 +01:00
$ ( '#create_button' ) . removeAttr ( 'disabled' ) ;
2023-07-20 19:32:15 +02:00
} ,
} ) ;
}
export async function saveGroupBookmarkChat ( groupId , name , metadata , mesId ) {
const group = groups . find ( x => x . id === groupId ) ;
if ( ! group ) {
return ;
}
group . past _metadata [ name ] = { ... chat _metadata , ... ( metadata || { } ) } ;
group . chats . push ( name ) ;
const trimmed _chat = ( mesId !== undefined && mesId >= 0 && mesId < chat . length )
? chat . slice ( 0 , parseInt ( mesId ) + 1 )
: chat ;
2023-08-19 01:53:05 +02:00
await editGroup ( groupId , true , false ) ;
2023-07-20 19:32:15 +02:00
2023-12-06 04:34:52 +01:00
await fetch ( '/api/chats/group/save' , {
2023-12-02 19:04:51 +01:00
method : 'POST' ,
2023-07-20 19:32:15 +02:00
headers : getRequestHeaders ( ) ,
body : JSON . stringify ( { id : name , chat : [ ... trimmed _chat ] } ) ,
} ) ;
}
function onSendTextareaInput ( ) {
if ( is _group _automode _enabled ) {
// Wait for current automode generation to finish
is _group _automode _enabled = false ;
2023-12-02 19:04:51 +01:00
$ ( '#rm_group_automode' ) . prop ( 'checked' , false ) ;
2023-07-20 19:32:15 +02:00
}
}
function stopAutoModeGeneration ( ) {
if ( groupAutoModeAbortController ) {
groupAutoModeAbortController . abort ( ) ;
}
is _group _automode _enabled = false ;
2023-12-02 19:04:51 +01:00
$ ( '#rm_group_automode' ) . prop ( 'checked' , false ) ;
2023-07-20 19:32:15 +02:00
}
function doCurMemberListPopout ( ) {
//repurposes the zoomed avatar template to server as a floating group member list
2023-12-02 19:04:51 +01:00
if ( $ ( '#groupMemberListPopout' ) . length === 0 ) {
2023-12-02 20:11:06 +01:00
console . debug ( 'did not see popout yet, creating' ) ;
const memberListClone = $ ( this ) . parent ( ) . parent ( ) . find ( '.inline-drawer-content' ) . html ( ) ;
2023-07-20 19:32:15 +02:00
const template = $ ( '#zoomed_avatar_template' ) . html ( ) ;
const controlBarHtml = ` <div class="panelControlBar flex-container">
< div id = "groupMemberListPopoutheader" class = "fa-solid fa-grip drag-grabber hoverglow" > < / d i v >
< div id = "groupMemberListPopoutClose" class = "fa-solid fa-circle-xmark hoverglow" > < / d i v >
2023-12-02 20:11:06 +01:00
< / d i v > ` ;
2023-07-20 19:32:15 +02:00
const newElement = $ ( template ) ;
2023-10-13 17:29:41 +02:00
newElement . attr ( 'id' , 'groupMemberListPopout' )
. removeClass ( 'zoomed_avatar' )
. addClass ( 'draggable' )
. empty ( )
. append ( controlBarHtml )
2023-12-02 20:11:06 +01:00
. append ( memberListClone ) ;
2023-10-13 17:29:41 +02:00
2023-10-15 18:42:23 +02:00
// Remove pagination from popout
newElement . find ( '.group_pagination' ) . empty ( ) ;
2023-07-20 19:32:15 +02:00
$ ( 'body' ) . append ( newElement ) ;
loadMovingUIState ( ) ;
2023-12-11 15:23:21 +01:00
$ ( '#groupMemberListPopout' ) . fadeIn ( animation _duration ) ;
2023-12-02 20:11:06 +01:00
dragElement ( newElement ) ;
2023-07-20 19:32:15 +02:00
$ ( '#groupMemberListPopoutClose' ) . off ( 'click' ) . on ( 'click' , function ( ) {
2023-12-11 15:23:21 +01:00
$ ( '#groupMemberListPopout' ) . fadeOut ( animation _duration , ( ) => { $ ( '#groupMemberListPopout' ) . remove ( ) ; } ) ;
2023-12-02 20:11:06 +01:00
} ) ;
2023-07-20 19:32:15 +02:00
2023-10-15 18:42:23 +02:00
// Re-add pagination not working in popout
printGroupMembers ( ) ;
2023-07-20 19:32:15 +02:00
} else {
2023-12-02 20:11:06 +01:00
console . debug ( 'saw existing popout, removing' ) ;
2023-12-11 15:23:21 +01:00
$ ( '#groupMemberListPopout' ) . fadeOut ( animation _duration , ( ) => { $ ( '#groupMemberListPopout' ) . remove ( ) ; } ) ;
2023-07-20 19:32:15 +02:00
}
}
jQuery ( ( ) => {
2023-12-02 19:04:51 +01:00
$ ( document ) . on ( 'click' , '.group_select' , function ( ) {
const groupId = $ ( this ) . data ( 'id' ) ;
2023-08-19 20:44:15 +02:00
openGroupById ( groupId ) ;
} ) ;
2023-12-02 19:04:51 +01:00
$ ( '#rm_group_filter' ) . on ( 'input' , filterGroupMembers ) ;
$ ( '#rm_group_submit' ) . on ( 'click' , createGroup ) ;
$ ( '#rm_group_scenario' ) . on ( 'click' , setScenarioOverride ) ;
$ ( '#rm_group_automode' ) . on ( 'input' , function ( ) {
const value = $ ( this ) . prop ( 'checked' ) ;
2023-07-20 19:32:15 +02:00
is _group _automode _enabled = value ;
eventSource . once ( event _types . GENERATION _STOPPED , stopAutoModeGeneration ) ;
} ) ;
2023-12-02 19:04:51 +01:00
$ ( '#rm_group_hidemutedsprites' ) . on ( 'input' , function ( ) {
const value = $ ( this ) . prop ( 'checked' ) ;
2023-11-21 23:48:35 +01:00
hideMutedSprites = value ;
onHideMutedSpritesClick ( value ) ;
} ) ;
2023-12-02 19:04:51 +01:00
$ ( '#send_textarea' ) . on ( 'keyup' , onSendTextareaInput ) ;
$ ( '#groupCurrentMemberPopoutButton' ) . on ( 'click' , doCurMemberListPopout ) ;
2023-12-02 20:11:06 +01:00
$ ( '#rm_group_chat_name' ) . on ( 'input' , onGroupNameInput ) ;
2023-12-02 19:04:51 +01:00
$ ( '#rm_group_delete' ) . off ( ) . on ( 'click' , onDeleteGroupClick ) ;
$ ( '#group_favorite_button' ) . on ( 'click' , onFavoriteGroupClick ) ;
$ ( '#rm_group_allow_self_responses' ) . on ( 'input' , onGroupSelfResponsesClick ) ;
$ ( '#rm_group_activation_strategy' ) . on ( 'change' , onGroupActivationStrategyInput ) ;
$ ( '#rm_group_generation_mode' ) . on ( 'change' , onGroupGenerationModeInput ) ;
2023-12-16 01:52:48 +01:00
$ ( '#rm_group_automode_delay' ) . on ( 'input' , onGroupAutoModeDelayInput ) ;
2023-12-02 19:04:51 +01:00
$ ( '#group_avatar_button' ) . on ( 'input' , uploadGroupAvatar ) ;
$ ( '#rm_group_restore_avatar' ) . on ( 'click' , restoreGroupAvatar ) ;
$ ( document ) . on ( 'click' , '.group_member .right_menu_button' , onGroupActionClick ) ;
2023-07-20 19:32:15 +02:00
} ) ;