mirror of
https://github.com/SilenceLurker/ST-Amily2-Chat-Optimisation.git
synced 2026-06-06 15:55:50 +00:00
273 lines
9.8 KiB
JavaScript
273 lines
9.8 KiB
JavaScript
import { characters, saveCharacterDebounced, this_chid, getCharacters, getRequestHeaders } from "/script.js";
|
|
import { getContext } from "/scripts/extensions.js";
|
|
import { extensionName } from "../../utils/settings.js";
|
|
|
|
async function saveCharacterById(chid) {
|
|
let currentChid = undefined;
|
|
|
|
|
|
try {
|
|
const context = getContext();
|
|
if (context) currentChid = context.characterId;
|
|
} catch (e) {}
|
|
|
|
|
|
if (currentChid === undefined) currentChid = this_chid;
|
|
|
|
|
|
if (currentChid === undefined && typeof window !== 'undefined' && window.this_chid !== undefined) {
|
|
currentChid = window.this_chid;
|
|
}
|
|
|
|
|
|
if (currentChid === undefined && typeof $ !== 'undefined') {
|
|
|
|
const selected = $('.character_select.selected, .character-list-item.selected');
|
|
if (selected.length) {
|
|
currentChid = selected.attr('chid');
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if (typeof saveCharacterDebounced === 'function') {
|
|
|
|
|
|
|
|
if (currentChid === undefined || chid == currentChid) {
|
|
saveCharacterDebounced();
|
|
console.log(`[Amily2 CharAPI] Triggered saveCharacterDebounced for character ${chid} (Detected: ${currentChid})`);
|
|
return { success: true };
|
|
}
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
const formData = new FormData();
|
|
formData.append('avatar_url', char.avatar);
|
|
formData.append('ch_name', char.name);
|
|
formData.append('description', char.description || '');
|
|
formData.append('personality', char.personality || '');
|
|
formData.append('scenario', char.scenario || '');
|
|
formData.append('first_mes', char.first_mes || '');
|
|
formData.append('mes_example', char.mes_example || '');
|
|
formData.append('creator', char.creator || '');
|
|
formData.append('creator_notes', char.creator_notes || '');
|
|
formData.append('tags', Array.isArray(char.tags) ? char.tags.join(',') : (char.tags || ''));
|
|
formData.append('talkativeness', char.talkativeness || '0.5');
|
|
formData.append('fav', char.fav || 'false');
|
|
|
|
if (char.data) {
|
|
formData.append('extensions', JSON.stringify(char.data));
|
|
}
|
|
|
|
|
|
if (char.data && Array.isArray(char.data.alternate_greetings)) {
|
|
for (const value of char.data.alternate_greetings) {
|
|
formData.append('alternate_greetings', value);
|
|
}
|
|
}
|
|
|
|
const response = await fetch('/api/characters/edit', {
|
|
method: 'POST',
|
|
headers: getRequestHeaders({ omitContentType: true }),
|
|
body: formData
|
|
});
|
|
|
|
if (!response.ok) {
|
|
const errorText = await response.text();
|
|
console.error(`[Amily2 CharAPI] Failed to save character ${chid}:`, response.statusText, errorText);
|
|
return { success: false, message: `Save failed: ${response.statusText}` };
|
|
} else {
|
|
console.log(`[Amily2 CharAPI] Successfully saved character ${chid} (Background)`);
|
|
return { success: true };
|
|
}
|
|
} catch (e) {
|
|
console.error(`[Amily2 CharAPI] Error saving character ${chid}:`, e);
|
|
return { success: false, message: `Save error: ${e.message}` };
|
|
}
|
|
}
|
|
|
|
export function getCharacter(chid = this_chid) {
|
|
if (chid === undefined || chid < 0 || !characters[chid]) {
|
|
console.warn(`[Amily2 CharAPI] Invalid character ID: ${chid}`);
|
|
return null;
|
|
}
|
|
return characters[chid];
|
|
}
|
|
|
|
export async function updateCharacter(chid, updates) {
|
|
const char = getCharacter(chid);
|
|
if (!char) return false;
|
|
|
|
let changed = false;
|
|
const fields = ['name', 'description', 'personality', 'scenario', 'first_mes', 'mes_example'];
|
|
|
|
fields.forEach(field => {
|
|
if (updates[field] !== undefined && char[field] !== updates[field]) {
|
|
char[field] = updates[field];
|
|
changed = true;
|
|
}
|
|
});
|
|
|
|
if (changed) {
|
|
const success = await saveCharacterById(chid);
|
|
if (success) {
|
|
console.log(`[Amily2 CharAPI] Updated character ${chid}:`, Object.keys(updates));
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
export function getFirstMessages(chid) {
|
|
const char = getCharacter(chid);
|
|
if (!char) return [];
|
|
|
|
const messages = [char.first_mes];
|
|
if (char.data && Array.isArray(char.data.alternate_greetings)) {
|
|
messages.push(...char.data.alternate_greetings);
|
|
}
|
|
return messages;
|
|
}
|
|
|
|
export async function addFirstMessage(chid, message) {
|
|
const char = getCharacter(chid);
|
|
if (!char) return false;
|
|
|
|
if (!char.data) char.data = {};
|
|
if (!Array.isArray(char.data.alternate_greetings)) {
|
|
char.data.alternate_greetings = [];
|
|
}
|
|
|
|
char.data.alternate_greetings.push(message);
|
|
const success = await saveCharacterById(chid);
|
|
if (success) {
|
|
console.log(`[Amily2 CharAPI] Added alternate greeting to character ${chid}`);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
export async function updateFirstMessage(chid, index, message) {
|
|
const char = getCharacter(chid);
|
|
if (!char) return false;
|
|
|
|
if (index === 0) {
|
|
char.first_mes = message;
|
|
} else {
|
|
const altIndex = index - 1;
|
|
if (char.data && Array.isArray(char.data.alternate_greetings) && char.data.alternate_greetings[altIndex] !== undefined) {
|
|
char.data.alternate_greetings[altIndex] = message;
|
|
} else {
|
|
console.warn(`[Amily2 CharAPI] Alternate greeting index out of bounds: ${altIndex}`);
|
|
return false;
|
|
}
|
|
}
|
|
const success = await saveCharacterById(chid);
|
|
if (success) {
|
|
console.log(`[Amily2 CharAPI] Updated greeting ${index} for character ${chid}`);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
export async function removeFirstMessage(chid, index) {
|
|
const char = getCharacter(chid);
|
|
if (!char) return false;
|
|
|
|
if (index === 0) {
|
|
console.warn(`[Amily2 CharAPI] Cannot remove main greeting, clearing instead.`);
|
|
char.first_mes = "";
|
|
} else {
|
|
const altIndex = index - 1;
|
|
if (char.data && Array.isArray(char.data.alternate_greetings) && char.data.alternate_greetings[altIndex] !== undefined) {
|
|
char.data.alternate_greetings.splice(altIndex, 1);
|
|
} else {
|
|
console.warn(`[Amily2 CharAPI] Alternate greeting index out of bounds: ${altIndex}`);
|
|
return false;
|
|
}
|
|
}
|
|
const success = await saveCharacterById(chid);
|
|
if (success) {
|
|
console.log(`[Amily2 CharAPI] Removed greeting ${index} for character ${chid}`);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
export async function createNewCharacter(name) {
|
|
try {
|
|
const formData = new FormData();
|
|
formData.append('ch_name', name);
|
|
formData.append('description', '');
|
|
formData.append('personality', '');
|
|
formData.append('scenario', '');
|
|
formData.append('first_mes', 'Hello!');
|
|
formData.append('mes_example', '');
|
|
formData.append('creator', 'Amily2-AutoChar');
|
|
formData.append('creator_notes', 'Character created automatically by Amily2 AutoChar Card.');
|
|
formData.append('tags', '');
|
|
formData.append('character_version', '1.0');
|
|
formData.append('post_history_instructions', '');
|
|
formData.append('system_prompt', '');
|
|
formData.append('talkativeness', '0.5');
|
|
formData.append('extensions', '{}');
|
|
formData.append('fav', 'false');
|
|
|
|
formData.append('world', '');
|
|
formData.append('depth_prompt_prompt', '');
|
|
formData.append('depth_prompt_depth', '4');
|
|
formData.append('depth_prompt_role', 'system');
|
|
|
|
try {
|
|
const res = await fetch(`scripts/extensions/third-party/${extensionName}/core/auto-char-card/Amily.png`);
|
|
if (res.ok) {
|
|
const blob = await res.blob();
|
|
formData.append('avatar', blob, 'default.png');
|
|
} else {
|
|
throw new Error('Failed to fetch default avatar');
|
|
}
|
|
} catch (e) {
|
|
console.warn("[Amily2 CharAPI] Failed to load default avatar, using fallback 1x1 PNG.", e);
|
|
const base64Png = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==";
|
|
const byteCharacters = atob(base64Png);
|
|
const byteNumbers = new Array(byteCharacters.length);
|
|
for (let i = 0; i < byteCharacters.length; i++) {
|
|
byteNumbers[i] = byteCharacters.charCodeAt(i);
|
|
}
|
|
const byteArray = new Uint8Array(byteNumbers);
|
|
const blob = new Blob([byteArray], { type: 'image/png' });
|
|
formData.append('avatar', blob, 'default.png');
|
|
}
|
|
|
|
const response = await fetch('/api/characters/create', {
|
|
method: 'POST',
|
|
headers: getRequestHeaders({ omitContentType: true }),
|
|
body: formData,
|
|
});
|
|
|
|
if (response.ok) {
|
|
const avatarId = await response.text();
|
|
console.log(`[Amily2 CharAPI] Created character: ${name}, Avatar ID: ${avatarId}`);
|
|
|
|
await getCharacters();
|
|
|
|
const newChid = characters.findIndex(c => c.avatar === avatarId);
|
|
if (newChid !== -1) {
|
|
return newChid;
|
|
}
|
|
|
|
return -2;
|
|
} else {
|
|
console.error(`[Amily2 CharAPI] Failed to create character: ${response.statusText}`);
|
|
return -1;
|
|
}
|
|
} catch (error) {
|
|
console.error(`[Amily2 CharAPI] Error creating character:`, error);
|
|
return -1;
|
|
}
|
|
}
|