mirror of
https://github.com/SilenceLurker/ST-Amily2-Chat-Optimisation.git
synced 2026-06-06 14:45:51 +00:00
200 lines
8.8 KiB
JavaScript
200 lines
8.8 KiB
JavaScript
import { loadWorldInfo, createNewWorldInfo, saveWorldInfo, world_names, createWorldInfoEntry } from "/scripts/world-info.js";
|
|
import { characters, eventSource, event_types } from "/script.js";
|
|
import { getContext } from "/scripts/extensions.js";
|
|
|
|
|
|
export function isTavernHelperAvailable() {
|
|
return typeof window.TavernHelper !== 'undefined' &&
|
|
window.TavernHelper !== null &&
|
|
typeof window.TavernHelper.getLorebooks === 'function';
|
|
}
|
|
|
|
|
|
export async function safeLorebooks() {
|
|
try {
|
|
if (isTavernHelperAvailable()) {
|
|
return await window.TavernHelper.getLorebooks();
|
|
}
|
|
return [...world_names];
|
|
} catch (error) {
|
|
console.error('[Amily2-兼容性] 获取世界书列表失败:', error);
|
|
return [...world_names];
|
|
}
|
|
}
|
|
|
|
|
|
export async function safeCharLorebooks(options = { type: 'all' }) {
|
|
try {
|
|
if (isTavernHelperAvailable()) {
|
|
return await window.TavernHelper.getCharLorebooks(options);
|
|
}
|
|
const context = getContext();
|
|
const character = characters[context.characterId];
|
|
const primary = character?.data?.extensions?.world;
|
|
return { primary: primary || null, additional: [] };
|
|
} catch (error) {
|
|
console.error('[Amily2-兼容性] 获取角色世界书失败:', error);
|
|
const context = getContext();
|
|
const character = characters[context.characterId];
|
|
const primary = character?.data?.extensions?.world;
|
|
return { primary: primary || null, additional: [] };
|
|
}
|
|
}
|
|
|
|
export async function safeLorebookEntries(bookName) {
|
|
try {
|
|
if (isTavernHelperAvailable()) {
|
|
return await window.TavernHelper.getLorebookEntries(bookName);
|
|
}
|
|
const bookData = await loadWorldInfo(bookName);
|
|
if (!bookData || !bookData.entries) return [];
|
|
return Object.entries(bookData.entries).map(([uid, entry]) => ({
|
|
uid: parseInt(uid),
|
|
comment: entry.comment || '无标题条目',
|
|
content: entry.content || '',
|
|
key: entry.key || [],
|
|
enabled: !entry.disable,
|
|
constant: entry.constant || false,
|
|
position: entry.position || 4,
|
|
depth: entry.depth || 998,
|
|
}));
|
|
} catch (error) {
|
|
console.error(`[Amily2-兼容性] 获取世界书 ${bookName} 条目失败:`, error);
|
|
return [];
|
|
}
|
|
}
|
|
|
|
|
|
export async function safeUpdateLorebookEntries(bookName, entries) {
|
|
try {
|
|
if (isTavernHelperAvailable()) {
|
|
await window.TavernHelper.setLorebookEntries(bookName, entries);
|
|
return true;
|
|
}
|
|
|
|
const bookData = await loadWorldInfo(bookName);
|
|
if (!bookData) return false;
|
|
for (const entryUpdate of entries) {
|
|
const existingEntry = bookData.entries[entryUpdate.uid];
|
|
if (existingEntry) {
|
|
if (entryUpdate.content !== undefined) existingEntry.content = entryUpdate.content;
|
|
if (entryUpdate.enabled !== undefined) existingEntry.disable = !entryUpdate.enabled;
|
|
}
|
|
}
|
|
await saveWorldInfo(bookName, bookData, true);
|
|
return true;
|
|
} catch (error) {
|
|
console.error(`[Amily2-兼容性] 更新世界书条目失败:`, error);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
export async function compatibleWriteToLorebook(targetLorebookName, entryComment, contentUpdateCallback, options = {}) {
|
|
console.log('[Amily2-兼容性] compatibleWriteToLorebook 接收到的选项:', options);
|
|
|
|
if (isTavernHelperAvailable()) {
|
|
try {
|
|
if (!world_names.includes(targetLorebookName)) {
|
|
await createNewWorldInfo(targetLorebookName);
|
|
if (Array.isArray(world_names) && !world_names.includes(targetLorebookName)) {
|
|
world_names.push(targetLorebookName);
|
|
world_names.sort();
|
|
}
|
|
if (eventSource && typeof eventSource.emit === "function" && event_types.CHARACTER_PAGE_LOADED) {
|
|
eventSource.emit(event_types.CHARACTER_PAGE_LOADED);
|
|
}
|
|
console.log(`[Amily2-兼容性] (混合模式) 已创建新世界书: ${targetLorebookName}`);
|
|
}
|
|
|
|
const entries = await safeLorebookEntries(targetLorebookName);
|
|
const existingEntry = entries.find((e) => e.comment === entryComment && !e.disable);
|
|
|
|
if (existingEntry) {
|
|
const newContent = contentUpdateCallback(existingEntry.content);
|
|
await safeUpdateLorebookEntries(targetLorebookName, [{ uid: existingEntry.uid, content: newContent }]);
|
|
} else {
|
|
const newContent = contentUpdateCallback(null);
|
|
const { keys = [], isConstant = false, insertion_position, depth: insertion_depth } = options;
|
|
const positionMap = { 'before_char': 0, 'after_char': 1, 'before_an': 2, 'after_an': 3, 'at_depth': 4 };
|
|
|
|
const newEntryData = {
|
|
comment: entryComment, content: newContent, key: keys,
|
|
constant: isConstant, position: positionMap[insertion_position] ?? 4,
|
|
depth: parseInt(insertion_depth) || 998, enabled: true,
|
|
};
|
|
|
|
await window.TavernHelper.createLorebookEntries(targetLorebookName, [newEntryData]);
|
|
|
|
const bookData = await loadWorldInfo(targetLorebookName);
|
|
const createdEntry = Object.values(bookData.entries).find(e => e.comment === entryComment);
|
|
if (createdEntry) {
|
|
createdEntry.constant = isConstant;
|
|
createdEntry.position = positionMap[insertion_position] ?? 4;
|
|
createdEntry.depth = parseInt(insertion_depth) || 998;
|
|
await saveWorldInfo(targetLorebookName, bookData, true);
|
|
console.log(`[Amily2-兼容性] (混合模式) 已修正条目激活状态、位置和深度。`);
|
|
}
|
|
}
|
|
console.log(`[Amily2-兼容性] (混合模式) 成功写入条目 "${entryComment}"。`);
|
|
return true;
|
|
} catch (error) {
|
|
console.error(`[Amily2-兼容性] (混合模式) 写入失败:`, error);
|
|
toastr.error(`写入世界书失败: ${error.message}`, "Amily2号-兼容性模块");
|
|
return false;
|
|
}
|
|
} else {
|
|
console.warn('[Amily2-兼容性] TavernHelper 不可用,回退到传统写入逻辑。');
|
|
try {
|
|
if (!world_names.includes(targetLorebookName)) {
|
|
await createNewWorldInfo(targetLorebookName);
|
|
console.log(`[Amily2-兼容性] (传统模式) 已创建新世界书: ${targetLorebookName}`);
|
|
}
|
|
|
|
const bookData = await loadWorldInfo(targetLorebookName);
|
|
if (!bookData) throw new Error(`无法加载世界书《${targetLorebookName}》`);
|
|
|
|
const existingEntry = Object.values(bookData.entries).find(e => e.comment === entryComment && !e.disable);
|
|
|
|
if (existingEntry) {
|
|
existingEntry.content = contentUpdateCallback(existingEntry.content);
|
|
} else {
|
|
const newContent = contentUpdateCallback(null);
|
|
const { keys = [], isConstant = false, insertion_position, depth: insertion_depth } = options;
|
|
const positionMap = { 'before_char': 0, 'after_char': 1, 'before_an': 2, 'after_an': 3, 'at_depth': 4 };
|
|
|
|
const newEntry = createWorldInfoEntry(targetLorebookName, bookData);
|
|
Object.assign(newEntry, {
|
|
comment: entryComment, content: newContent, key: keys,
|
|
constant: isConstant, position: positionMap[insertion_position] ?? 4,
|
|
depth: parseInt(insertion_depth) || 998, disable: false,
|
|
});
|
|
}
|
|
|
|
await saveWorldInfo(targetLorebookName, bookData, true);
|
|
console.log(`[Amily2-兼容性] (传统模式) 成功写入条目 "${entryComment}"。`);
|
|
return true;
|
|
} catch (error) {
|
|
console.error(`[Amily2-兼容性] (传统模式) 写入失败:`, error);
|
|
toastr.error(`传统模式写入世界书失败: ${error.message}`, "Amily2号-兼容性模块");
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
export async function safeTriggerSlash(command) {
|
|
if (isTavernHelperAvailable() && typeof window.TavernHelper.triggerSlash === 'function') {
|
|
try {
|
|
return await window.TavernHelper.triggerSlash(command);
|
|
} catch (error) {
|
|
console.error(`[Amily2-兼容性] TavernHelper.triggerSlash 执行失败:`, error);
|
|
throw error;
|
|
}
|
|
} else {
|
|
const errorMsg = 'TavernHelper 或 triggerSlash 方法不可用';
|
|
console.error(`[Amily2-兼容性] ${errorMsg}`);
|
|
throw new Error(errorMsg);
|
|
}
|
|
}
|