Files

799 lines
32 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import {
world_names,
loadWorldInfo,
saveWorldInfo,
createNewWorldInfo,
createWorldInfoEntry
} from "/scripts/world-info.js";
let reloadEditor = () => {
console.warn("[Amily助手] reloadEditor 函数不可用,可能是旧版本。已使用空函数代替。");
};
(async () => {
try {
const { reloadEditor: importedReloadEditor } = await import("/scripts/world-info.js");
if (importedReloadEditor) {
reloadEditor = importedReloadEditor;
console.log("[Amily助手] 已成功动态导入 reloadEditor。");
}
} catch (error) {
console.warn("[Amily助手] 动态导入 reloadEditor 失败,将使用空函数。错误信息:", error.message);
}
})();
import {
characters,
eventSource,
event_types,
chat,
reloadCurrentChat,
saveChatConditional,
name1,
name2,
addOneMessage,
messageFormatting,
substituteParamsExtended,
saveCharacterDebounced,
this_chid
} from "/script.js";
import { getContext } from "/scripts/extensions.js";
import { executeSlashCommandsWithOptions } from '/scripts/slash-commands.js';
class AmilyHelper {
// ==================== Chat Message 相关方法 ====================
getChatMessages(range, options = {}) {
const { role = 'all', hide_state = 'all', include_swipes = false, include_swipe = false } = options;
const includeSwipes = include_swipes || include_swipe;
if (!chat || !Array.isArray(chat)) {
throw new Error('聊天数组不可用');
}
let start, end;
const rangeStr = String(range);
if (rangeStr.match(/^(-?\d+)$/)) {
const value = Number(rangeStr);
start = end = value < 0 ? chat.length + value : value;
} else {
const match = rangeStr.match(/^(-?\d+)-(-?\d+)$/);
if (!match) {
throw new Error(`无效的消息范围: ${range}`);
}
const [, s, e] = match;
const startVal = Number(s) < 0 ? chat.length + Number(s) : Number(s);
const endVal = Number(e) < 0 ? chat.length + Number(e) : Number(e);
start = Math.min(startVal, endVal);
end = Math.max(startVal, endVal);
}
if (start < 0 || end >= chat.length || start > end) {
throw new Error(`消息范围超出界限: ${range}`);
}
const getRole = (msg) => {
if (msg.is_system) return 'system';
return msg.is_user ? 'user' : 'assistant';
};
const messages = [];
for (let i = start; i <= end; i++) {
const msg = chat[i];
if (!msg) continue;
const msgRole = getRole(msg);
if (role !== 'all' && msgRole !== role) continue;
if (hide_state !== 'all') {
if ((hide_state === 'hidden') !== msg.is_system) continue;
}
const swipe_id = msg.swipe_id ?? 0;
const swipes = msg.swipes ?? [msg.mes];
const swipes_data = msg.variables ?? [{}];
const swipes_info = msg.swipes_info ?? [msg.extra ?? {}];
if (includeSwipes) {
messages.push({
message_id: i,
name: msg.name,
role: msgRole,
is_hidden: msg.is_system,
swipe_id: swipe_id,
swipes: swipes,
swipes_data: swipes_data,
swipes_info: swipes_info
});
} else {
messages.push({
message_id: i,
name: msg.name,
role: msgRole,
is_hidden: msg.is_system,
message: msg.mes,
data: swipes_data[swipe_id],
extra: swipes_info[swipe_id]
});
}
}
return messages;
}
async setChatMessages(chat_messages, options = {}) {
const { refresh = 'affected' } = options;
if (!Array.isArray(chat_messages)) {
throw new Error('chat_messages 必须是数组');
}
for (const chatMsg of chat_messages) {
const msg = chat[chatMsg.message_id];
if (!msg) continue;
if (chatMsg.name !== undefined) msg.name = chatMsg.name;
if (chatMsg.role !== undefined) msg.is_user = chatMsg.role === 'user';
if (chatMsg.is_hidden !== undefined) msg.is_system = chatMsg.is_hidden;
if (chatMsg.message !== undefined) {
msg.mes = chatMsg.message;
if (msg.swipes) {
msg.swipes[msg.swipe_id ?? 0] = chatMsg.message;
}
}
if (chatMsg.data !== undefined) {
if (!msg.variables) {
msg.variables = Array(msg.swipes?.length ?? 1).fill({});
}
msg.variables[msg.swipe_id ?? 0] = chatMsg.data;
}
if (chatMsg.extra !== undefined) {
if (!msg.swipes_info) {
msg.swipes_info = Array(msg.swipes?.length ?? 1).fill({});
}
msg.extra = chatMsg.extra;
msg.swipes_info[msg.swipe_id ?? 0] = chatMsg.extra;
}
}
await saveChatConditional();
if (refresh === 'all') {
await reloadCurrentChat();
} else if (refresh === 'affected') {
for (const chatMsg of chat_messages) {
const $mes = $(`div.mes[mesid="${chatMsg.message_id}"]`);
if ($mes.length) {
const msg = chat[chatMsg.message_id];
$mes.find('.mes_text').empty().append(
messageFormatting(msg.mes, msg.name, msg.is_system, msg.is_user, chatMsg.message_id)
);
}
}
}
console.log(`[Amily助手] 已修改消息: ${chat_messages.map(m => m.message_id).join(', ')}`);
}
async setChatMessage(field_values, message_id, {
swipe_id = 'current',
refresh = 'display_and_render_current'
} = {}) {
field_values = typeof field_values === 'string' ? { message: field_values } : field_values;
if (typeof swipe_id !== 'number' && swipe_id !== 'current') {
throw new Error(`提供的 swipe_id 无效, 请提供 'current' 或序号, 你提供的是: ${swipe_id}`);
}
if (!['none', 'display_current', 'display_and_render_current', 'all'].includes(refresh)) {
throw new Error(
`提供的 refresh 无效, 请提供 'none', 'display_current', 'display_and_render_current' 或 'all', 你提供的是: ${refresh}`
);
}
const chat_message = chat[message_id];
if (!chat_message) {
console.warn(`[Amily助手] 未找到第 ${message_id} 楼的消息`);
return;
}
const add_swipes_if_required = () => {
if (swipe_id === 'current') {
return false;
}
if (swipe_id == 0 || (chat_message.swipes && swipe_id < chat_message.swipes.length)) {
return true;
}
if (!chat_message.swipes) {
chat_message.swipe_id = 0;
chat_message.swipes = [chat_message.mes];
chat_message.variables = [{}];
}
for (let i = chat_message.swipes.length; i <= swipe_id; ++i) {
chat_message.swipes.push('');
chat_message.variables.push({});
}
return true;
};
const swipe_id_previous_index = chat_message.swipe_id ?? 0;
const swipe_id_to_set_index = swipe_id == 'current' ? swipe_id_previous_index : swipe_id;
const swipe_id_to_use_index = refresh != 'none' ? swipe_id_to_set_index : swipe_id_previous_index;
const message = field_values.message ??
(chat_message.swipes ? chat_message.swipes[swipe_id_to_set_index] : undefined) ??
chat_message.mes;
const update_chat_message = () => {
const message_demacroed = substituteParamsExtended(message);
if (field_values.data) {
if (!chat_message.variables) {
chat_message.variables = [];
}
chat_message.variables[swipe_id_to_set_index] = field_values.data;
}
if (chat_message.swipes) {
chat_message.swipes[swipe_id_to_set_index] = message_demacroed;
chat_message.swipe_id = swipe_id_to_use_index;
}
if (swipe_id_to_use_index === swipe_id_to_set_index) {
chat_message.mes = message_demacroed;
}
};
const update_partial_html = async (should_update_swipe) => {
const mes_html = $(`div.mes[mesid="${message_id}"]`);
if (!mes_html.length) {
return;
}
if (should_update_swipe) {
mes_html.find('.swipes-counter').text(`${swipe_id_to_use_index + 1}\u200b/\u200b${chat_message.swipes.length}`);
}
if (refresh != 'none') {
mes_html
.find('.mes_text')
.empty()
.append(
messageFormatting(message, chat_message.name, chat_message.is_system, chat_message.is_user, message_id)
);
if (refresh === 'display_and_render_current') {
await eventSource.emit(
chat_message.is_user ? event_types.USER_MESSAGE_RENDERED : event_types.CHARACTER_MESSAGE_RENDERED,
message_id
);
}
}
};
const should_update_swipe = add_swipes_if_required();
update_chat_message();
await saveChatConditional();
if (refresh == 'all') {
await reloadCurrentChat();
} else {
await update_partial_html(should_update_swipe);
}
console.log(
`[Amily助手] 设置第 ${message_id} 楼消息, 选项: ${JSON.stringify({
swipe_id,
refresh,
})}, 设置前使用的消息页: ${swipe_id_previous_index}, 设置的消息页: ${swipe_id_to_set_index}, 现在使用的消息页: ${swipe_id_to_use_index}`
);
}
async createChatMessages(chat_messages, options = {}) {
const { insert_at = 'end', refresh = 'all' } = options;
let insertIndex = insert_at;
if (insert_at !== 'end') {
insertIndex = insert_at < 0 ? chat.length + insert_at : insert_at;
if (insertIndex < 0 || insertIndex > chat.length) {
throw new Error(`无效的插入位置: ${insert_at}`);
}
}
const newMessages = chat_messages.map(msg => ({
name: msg.name ?? (msg.role === 'user' ? name1 : name2),
is_user: msg.role === 'user',
is_system: msg.is_hidden ?? false,
mes: msg.message,
variables: [msg.data ?? {}]
}));
if (insertIndex === 'end') {
chat.push(...newMessages);
} else {
chat.splice(insertIndex, 0, ...newMessages);
}
await saveChatConditional();
if (refresh === 'affected' && insertIndex === 'end') {
newMessages.forEach(msg => addOneMessage(msg));
} else if (refresh === 'all') {
await reloadCurrentChat();
}
console.log(`[Amily助手] 已创建 ${chat_messages.length} 条消息`);
}
async deleteChatMessages(message_ids, options = {}) {
const { refresh = 'all' } = options;
const validIds = message_ids
.map(id => id < 0 ? chat.length + id : id)
.filter(id => id >= 0 && id < chat.length)
.sort((a, b) => b - a); // 从后往前删除
for (const id of validIds) {
chat.splice(id, 1);
}
await saveChatConditional();
if (refresh === 'all') {
await reloadCurrentChat();
}
console.log(`[Amily助手] 已删除消息: ${validIds.join(', ')}`);
}
async getLorebooks() {
return [...world_names];
}
async getCharLorebooks(options = { type: 'all' }) {
try {
const context = getContext();
if (!context || context.characterId === undefined) {
console.warn('[Amily助手] 无法获取当前角色上下文');
return { primary: null, additional: [] };
}
const character = characters[context.characterId];
const primary = character?.data?.extensions?.world;
return { primary: primary || null, additional: [] };
} catch (error) {
console.error('[Amily助手] 获取角色世界书时出错:', error);
return { primary: null, additional: [] };
}
}
async getLorebookEntries(bookName) {
try {
const bookData = await loadWorldInfo(bookName);
if (!bookData || !bookData.entries) {
return [];
}
const positionMap = {
0: 'before_character_definition',
1: 'after_character_definition',
2: 'before_author_note',
3: 'after_author_note',
4: 'at_depth_as_system'
};
return Object.entries(bookData.entries).map(([uid, entry]) => ({
uid: parseInt(uid),
comment: entry.comment || '无标题条目',
content: entry.content || '',
key: entry.key || [],
keys: entry.key || [],
enabled: !entry.disable,
constant: entry.constant || false,
position: positionMap[entry.position ?? entry.extensions?.position] || 'at_depth_as_system',
depth: entry.depth ?? entry.extensions?.depth ?? 998,
scanDepth: entry.scanDepth ?? entry.extensions?.scan_depth,
order: entry.order ?? entry.extensions?.display_index,
exclude_recursion: entry.excludeRecursion ?? entry.extensions?.exclude_recursion ?? false,
prevent_recursion: entry.preventRecursion ?? entry.extensions?.prevent_recursion ?? false,
}));
} catch (error) {
console.error(`[Amily助手] 获取世界书《${bookName}》条目时出错:`, error);
return [];
}
}
async setLorebookEntries(bookName, entries) {
try {
const bookData = await loadWorldInfo(bookName);
if (!bookData) {
console.error(`[Amily助手] 更新失败:找不到世界书《${bookName}`);
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;
if (entryUpdate.comment !== undefined) existingEntry.comment = entryUpdate.comment;
if (entryUpdate.key !== undefined) existingEntry.key = entryUpdate.key;
if (entryUpdate.keys !== undefined) existingEntry.key = entryUpdate.keys;
if (entryUpdate.constant !== undefined) existingEntry.constant = entryUpdate.constant;
if (entryUpdate.type === 'constant') existingEntry.constant = true;
if (entryUpdate.type === 'selective') existingEntry.constant = false;
if (entryUpdate.position !== undefined) {
const positionMap = {
'before_character_definition': 0,
'after_character_definition': 1,
'before_author_note': 2,
'after_author_note': 3,
'at_depth': 4,
'at_depth_as_system': 4
};
const mappedPos = positionMap[entryUpdate.position] ?? 4;
existingEntry.position = mappedPos;
if (!existingEntry.extensions) existingEntry.extensions = {};
existingEntry.extensions.position = mappedPos;
}
if (entryUpdate.depth !== undefined) {
existingEntry.depth = entryUpdate.depth;
if (!existingEntry.extensions) existingEntry.extensions = {};
existingEntry.extensions.depth = entryUpdate.depth;
}
if (entryUpdate.scanDepth !== undefined) {
existingEntry.scanDepth = entryUpdate.scanDepth;
if (!existingEntry.extensions) existingEntry.extensions = {};
existingEntry.extensions.scan_depth = entryUpdate.scanDepth;
}
if (entryUpdate.order !== undefined) {
existingEntry.order = entryUpdate.order;
if (!existingEntry.extensions) existingEntry.extensions = {};
existingEntry.extensions.display_index = entryUpdate.order;
}
if (entryUpdate.exclude_recursion !== undefined) {
existingEntry.excludeRecursion = entryUpdate.exclude_recursion;
if (!existingEntry.extensions) existingEntry.extensions = {};
existingEntry.extensions.exclude_recursion = entryUpdate.exclude_recursion;
}
if (entryUpdate.prevent_recursion !== undefined) {
existingEntry.preventRecursion = entryUpdate.prevent_recursion;
if (!existingEntry.extensions) existingEntry.extensions = {};
existingEntry.extensions.prevent_recursion = entryUpdate.prevent_recursion;
}
}
}
await saveWorldInfo(bookName, bookData, true);
reloadEditor(bookName);
eventSource.emit(event_types.WORLD_INFO_UPDATED, bookName);
return true;
} catch (error) {
console.error(`[Amily助手] 更新世界书《${bookName}》条目时出错:`, error);
return false;
}
}
async createLorebookEntries(bookName, entries) {
try {
let bookData = await loadWorldInfo(bookName);
if (!bookData) {
console.warn(`[Amily助手] 世界书《${bookName}》不存在,将自动创建`);
await this.createLorebook(bookName);
bookData = await loadWorldInfo(bookName);
if (!bookData) {
throw new Error(`创建并加载世界书《${bookName}》失败`);
}
}
for (const newEntryData of entries) {
const newEntry = createWorldInfoEntry(bookName, bookData);
const positionMap = {
'before_character_definition': 0,
'after_character_definition': 1,
'before_author_note': 2,
'after_author_note': 3,
'at_depth': 4,
'at_depth_as_system': 4
};
const mappedPos = typeof newEntryData.position === 'string' ? (positionMap[newEntryData.position] ?? 4) : (newEntryData.position ?? 4);
Object.assign(newEntry, {
comment: newEntryData.comment || '新条目',
content: newEntryData.content || '',
key: newEntryData.keys || newEntryData.key || [],
constant: newEntryData.type === 'constant' ? true : (newEntryData.constant || false),
position: mappedPos,
depth: newEntryData.depth ?? 998,
scanDepth: newEntryData.scanDepth ?? null,
order: newEntryData.order ?? 100,
disable: !(newEntryData.enabled ?? true),
excludeRecursion: newEntryData.excludeRecursion ?? newEntryData.exclude_recursion ?? false,
preventRecursion: newEntryData.preventRecursion ?? newEntryData.prevent_recursion ?? false,
});
if (newEntryData.type === 'selective') newEntry.constant = false;
// 兼容新版酒馆的防递归等扩展逻辑 (v1.17.0+)
if (!newEntry.extensions) newEntry.extensions = {};
newEntry.extensions.position = mappedPos;
newEntry.extensions.depth = newEntry.depth;
if (newEntry.scanDepth !== null) newEntry.extensions.scan_depth = newEntry.scanDepth;
if (newEntryData.order !== undefined) newEntry.extensions.display_index = newEntryData.order;
const hasExclude = newEntryData.excludeRecursion !== undefined || newEntryData.exclude_recursion !== undefined;
const hasPrevent = newEntryData.preventRecursion !== undefined || newEntryData.prevent_recursion !== undefined;
if (hasExclude) {
newEntry.extensions.exclude_recursion = newEntryData.excludeRecursion ?? newEntryData.exclude_recursion ?? false;
}
if (hasPrevent) {
newEntry.extensions.prevent_recursion = newEntryData.preventRecursion ?? newEntryData.prevent_recursion ?? false;
}
}
await saveWorldInfo(bookName, bookData, true);
reloadEditor(bookName);
return true;
} catch (error) {
console.error(`[Amily助手] 在世界书《${bookName}》中创建新条目时出错:`, error);
return false;
}
}
async deleteLorebookEntries(bookName, uids) {
try {
const bookData = await loadWorldInfo(bookName);
if (!bookData || !bookData.entries) {
return false;
}
let deletedCount = 0;
for (const uid of uids) {
if (bookData.entries[uid]) {
delete bookData.entries[uid];
deletedCount++;
}
}
if (deletedCount > 0) {
await saveWorldInfo(bookName, bookData, true);
reloadEditor(bookName);
console.log(`[Amily助手] 已从世界书《${bookName}》删除 ${deletedCount} 个条目`);
return true;
}
return false;
} catch (error) {
console.error(`[Amily助手] 删除世界书《${bookName}》条目时出错:`, error);
return false;
}
}
async createLorebook(bookName) {
try {
if (world_names.includes(bookName)) {
console.warn(`[Amily助手] 创建失败:世界书《${bookName}》已存在`);
return false;
}
await createNewWorldInfo(bookName);
if (!world_names.includes(bookName)) {
world_names.push(bookName);
world_names.sort();
}
document.dispatchEvent(new CustomEvent('amily-lorebook-created', { detail: { bookName } }));
return true;
} catch (error) {
console.error(`[Amily助手] 创建世界书《${bookName}》时出错:`, error);
return false;
}
}
// ==================== 斜杠命令相关 ====================
async triggerSlash(command) {
try {
console.log(`[Amily助手] 正在执行斜杠命令: ${command}`);
const result = await executeSlashCommandsWithOptions(command);
if (result.isError) {
throw new Error(result.errorMessage);
}
return result.pipe;
} catch (error) {
console.error(`[Amily助手] 执行斜杠命令 '${command}' 时出错:`, error);
throw error;
}
}
// ==================== 工具方法 ====================
async loadWorldInfo(bookName) {
return await loadWorldInfo(bookName);
}
async saveWorldInfo(bookName, data, isWorldInfo) {
await saveWorldInfo(bookName, data, isWorldInfo);
}
getLastMessageId() {
return chat.length - 1;
}
/**
* 将指定世界书绑定到当前角色
* @param {string} bookName 世界书名称
*/
async bindLorebookToCharacter(bookName) {
if (this_chid === undefined || !characters[this_chid]) {
console.warn('[Amily助手] 无法绑定世界书:未选中角色');
return false;
}
const char = characters[this_chid];
if (!char.data) char.data = {};
if (!char.data.extensions) char.data.extensions = {};
// 确保 world 字段是数组
let worlds = char.data.extensions.world;
if (!Array.isArray(worlds)) {
worlds = worlds ? [worlds] : [];
}
if (!worlds.includes(bookName)) {
worlds.push(bookName);
char.data.extensions.world = worlds;
console.log(`[Amily助手] 已将世界书《${bookName}》绑定到角色 ${char.name}`);
if (typeof saveCharacterDebounced === 'function') {
saveCharacterDebounced();
return true;
} else {
console.warn('[Amily助手] 无法保存角色数据saveCharacterDebounced 不可用');
return false;
}
}
return true; // 已经绑定
}
}
export const amilyHelper = new AmilyHelper();
export function initializeAmilyHelper() {
if (!window.AmilyHelper) {
window.AmilyHelper = amilyHelper;
console.log('[Amily2] AmilyHelper 已成功初始化并附加到 window 对象');
}
}
// ==================== iframe 通信 API ====================
export function makeRequest(request, data) {
return new Promise((resolve, reject) => {
const uid = Date.now() + Math.random();
const callbackRequest = `${request}_callback`;
function handleMessage(event) {
const msgData = event.data || {};
if (msgData.request === callbackRequest && msgData.uid === uid) {
window.removeEventListener('message', handleMessage);
if (msgData.error) {
reject(new Error(msgData.error));
} else {
resolve(msgData.result);
}
}
}
window.addEventListener('message', handleMessage);
setTimeout(() => {
window.removeEventListener('message', handleMessage);
reject(new Error(`请求 '${request}' 超时 (30秒)`));
}, 30000);
const targetOrigin = window.location.origin === 'null' ? '*' : window.location.origin;
window.parent.postMessage({
source: 'amily2-iframe-request',
request: request,
uid: uid,
data: data
}, targetOrigin);
});
}
// ==================== 主窗口 API ====================
const apiHandlers = new Map();
export function registerApiHandler(request, handler) {
if (apiHandlers.has(request)) {
console.warn(`[Amily2-IframeAPI] 覆盖请求处理器: ${request}`);
}
apiHandlers.set(request, handler);
}
export function initializeApiListener() {
window.addEventListener('message', async (event) => {
if (window.location.origin !== 'null' && event.origin !== window.location.origin) {
console.warn(`[Amily2-IframeAPI] 拒绝来自未知来源的请求: ${event.origin}`);
return;
}
const data = event.data || {};
if (data.source !== 'amily2-iframe-request' || !data.request || data.uid === undefined) {
return;
}
const handler = apiHandlers.get(data.request);
const callbackRequest = `${data.request}_callback`;
const targetOrigin = event.origin === 'null' ? '*' : event.origin;
if (!handler) {
console.error(`[Amily2-IframeAPI] 收到未知请求: ${data.request}`);
event.source.postMessage({
request: callbackRequest,
uid: data.uid,
error: `未注册请求 '${data.request}' 的处理器`
}, targetOrigin);
return;
}
try {
const result = await handler(data.data, event);
event.source.postMessage({
request: callbackRequest,
uid: data.uid,
result: result
}, targetOrigin);
} catch (error) {
console.error(`[Amily2-IframeAPI] 执行处理器 '${data.request}' 时出错:`, error);
event.source.postMessage({
request: callbackRequest,
uid: data.uid,
error: error.message || String(error)
}, targetOrigin);
}
});
console.log('[Amily2-IframeAPI] 主窗口监听器已初始化 (已启用安全验证)');
}
// ── Bus 注册 ──────────────────────────────────────────────────────────────
// 注册名:'TavernHelper'
// 暴露 amilyHelper 的全部公开方法,供其他模块通过 Bus query 访问,
// 替代各处的直接 import { amilyHelper } from '...tavern-helper/main.js'。
setTimeout(() => {
try {
const _ctx = window.Amily2Bus?.register('TavernHelper');
if (!_ctx) {
console.warn('[TavernHelper] Amily2Bus 尚未就绪,服务注册跳过。');
return;
}
_ctx.expose({
// Chat 消息操作
getChatMessages: (...a) => amilyHelper.getChatMessages(...a),
setChatMessages: (...a) => amilyHelper.setChatMessages(...a),
setChatMessage: (...a) => amilyHelper.setChatMessage(...a),
createChatMessages: (...a) => amilyHelper.createChatMessages(...a),
deleteChatMessages: (...a) => amilyHelper.deleteChatMessages(...a),
getLastMessageId: (...a) => amilyHelper.getLastMessageId(...a),
// 世界书 / Lorebook 操作
getLorebooks: (...a) => amilyHelper.getLorebooks(...a),
getCharLorebooks: (...a) => amilyHelper.getCharLorebooks(...a),
getLorebookEntries: (...a) => amilyHelper.getLorebookEntries(...a),
setLorebookEntries: (...a) => amilyHelper.setLorebookEntries(...a),
createLorebookEntries: (...a) => amilyHelper.createLorebookEntries(...a),
deleteLorebookEntries: (...a) => amilyHelper.deleteLorebookEntries(...a),
createLorebook: (...a) => amilyHelper.createLorebook(...a),
loadWorldInfo: (...a) => amilyHelper.loadWorldInfo(...a),
saveWorldInfo: (...a) => amilyHelper.saveWorldInfo(...a),
bindLorebookToCharacter: (...a) => amilyHelper.bindLorebookToCharacter(...a),
// 其他
triggerSlash: (...a) => amilyHelper.triggerSlash(...a),
});
_ctx.log('TavernHelper', 'info', 'TavernHelper 服务已注册到 Bus。');
} catch (e) {
console.error('[TavernHelper] Bus 注册失败:', e);
}
}, 0);