Skip to content

监听和发送事件

扩展允许你设置当发生某种事件时,运行想要的函数。例如,你也许想在玩家擅自更改你的世界书时警告玩家。

可以处理的事件请查看事件列表

eventOn

监听事件。

listener 监听 event_type,当事件发生时自动运行 listener

如果 listener 已经在监听 event_type,则调用本函数不会有任何效果。

typescript
function eventOn<T extends EventType>(
  event_type: T,
  listener: ListenerType[T],
): void;

参数

event_type

listener

  • 类型: Function
  • 描述: 要注册的函数

示例

typescript
function hello() {
  alert("hello");
}
eventOn(tavern_events.MESSAGE_RECEIVED, hello);
typescript
// 酒馆事件 tavern_events.MESSAGE_UPDATED 会传递被更新的楼层 id
eventOn(tavern_events.MESSAGE_UPDATED, detectMessageEdited);

function detectMessageEdited(message_id) {
  alert(`你刚刚更新了第 ${message_id} 条聊天消息对吧😡`);
}

eventOnButton

监听脚本库中的按键事件。

typescript
function eventOnButton<T extends EventType>(event_type: T, listener: ListenerType[T]): void

参数

event_type

  • 类型: string
  • 描述: 在按键绑定中设定的按键名称

listener

  • 类型: Function
  • 描述: 要注册的函数

示例

typescript
eventOnButton("按键名称", 要注册的函数);

eventMakeLast

listener 监听 event_type,当事件发生时自动在最后运行 listener

如果 listener 已经在监听 event_type,则调用本函数会将 listener 调整为最后运行。

typescript
function eventMakeLast<T extends EventType>(
  event_type: T,
  listener: ListenerType[T],
): void;

参数

event_type

listener

  • 类型: Function
  • 描述: 要注册/调整到最后运行的函数

示例

typescript
eventMakeLast(要监听的事件, 要注册的函数);

eventMakeFirst

listener 监听 event_type,当事件发生时自动在最先运行 listener

如果 listener 已经在监听 event_type,则调用本函数会将 listener 调整为最先运行。

typescript
function eventMakeFirst<T extends EventType>(
  event_type: T,
  listener: ListenerType[T],
): void;

参数

event_type

listener

  • 类型: Function
  • 描述: 要注册/调整到最先运行的函数

示例

typescript
eventMakeLast(要监听的事件, 要注册的函数);

eventOnce

listener 仅监听下一次 event_type,当该次事件发生时运行 listener, 此后取消监听。

如果 listener 已经在监听 event_type,则调用本函数不会有任何效果。

typescript
function eventOnce<T extends EventType>(
  event_type: T,
  listener: ListenerType[T],
): void;

参数

event_type

listener

  • 类型: Function
  • 描述: 要注册的函数

示例

typescript
eventOnce(要监听的事件, 要注册的函数);

eventWaitOnce

等待一次 event_type 事件

typescript
async function eventWaitOnce(event_type: EventType): Promise<any | undefined>;

参数

event_type

示例

typescript
await eventWaitOnce(tavern_events.MESSAGE_DELETED);

eventWaitOnce

等待 listener 监听到一次 event_type 且执行完成,返回 listener 的执行结果。

在调用本函数前, listener 必须已经在监听 event_type

typescript
async function eventWaitOnce<T extends EventType>(
  event_type: T,
  listener: ListenerType[T],
): Promise<any | undefined>;

参数

event_type

  • 类型: EventType
  • 描述: listener 在监听的事件

listener

  • 类型: Function
  • 描述: 已经在监听 event_type 的函数

示例

typescript
eventOnce("存档", save);
await eventWaitOnce("存档", save);

eventEmit

发送 event_type 事件,同时可以发送一些数据 data

所有正在监听 event_type 消息频道的都会收到该消息并接收到 data

typescript
async function eventEmit<T extends EventType>(
  event_type: T,
  ...data: Parameters<ListenerType[T]>
): Promise<void>;

参数

event_type

data

  • 类型: any[]
  • 描述: 要随着事件发送的数据

示例

typescript
// 发送 "角色阶段更新完成" 事件, 所有监听该事件的 `listener` 都会被运行
eventEmit("角色阶段更新完成");
typescript
// 发送 "存档" 事件, 并等待所有 `listener` (也许是负责存档的函数) 执行完毕后才继续
await eventEmit("存档");
typescript
// 发送时携带数据 ["你好", 0]
eventEmit("事件", "你好", 0);

eventRemoveListener

listener 取消对 event_type 的监听。

如果 listener 没有监听 event_type,则调用本函数不会有任何效果。

typescript
function eventRemoveListener<T extends EventType>(
  event_type: T,
  listener: ListenerType[T],
): void;

参数

event_type

listener

  • 类型: Function
  • 描述: 要取消注册的函数

示例

typescript
eventRemoveListener(要取消监听的事件, 要取消注册的函数);

eventClearEvent

取消本 iframe 中对 event_type 的所有监听。

typescript
function eventClearEvent(event_type: EventType): void;

参数

event_type

  • 类型: EventType
  • 描述: 要取消监听的事件

eventClearListener

取消本 iframe 中 listener 的所有监听。

typescript
function eventClearListener(listener: Function): void;

参数

listener

  • 类型: Function
  • 描述: 要取消注册的函数

eventClearAll

取消本 iframe 中对所有事件的所有监听。

typescript
function eventClearAll(): void;

Quick Reply 命令

我们还提供了 Quick Reply 命令 /event-emit,允许你通过在快速回复中发送事件来触发 js 代码。

plaintext
/event-emit event="随便什么名称" data="这是一个 数据" data={{user}}
typescript
eventOn("随便什么名字", (data1, data2) => { console.info(data1, data2); });

当我们按下该快速回复的按钮后,正在监听 "事件名称" 消息频道的 js 代码将会获得 data 并开始执行。

事件列表

事件可以是:

  • iframe_events 中的 iframe 事件
  • tavern_events 中的酒馆事件
  • 自定义的字符串事件
所有 iframe 事件
typescript
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` 函数完成生成
};
所有酒馆事件
typescript
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",
};
查看事件发生时会发送的数据
typescript
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;
};

作者:KAKAA, 青空莉想做舞台少女的狗