监听和发送事件
扩展允许你设置当发生某种事件时,运行想要的函数。例如,你也许想在玩家擅自更改你的世界书时警告玩家。
可以处理的事件请查看事件列表。
eventOn
监听事件。
让 listener
监听 event_type
,当事件发生时自动运行 listener
。
如果 listener
已经在监听 event_type
,则调用本函数不会有任何效果。
function eventOn<T extends EventType>(
event_type: T,
listener: ListenerType[T],
): void;
参数
event_type
- 类型:
EventType
- 描述: 要监听的事件
listener
- 类型:
Function
- 描述: 要注册的函数
示例
function hello() {
alert("hello");
}
eventOn(tavern_events.MESSAGE_RECEIVED, hello);
// 酒馆事件 tavern_events.MESSAGE_UPDATED 会传递被更新的楼层 id
eventOn(tavern_events.MESSAGE_UPDATED, detectMessageEdited);
function detectMessageEdited(message_id) {
alert(`你刚刚更新了第 ${message_id} 条聊天消息对吧😡`);
}
eventOnButton
监听脚本库中的按键事件。
function eventOnButton<T extends EventType>(event_type: T, listener: ListenerType[T]): void
参数
event_type
- 类型:
string
- 描述: 在按键绑定中设定的按键名称
listener
- 类型:
Function
- 描述: 要注册的函数
示例
eventOnButton("按键名称", 要注册的函数);
eventMakeLast
让 listener
监听 event_type
,当事件发生时自动在最后运行 listener
。
如果 listener
已经在监听 event_type
,则调用本函数会将 listener
调整为最后运行。
function eventMakeLast<T extends EventType>(
event_type: T,
listener: ListenerType[T],
): void;
参数
event_type
- 类型:
EventType
- 描述: 要监听的事件
listener
- 类型:
Function
- 描述: 要注册/调整到最后运行的函数
示例
eventMakeLast(要监听的事件, 要注册的函数);
eventMakeFirst
让 listener
监听 event_type
,当事件发生时自动在最先运行 listener
。
如果 listener
已经在监听 event_type
,则调用本函数会将 listener
调整为最先运行。
function eventMakeFirst<T extends EventType>(
event_type: T,
listener: ListenerType[T],
): void;
参数
event_type
- 类型:
EventType
- 描述: 要监听的事件
listener
- 类型:
Function
- 描述: 要注册/调整到最先运行的函数
示例
eventMakeLast(要监听的事件, 要注册的函数);
eventOnce
让 listener
仅监听下一次 event_type
,当该次事件发生时运行 listener
, 此后取消监听。
如果 listener
已经在监听 event_type
,则调用本函数不会有任何效果。
function eventOnce<T extends EventType>(
event_type: T,
listener: ListenerType[T],
): void;
参数
event_type
- 类型:
EventType
- 描述: 要监听的事件
listener
- 类型:
Function
- 描述: 要注册的函数
示例
eventOnce(要监听的事件, 要注册的函数);
eventWaitOnce
等待一次 event_type
事件
async function eventWaitOnce(event_type: EventType): Promise<any | undefined>;
参数
event_type
- 类型:
EventType
- 描述: 要等待的事件
示例
await eventWaitOnce(tavern_events.MESSAGE_DELETED);
eventWaitOnce
等待 listener
监听到一次 event_type
且执行完成,返回 listener
的执行结果。
在调用本函数前, listener
必须已经在监听 event_type
。
async function eventWaitOnce<T extends EventType>(
event_type: T,
listener: ListenerType[T],
): Promise<any | undefined>;
参数
event_type
- 类型:
EventType
- 描述:
listener
在监听的事件
listener
- 类型:
Function
- 描述: 已经在监听
event_type
的函数
示例
eventOnce("存档", save);
await eventWaitOnce("存档", save);
eventEmit
发送 event_type
事件,同时可以发送一些数据 data
。
所有正在监听 event_type
消息频道的都会收到该消息并接收到 data
。
async function eventEmit<T extends EventType>(
event_type: T,
...data: Parameters<ListenerType[T]>
): Promise<void>;
参数
event_type
- 类型:
EventType
- 描述: 要发送的事件
data
- 类型:
any[]
- 描述: 要随着事件发送的数据
示例
// 发送 "角色阶段更新完成" 事件, 所有监听该事件的 `listener` 都会被运行
eventEmit("角色阶段更新完成");
// 发送 "存档" 事件, 并等待所有 `listener` (也许是负责存档的函数) 执行完毕后才继续
await eventEmit("存档");
// 发送时携带数据 ["你好", 0]
eventEmit("事件", "你好", 0);
eventRemoveListener
让 listener
取消对 event_type
的监听。
如果 listener
没有监听 event_type
,则调用本函数不会有任何效果。
function eventRemoveListener<T extends EventType>(
event_type: T,
listener: ListenerType[T],
): void;
参数
event_type
- 类型:
EventType
- 描述: 要发送的事件
listener
- 类型:
Function
- 描述: 要取消注册的函数
示例
eventRemoveListener(要取消监听的事件, 要取消注册的函数);
eventClearEvent
取消本 iframe 中对 event_type
的所有监听。
function eventClearEvent(event_type: EventType): void;
参数
event_type
- 类型:
EventType
- 描述: 要取消监听的事件
eventClearListener
取消本 iframe 中 listener
的所有监听。
function eventClearListener(listener: Function): void;
参数
listener
- 类型:
Function
- 描述: 要取消注册的函数
eventClearAll
取消本 iframe 中对所有事件的所有监听。
function eventClearAll(): void;
Quick Reply 命令
我们还提供了 Quick Reply 命令 /event-emit
,允许你通过在快速回复中发送事件来触发 js 代码。
/event-emit event="随便什么名称" data="这是一个 数据" data={{user}}
eventOn("随便什么名字", (data1, data2) => { console.info(data1, data2); });
当我们按下该快速回复的按钮后,正在监听 "事件名称" 消息频道的 js 代码将会获得 data
并开始执行。
事件列表
事件可以是:
iframe_events
中的 iframe 事件tavern_events
中的酒馆事件- 自定义的字符串事件
所有 iframe 事件
const iframe_events = {
MESSAGE_IFRAME_RENDER_STARTED: "message_iframe_render_started",
MESSAGE_IFRAME_RENDER_ENDED: "message_iframe_render_ended",
VARIABLES_UPDATED: 'variables_updated', // 全局/聊天/随楼变量通过酒馆助手的函数得到更新
GENERATION_STARTED: "js_generation_started", // `generate` 函数开始生成
STREAM_TOKEN_RECEIVED_FULLY: "js_stream_token_received_fully", // 启用流式传输的 `generate` 函数传输当前完整文本: "这是", "这是一条", "这是一条流式传输"
STREAM_TOKEN_RECEIVED_INCREMENTALLY: "js_stream_token_received_incrementally", // 启用流式传输的 `generate` 函数传输当前增量文本: "这是", "一条", "流式传输"
GENERATION_ENDED: "js_generation_ended", // `generate` 函数完成生成
};
所有酒馆事件
const tavern_events = {
APP_READY: "app_ready",
EXTRAS_CONNECTED: "extras_connected",
MESSAGE_SWIPED: "message_swiped",
MESSAGE_SENT: "message_sent",
MESSAGE_RECEIVED: "message_received",
MESSAGE_EDITED: "message_edited",
MESSAGE_DELETED: "message_deleted",
MESSAGE_UPDATED: "message_updated",
MESSAGE_FILE_EMBEDDED: "message_file_embedded",
IMPERSONATE_READY: "impersonate_ready",
CHAT_CHANGED: "chat_id_changed",
GENERATION_AFTER_COMMANDS: "GENERATION_AFTER_COMMANDS",
GENERATION_STARTED: "generation_started",
GENERATION_STOPPED: "generation_stopped",
GENERATION_ENDED: "generation_ended",
EXTENSIONS_FIRST_LOAD: "extensions_first_load",
EXTENSION_SETTINGS_LOADED: "extension_settings_loaded",
SETTINGS_LOADED: "settings_loaded",
SETTINGS_UPDATED: "settings_updated",
GROUP_UPDATED: "group_updated",
MOVABLE_PANELS_RESET: "movable_panels_reset",
SETTINGS_LOADED_BEFORE: "settings_loaded_before",
SETTINGS_LOADED_AFTER: "settings_loaded_after",
CHATCOMPLETION_SOURCE_CHANGED: "chatcompletion_source_changed",
CHATCOMPLETION_MODEL_CHANGED: "chatcompletion_model_changed",
OAI_PRESET_CHANGED_BEFORE: "oai_preset_changed_before",
OAI_PRESET_CHANGED_AFTER: "oai_preset_changed_after",
OAI_PRESET_EXPORT_READY: "oai_preset_export_ready",
OAI_PRESET_IMPORT_READY: "oai_preset_import_ready",
WORLDINFO_SETTINGS_UPDATED: "worldinfo_settings_updated",
WORLDINFO_UPDATED: "worldinfo_updated",
CHARACTER_EDITED: "character_edited",
CHARACTER_PAGE_LOADED: "character_page_loaded",
CHARACTER_GROUP_OVERLAY_STATE_CHANGE_BEFORE:
"character_group_overlay_state_change_before",
CHARACTER_GROUP_OVERLAY_STATE_CHANGE_AFTER:
"character_group_overlay_state_change_after",
USER_MESSAGE_RENDERED: "user_message_rendered",
CHARACTER_MESSAGE_RENDERED: "character_message_rendered",
FORCE_SET_BACKGROUND: "force_set_background",
CHAT_DELETED: "chat_deleted",
CHAT_CREATED: "chat_created",
GROUP_CHAT_DELETED: "group_chat_deleted",
GROUP_CHAT_CREATED: "group_chat_created",
GENERATE_BEFORE_COMBINE_PROMPTS: "generate_before_combine_prompts",
GENERATE_AFTER_COMBINE_PROMPTS: "generate_after_combine_prompts",
GENERATE_AFTER_DATA: "generate_after_data",
GROUP_MEMBER_DRAFTED: "group_member_drafted",
WORLD_INFO_ACTIVATED: "world_info_activated",
TEXT_COMPLETION_SETTINGS_READY: "text_completion_settings_ready",
CHAT_COMPLETION_SETTINGS_READY: "chat_completion_settings_ready",
CHAT_COMPLETION_PROMPT_READY: "chat_completion_prompt_ready",
CHARACTER_FIRST_MESSAGE_SELECTED: "character_first_message_selected",
// TODO: Naming convention is inconsistent with other events
CHARACTER_DELETED: "characterDeleted",
CHARACTER_DUPLICATED: "character_duplicated",
STREAM_TOKEN_RECEIVED: "stream_token_received",
FILE_ATTACHMENT_DELETED: "file_attachment_deleted",
WORLDINFO_FORCE_ACTIVATE: "worldinfo_force_activate",
OPEN_CHARACTER_LIBRARY: "open_character_library",
ONLINE_STATUS_CHANGED: "online_status_changed",
IMAGE_SWIPED: "image_swiped",
CONNECTION_PROFILE_LOADED: "connection_profile_loaded",
TOOL_CALLS_PERFORMED: "tool_calls_performed",
TOOL_CALLS_RENDERED: "tool_calls_rendered",
};
查看事件发生时会发送的数据
type ListenerType = {
[iframe_events.MESSAGE_IFRAME_RENDER_STARTED]: (iframe_name: string) => void;
[iframe_events.MESSAGE_IFRAME_RENDER_ENDED]: (iframe_name: string) => void;
[iframe_events.VARIABLES_UPDATED]: (
type: 'global' | 'chat' | 'message',
variables: Record<string, any>,
message_info?: { message_id: number; swipe_id: number },
) => void;
[iframe_events.GENERATION_STARTED]: () => void;
[iframe_events.STREAM_TOKEN_RECEIVED_FULLY]: (full_text: string) => void;
[iframe_events.STREAM_TOKEN_RECEIVED_INCREMENTALLY]: (incremental_text: string) => void;
[iframe_events.GENERATION_ENDED]: (text: string) => void;
[tavern_events.APP_READY]: () => void;
[tavern_events.EXTRAS_CONNECTED]: (modules: any) => void;
[tavern_events.MESSAGE_SWIPED]: (message_id: number) => void;
[tavern_events.MESSAGE_SENT]: (message_id: number) => void;
[tavern_events.MESSAGE_RECEIVED]: (message_id: number) => void;
[tavern_events.MESSAGE_EDITED]: (message_id: number) => void;
[tavern_events.MESSAGE_DELETED]: (message_id: number) => void;
[tavern_events.MESSAGE_UPDATED]: (message_id: number) => void;
[tavern_events.MESSAGE_FILE_EMBEDDED]: (message_id: number) => void;
[tavern_events.IMPERSONATE_READY]: (message: string) => void;
[tavern_events.CHAT_CHANGED]: (chat_file_name: string) => void;
[tavern_events.GENERATION_AFTER_COMMANDS]: (
type: string,
option: {
automatic_trigger?: boolean;
force_name2?: boolean;
quiet_prompt?: string;
quietToLoud?: boolean;
skipWIAN?: boolean;
force_chid?: number;
signal?: AbortSignal;
quietImage?: string;
quietName?: string;
depth?: number;
},
dry_run: boolean,
) => void;
[tavern_events.GENERATION_STARTED]: (
type: string,
option: {
automatic_trigger?: boolean;
force_name2?: boolean;
quiet_prompt?: string;
quietToLoud?: boolean;
skipWIAN?: boolean;
force_chid?: number;
signal?: AbortSignal;
quietImage?: string;
quietName?: string;
depth?: number;
},
dry_run: boolean,
) => void;
[tavern_events.GENERATION_STOPPED]: () => void;
[tavern_events.GENERATION_ENDED]: (message_id: number) => void;
[tavern_events.EXTENSIONS_FIRST_LOAD]: () => void;
[tavern_events.EXTENSION_SETTINGS_LOADED]: () => void;
[tavern_events.SETTINGS_LOADED]: () => void;
[tavern_events.SETTINGS_UPDATED]: () => void;
[tavern_events.GROUP_UPDATED]: () => void;
[tavern_events.MOVABLE_PANELS_RESET]: () => void;
[tavern_events.SETTINGS_LOADED_BEFORE]: (settings: Object) => void;
[tavern_events.SETTINGS_LOADED_AFTER]: (settings: Object) => void;
[tavern_events.CHATCOMPLETION_SOURCE_CHANGED]: (source: string) => void;
[tavern_events.CHATCOMPLETION_MODEL_CHANGED]: (model: string) => void;
[tavern_events.OAI_PRESET_CHANGED_BEFORE]: (result: {
preset: Object;
presetName: string;
settingsToUpdate: Object;
settings: Object;
savePreset: Function;
}) => void;
[tavern_events.OAI_PRESET_CHANGED_AFTER]: () => void;
[tavern_events.OAI_PRESET_EXPORT_READY]: (preset: Object) => void;
[tavern_events.OAI_PRESET_IMPORT_READY]: (result: { data: Object; presetName: string }) => void;
[tavern_events.WORLDINFO_SETTINGS_UPDATED]: () => void;
[tavern_events.WORLDINFO_UPDATED]: (name: string, data: { entries: Object[] }) => void;
[tavern_events.CHARACTER_EDITED]: (result: { detail: { id: string; character: Object } }) => void;
[tavern_events.CHARACTER_PAGE_LOADED]: () => void;
[tavern_events.CHARACTER_GROUP_OVERLAY_STATE_CHANGE_BEFORE]: (state: number) => void;
[tavern_events.CHARACTER_GROUP_OVERLAY_STATE_CHANGE_AFTER]: (state: number) => void;
[tavern_events.USER_MESSAGE_RENDERED]: (message_id: number) => void;
[tavern_events.CHARACTER_MESSAGE_RENDERED]: (message_id: number) => void;
[tavern_events.FORCE_SET_BACKGROUND]: (background: { url: string; path: string }) => void;
[tavern_events.CHAT_DELETED]: (chat_file_name: string) => void;
[tavern_events.CHAT_CREATED]: () => void;
[tavern_events.GROUP_CHAT_DELETED]: (chat_file_name: string) => void;
[tavern_events.GROUP_CHAT_CREATED]: () => void;
[tavern_events.GENERATE_BEFORE_COMBINE_PROMPTS]: () => void;
[tavern_events.GENERATE_AFTER_COMBINE_PROMPTS]: (result: { prompt: string; dryRun: boolean }) => void;
[tavern_events.GENERATE_AFTER_DATA]: (generate_data: Object) => void;
[tavern_events.GROUP_MEMBER_DRAFTED]: (character_id: string) => void;
[tavern_events.WORLD_INFO_ACTIVATED]: (entries: any[]) => void;
[tavern_events.TEXT_COMPLETION_SETTINGS_READY]: () => void;
[tavern_events.CHAT_COMPLETION_SETTINGS_READY]: (generate_data: {
messages: { role: string; content: string }[];
model: string;
temprature: number;
frequency_penalty: number;
presence_penalty: number;
top_p: number;
max_tokens: number;
stream: boolean;
logit_bias: Object;
stop: string[];
chat_comletion_source: string;
n?: number;
user_name: string;
char_name: string;
group_names: string[];
include_reasoning: boolean;
reasoning_effort: string;
[others: string]: any;
}) => void;
[tavern_events.CHAT_COMPLETION_PROMPT_READY]: (event_data: {
chat: { role: string; content: string }[];
dryRun: boolean;
}) => void;
[tavern_events.CHARACTER_FIRST_MESSAGE_SELECTED]: (event_args: {
input: string;
output: string;
character: Object;
}) => void;
[tavern_events.CHARACTER_DELETED]: (result: { id: string; character: Object }) => void;
[tavern_events.CHARACTER_DUPLICATED]: (result: { oldAvatar: string; newAvatar: string }) => void;
[tavern_events.STREAM_TOKEN_RECEIVED]: (text: string) => void;
[tavern_events.FILE_ATTACHMENT_DELETED]: (url: string) => void;
[tavern_events.WORLDINFO_FORCE_ACTIVATE]: (entries: Object[]) => void;
[tavern_events.OPEN_CHARACTER_LIBRARY]: () => void;
[tavern_events.ONLINE_STATUS_CHANGED]: () => void;
[tavern_events.IMAGE_SWIPED]: (result: {
message: Object;
element: JQuery<HTMLElement>;
direction: 'left' | 'right';
}) => void;
[tavern_events.CONNECTION_PROFILE_LOADED]: (profile_name: string) => void;
[tavern_events.TOOL_CALLS_PERFORMED]: (tool_invocations: Object[]) => void;
[tavern_events.TOOL_CALLS_RENDERED]: (tool_invocations: Object[]) => void;
[custom_event: string]: (...args: any) => any;
};