mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-06-05 21:59:27 +02:00
Update group chats internal format to support multiple chats per group and duplicate name handling
This commit is contained in:
@ -732,7 +732,6 @@ function printCharacters() {
|
||||
}
|
||||
|
||||
async function getCharacters() {
|
||||
await getGroups();
|
||||
var response = await fetch("/getcharacters", {
|
||||
method: "POST",
|
||||
headers: getRequestHeaders(),
|
||||
@ -752,6 +751,7 @@ async function getCharacters() {
|
||||
if (this_chid != undefined && this_chid != "invalid-safety-id") {
|
||||
$("#avatar_url_pole").val(characters[this_chid].avatar);
|
||||
}
|
||||
await getGroups();
|
||||
printCharacters();
|
||||
}
|
||||
}
|
||||
@ -1212,12 +1212,20 @@ function cleanGroupMessage(getMessage) {
|
||||
|
||||
if (group && Array.isArray(group.members) && group.members) {
|
||||
for (let member of group.members) {
|
||||
// Skip current speaker.
|
||||
if (member === name2) {
|
||||
const character = characters.find(x => x.avatar == member);
|
||||
|
||||
if (!character) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const indexOfMember = getMessage.indexOf(member + ":");
|
||||
const name = character.name;
|
||||
|
||||
// Skip current speaker.
|
||||
if (name === name2) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const indexOfMember = getMessage.indexOf(`${name}:`);
|
||||
if (indexOfMember != -1) {
|
||||
getMessage = getMessage.substr(0, indexOfMember);
|
||||
}
|
||||
@ -2399,6 +2407,7 @@ function saveReply(type, getMessage, this_mes_is_name, title) {
|
||||
}
|
||||
chat[chat.length - 1]['is_name'] = true;
|
||||
chat[chat.length - 1]['force_avatar'] = avatarImg;
|
||||
chat[chat.length - 1]['original_avatar'] = characters[this_chid].avatar;
|
||||
chat[chat.length - 1]['extra']['gen_id'] = group_generation_id;
|
||||
}
|
||||
|
||||
|
@ -115,19 +115,20 @@ async function regenerateGroup() {
|
||||
generateGroupWrapper();
|
||||
}
|
||||
|
||||
export async function getGroupChat(id) {
|
||||
export async function getGroupChat(groupId) {
|
||||
const group = groups.find((x) => x.id === groupId);
|
||||
const chat_id = group.chat_id;
|
||||
const response = await fetch("/getgroupchat", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"X-CSRF-Token": token,
|
||||
},
|
||||
body: JSON.stringify({ id: id }),
|
||||
body: JSON.stringify({ id: chat_id }),
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
const group = groups.find((x) => x.id === id);
|
||||
if (Array.isArray(data) && data.length) {
|
||||
data[0].is_group = true;
|
||||
for (let key of data) {
|
||||
@ -137,8 +138,8 @@ export async function getGroupChat(id) {
|
||||
} else {
|
||||
sendSystemMessage(system_message_types.GROUP);
|
||||
if (group && Array.isArray(group.members)) {
|
||||
for (let name of group.members) {
|
||||
const character = characters.find((x) => x.name === name);
|
||||
for (let member of group.members) {
|
||||
const character = characters.find(x => x.avatar === member || x.name === member);
|
||||
|
||||
if (!character) {
|
||||
continue;
|
||||
@ -156,7 +157,7 @@ export async function getGroupChat(id) {
|
||||
updateChatMetadata(metadata, true);
|
||||
}
|
||||
|
||||
await saveGroupChat(id, true);
|
||||
await saveGroupChat(groupId, true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -182,18 +183,20 @@ function resetSelectedGroup() {
|
||||
is_group_generating = false;
|
||||
}
|
||||
|
||||
async function saveGroupChat(id, shouldSaveGroup) {
|
||||
async function saveGroupChat(groupId, shouldSaveGroup) {
|
||||
const group = groups.find(x => x.id == groupId);
|
||||
const chat_id = group.chat_id;
|
||||
const response = await fetch("/savegroupchat", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"X-CSRF-Token": token,
|
||||
},
|
||||
body: JSON.stringify({ id: id, chat: [...chat] }),
|
||||
body: JSON.stringify({ id: chat_id, chat: [...chat] }),
|
||||
});
|
||||
|
||||
if (shouldSaveGroup && response.ok) {
|
||||
await editGroup(id);
|
||||
await editGroup(groupId);
|
||||
}
|
||||
}
|
||||
|
||||
@ -209,10 +212,21 @@ async function getGroups() {
|
||||
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) {
|
||||
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)
|
||||
.filter(onlyUnique)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function printGroups() {
|
||||
for (let group of groups) {
|
||||
const template = $("#group_list_template .group_select").clone();
|
||||
@ -248,7 +262,7 @@ function getGroupAvatar(group) {
|
||||
const memberAvatars = [];
|
||||
if (group && Array.isArray(group.members) && group.members.length) {
|
||||
for (const member of group.members) {
|
||||
const charIndex = characters.findIndex((x) => x.name === member);
|
||||
const charIndex = characters.findIndex(x => x.avatar === member);
|
||||
if (charIndex !== -1 && characters[charIndex].avatar !== "none") {
|
||||
const avatar = getThumbnailUrl('avatar', characters[charIndex].avatar);
|
||||
memberAvatars.push(avatar);
|
||||
@ -466,34 +480,49 @@ function getLastMessageGenerationId() {
|
||||
|
||||
function activateImpersonate(members) {
|
||||
const randomIndex = Math.floor(Math.random() * members.length);
|
||||
const activatedNames = [members[randomIndex]];
|
||||
const memberIds = activatedNames
|
||||
.map((x) => characters.findIndex((y) => y.name === x))
|
||||
const activatedMembers = [members[randomIndex]];
|
||||
const memberIds = activatedMembers
|
||||
.map((x) => characters.findIndex((y) => y.avatar === x))
|
||||
.filter((x) => x !== -1);
|
||||
return memberIds;
|
||||
}
|
||||
|
||||
function activateSwipe(members) {
|
||||
const name = chat[chat.length - 1].name;
|
||||
const activatedNames = members.includes(name) ? [name] : [];
|
||||
let activatedNames = [];
|
||||
|
||||
// pre-update group chat swipe
|
||||
if (!chat[chat.length -1].original_avatar) {
|
||||
const matches = characters.filter(x => x.name == chat[chat.length - 1].name);
|
||||
|
||||
for (const match of matches) {
|
||||
if (members.includes(match.avatar)) {
|
||||
activatedNames.push(match.avatar);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
activatedNames.push(chat[chat.length -1].original_avatar);
|
||||
}
|
||||
|
||||
const memberIds = activatedNames
|
||||
.map((x) => characters.findIndex((y) => y.name === x))
|
||||
.map((x) => characters.findIndex((y) => y.avatar === x))
|
||||
.filter((x) => x !== -1);
|
||||
return memberIds;
|
||||
}
|
||||
|
||||
function activateListOrder(members) {
|
||||
let activatedNames = members.filter(onlyUnique);
|
||||
let activatedMembers = members.filter(onlyUnique);
|
||||
|
||||
// map to character ids
|
||||
const memberIds = activatedNames
|
||||
.map((x) => characters.findIndex((y) => y.name === x))
|
||||
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 activatedNames = [];
|
||||
let activatedMembers = [];
|
||||
|
||||
// prevents the same character from speaking twice
|
||||
let bannedUser = !isUserInput && lastMessage && !lastMessage.is_user && lastMessage.name;
|
||||
@ -507,12 +536,14 @@ function activateNaturalOrder(members, input, lastMessage, allowSelfResponses, i
|
||||
if (input && input.length) {
|
||||
for (let inputWord of extractAllWords(input)) {
|
||||
for (let member of members) {
|
||||
if (member === bannedUser) {
|
||||
const character = characters.find(x => x.avatar === member)
|
||||
|
||||
if (!character || character.name === bannedUser) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (extractAllWords(member).includes(inputWord)) {
|
||||
activatedNames.push(member);
|
||||
if (extractAllWords(character.name).includes(inputWord)) {
|
||||
activatedMembers.push(member);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -522,13 +553,9 @@ function activateNaturalOrder(members, input, lastMessage, allowSelfResponses, i
|
||||
// activation by talkativeness (in shuffled order, except banned)
|
||||
const shuffledMembers = shuffle([...members]);
|
||||
for (let member of shuffledMembers) {
|
||||
if (member === bannedUser) {
|
||||
continue;
|
||||
}
|
||||
const character = characters.find((x) => x.avatar === member);
|
||||
|
||||
const character = characters.find((x) => x.name === member);
|
||||
|
||||
if (!character) {
|
||||
if (!character || character.name === bannedUser) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -538,22 +565,28 @@ function activateNaturalOrder(members, input, lastMessage, allowSelfResponses, i
|
||||
? talkativeness_default
|
||||
: talkativeness;
|
||||
if (talkativeness >= rollValue) {
|
||||
activatedNames.push(member);
|
||||
activatedMembers.push(member);
|
||||
}
|
||||
}
|
||||
|
||||
// pick 1 at random if no one was activated
|
||||
if (activatedNames.length === 0) {
|
||||
while (activatedMembers.length === 0) {
|
||||
const randomIndex = Math.floor(Math.random() * members.length);
|
||||
activatedNames.push(members[randomIndex]);
|
||||
const character = characters.find((x) => x.avatar === members[randomIndex]);
|
||||
|
||||
if (!character) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// de-duplicate array of names
|
||||
activatedNames = activatedNames.filter(onlyUnique);
|
||||
activatedMembers.push(members[randomIndex]);
|
||||
}
|
||||
|
||||
// de-duplicate array of character avatars
|
||||
activatedMembers = activatedMembers.filter(onlyUnique);
|
||||
|
||||
// map to character ids
|
||||
const memberIds = activatedNames
|
||||
.map((x) => characters.findIndex((y) => y.name === x))
|
||||
const memberIds = activatedMembers
|
||||
.map((x) => characters.findIndex((y) => y.avatar === x))
|
||||
.filter((x) => x !== -1);
|
||||
return memberIds;
|
||||
}
|
||||
@ -706,28 +739,28 @@ async function reorderGroupMember(chat_id, groupMember, direction) {
|
||||
}
|
||||
}
|
||||
|
||||
function select_group_chats(chat_id, skipAnimation) {
|
||||
const group = chat_id && groups.find((x) => x.id == chat_id);
|
||||
function select_group_chats(groupId, skipAnimation) {
|
||||
const group = groupId && groups.find((x) => x.id == groupId);
|
||||
const groupName = group?.name ?? "";
|
||||
group_rm_panel_mode = !!group ? 'edit' : 'create';
|
||||
$("#rm_group_chat_name").val(groupName);
|
||||
$("#rm_group_chat_name").off();
|
||||
$("#rm_group_chat_name").on("input", async function () {
|
||||
if (chat_id) {
|
||||
let _thisGroup = groups.find((x) => x.id == chat_id);
|
||||
if (groupId) {
|
||||
let _thisGroup = groups.find((x) => x.id == groupId);
|
||||
_thisGroup.name = $(this).val();
|
||||
$("#rm_button_selected_ch").children("h2").text(_thisGroup.name);
|
||||
await editGroup(chat_id);
|
||||
await editGroup(groupId);
|
||||
}
|
||||
});
|
||||
$("#rm_group_filter").val("").trigger("input");
|
||||
|
||||
$('input[name="rm_group_activation_strategy"]').off();
|
||||
$('input[name="rm_group_activation_strategy"]').on("input", async function (e) {
|
||||
if (chat_id) {
|
||||
let _thisGroup = groups.find((x) => x.id == chat_id);
|
||||
if (groupId) {
|
||||
let _thisGroup = groups.find((x) => x.id == groupId);
|
||||
_thisGroup.activation_strategy = Number(e.target.value);
|
||||
await editGroup(chat_id);
|
||||
await editGroup(groupId);
|
||||
}
|
||||
});
|
||||
const replyStrategy = Number(group?.activation_strategy ?? group_activation_strategy.NATURAL);
|
||||
@ -746,7 +779,7 @@ function select_group_chats(chat_id, skipAnimation) {
|
||||
? getThumbnailUrl('avatar', character.avatar)
|
||||
: default_avatar;
|
||||
const template = $("#group_member_template .group_member").clone();
|
||||
template.data("id", character.name);
|
||||
template.data("id", character.avatar);
|
||||
template.find(".avatar img").attr("src", avatar);
|
||||
template.find(".ch_name").text(character.name);
|
||||
template.attr("chid", characters.indexOf(character));
|
||||
@ -754,9 +787,9 @@ function select_group_chats(chat_id, skipAnimation) {
|
||||
if (
|
||||
group &&
|
||||
Array.isArray(group.members) &&
|
||||
group.members.includes(character.name)
|
||||
group.members.includes(character.avatar)
|
||||
) {
|
||||
template.css({ 'order': group.members.indexOf(character.name) });
|
||||
template.css({ 'order': group.members.indexOf(character.avatar) });
|
||||
$("#rm_group_members").append(template);
|
||||
} else {
|
||||
$("#rm_group_add_members").append(template);
|
||||
@ -770,7 +803,7 @@ function select_group_chats(chat_id, skipAnimation) {
|
||||
$("#rm_group_allow_self_responses").prop("checked", group && group.allow_self_responses);
|
||||
|
||||
// bottom buttons
|
||||
if (chat_id) {
|
||||
if (groupId) {
|
||||
$("#rm_group_submit").hide();
|
||||
$("#rm_group_delete").show();
|
||||
} else {
|
||||
@ -785,7 +818,7 @@ function select_group_chats(chat_id, skipAnimation) {
|
||||
return;
|
||||
}
|
||||
|
||||
$("#dialogue_popup").data("group_id", chat_id);
|
||||
$("#dialogue_popup").data("group_id", groupId);
|
||||
callPopup("<h3>Delete the group?</h3>", "del_group");
|
||||
});
|
||||
|
||||
@ -795,19 +828,19 @@ function select_group_chats(chat_id, skipAnimation) {
|
||||
$("#group_favorite_button").on('click', async function () {
|
||||
updateFavButtonState(!fav_grp_checked);
|
||||
if (group) {
|
||||
let _thisGroup = groups.find((x) => x.id == chat_id);
|
||||
let _thisGroup = groups.find((x) => x.id == groupId);
|
||||
_thisGroup.fav = fav_grp_checked;
|
||||
await editGroup(chat_id);
|
||||
await editGroup(groupId);
|
||||
}
|
||||
});
|
||||
|
||||
$("#rm_group_allow_self_responses").off();
|
||||
$("#rm_group_allow_self_responses").on("input", async function () {
|
||||
if (group) {
|
||||
let _thisGroup = groups.find((x) => x.id == chat_id);
|
||||
let _thisGroup = groups.find((x) => x.id == groupId);
|
||||
const value = $(this).prop("checked");
|
||||
_thisGroup.allow_self_responses = value;
|
||||
await editGroup(chat_id);
|
||||
await editGroup(groupId);
|
||||
}
|
||||
});
|
||||
|
||||
@ -828,15 +861,15 @@ function select_group_chats(chat_id, skipAnimation) {
|
||||
const member = $(this).closest('.group_member');
|
||||
|
||||
if (action == 'remove') {
|
||||
await modifyGroupMember(chat_id, member, true);
|
||||
await modifyGroupMember(groupId, member, true);
|
||||
}
|
||||
|
||||
if (action == 'add') {
|
||||
await modifyGroupMember(chat_id, member, false);
|
||||
await modifyGroupMember(groupId, member, false);
|
||||
}
|
||||
|
||||
if (action == 'up' || action == 'down') {
|
||||
await reorderGroupMember(chat_id, member, action);
|
||||
await reorderGroupMember(groupId, member, action);
|
||||
}
|
||||
|
||||
sortCharactersList("#rm_group_add_members .group_member");
|
||||
@ -852,24 +885,24 @@ function updateFavButtonState(state) {
|
||||
|
||||
$(document).ready(() => {
|
||||
$(document).on("click", ".group_select", async function () {
|
||||
const id = $(this).data("id");
|
||||
const groupId = $(this).data("id");
|
||||
|
||||
if (!is_send_press && !is_group_generating) {
|
||||
if (selected_group !== id) {
|
||||
selected_group = id;
|
||||
if (selected_group !== groupId) {
|
||||
selected_group = groupId;
|
||||
setCharacterId(undefined);
|
||||
setCharacterName('');
|
||||
setEditedMessageId(undefined);
|
||||
clearChat();
|
||||
updateChatMetadata({}, true);
|
||||
chat.length = 0;
|
||||
await getGroupChat(id);
|
||||
await getGroupChat(groupId);
|
||||
//to avoid the filter being lit up yellow and left at true while the list of character and group reseted.
|
||||
$("#filter_by_fav").removeClass("fav_on");
|
||||
filterByFav = false;
|
||||
}
|
||||
|
||||
select_group_chats(id);
|
||||
select_group_chats(groupId);
|
||||
}
|
||||
});
|
||||
|
||||
@ -895,13 +928,18 @@ $(document).ready(() => {
|
||||
.map((_, x) => $(x).data("id"))
|
||||
.toArray();
|
||||
|
||||
const memberNames = characters.filter(x => members.includes(x.avatar)).map(x => x.name).join(", ");
|
||||
|
||||
if (!name) {
|
||||
name = `Chat with ${members.join(", ")}`;
|
||||
name = `Chat with ${memberNames}`;
|
||||
}
|
||||
|
||||
// placeholder
|
||||
const avatar_url = 'img/five.png';
|
||||
|
||||
const chatName = humanizedDateTime();
|
||||
const chats = [chatName];
|
||||
|
||||
const createGroupResponse = await fetch("/creategroup", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
@ -916,6 +954,8 @@ $(document).ready(() => {
|
||||
activation_strategy: activation_strategy,
|
||||
chat_metadata: {},
|
||||
fav: fav_grp_checked,
|
||||
chat_id: chatName,
|
||||
chats: chats,
|
||||
}),
|
||||
});
|
||||
|
||||
|
@ -1739,7 +1739,7 @@ app.post('/creategroup', jsonParser, (request, response) => {
|
||||
}
|
||||
|
||||
const id = Date.now();
|
||||
const chatMetadata = {
|
||||
const groupMetadata = {
|
||||
id: id,
|
||||
name: request.body.name ?? 'New Group',
|
||||
members: request.body.members ?? [],
|
||||
@ -1748,16 +1748,18 @@ app.post('/creategroup', jsonParser, (request, response) => {
|
||||
activation_strategy: request.body.activation_strategy ?? 0,
|
||||
chat_metadata: request.body.chat_metadata ?? {},
|
||||
fav: request.body.fav,
|
||||
chat_id: request.body.chat_id ?? id,
|
||||
chats: request.body.chats ?? [id],
|
||||
};
|
||||
const pathToFile = path.join(directories.groups, `${id}.json`);
|
||||
const fileData = JSON.stringify(chatMetadata);
|
||||
const fileData = JSON.stringify(groupMetadata);
|
||||
|
||||
if (!fs.existsSync(directories.groups)) {
|
||||
fs.mkdirSync(directories.groups);
|
||||
}
|
||||
|
||||
fs.writeFileSync(pathToFile, fileData);
|
||||
return response.send(chatMetadata);
|
||||
return response.send(groupMetadata);
|
||||
});
|
||||
|
||||
app.post('/editgroup', jsonParser, (request, response) => {
|
||||
|
Reference in New Issue
Block a user