# 事件注册与处理

事件，可以说是插件开发的核心与基础。大多数用例中，插件的主要工作都是在接收到某些特定事件后对其做出响应。我们常见的诸如关键词匹配，上下文处理等功能，也是建立在接收并响应事件的这个基础上的。

在RainyBot中，事件分为以下几大类型：Bot自身事件，好友事件，群组事件，消息事件，请求事件，其它客户端事件，动作事件。其中的每种事件类型，还细分了许多不同的具体事件，诸如群组事件->群组更名事件，消息事件->好友消息事件等，这些您都可以在[RainyBot API](/api.md)中看到具体的划分与相关文档。

### 事件注册 <a href="#event_reg" id="event_reg"></a>

要通过插件对这些事件进行响应和处理，我们需要在插件中将所需事件\_**注册并绑定**\_到插件中的内置或自定义函数中。要这么做十分简单，就像在前面的渐进式教程中尝试过的那样——大部分情况下，我们将会在插件加载完毕时将执行的`_on_load()`虚函数中，通过调用`register_event()`函数来注册并绑定一个事件。当然，在特殊情况下您也可以在`_on_load()`虚函数以外的地方进行事件注册。

`register_event()`函数的用法十分简单，先让我们简单的看一下API中是如何介绍的：

> * void **register\_event (** [Variant](https://docs.godotengine.org/en/latest/classes/class_variant.html) event, [Variant](https://docs.godotengine.org/en/latest/classes/class_variant.html) function, [int](https://docs.godotengine.org/en/latest/classes/class_int.html) priority=0, [int](https://docs.godotengine.org/en/latest/classes/class_int.html) block\_mode=3 **)**
>
> 用于注册一个或多个事件并将其绑定到一个或多个函数，事件发生时将触发绑定的函数并传入事件实例
>
> 需要的参数从左到右分别为:
>
> 事件的类型:
>
> * 此处可传入单个事件类型名，或一个包含了任意数量事件类型名的数组以批量注册事件
> * 传入的事件需要直接或间接继承[Event](/api/event.md)类，如[GroupMessageEvent](/api/groupmessageevent.md)
>
> 事件绑定的函数名:
>
> * 此处可传入单个函数名，或一个包含了任意数量函数名的数组以批量绑定函数
> * 当对应事件发生时将依次传递并触发绑定的函数，绑定的函数需要定义一个参数用于接收事件实例
>
> 事件的全局优先级(可选,默认为0):
>
> * 在多个插件同时注册了同一事件后，事件发生时将按照优先级由高到低的顺序传递事件到对应的插件
> * 优先级相同时，将根据注册事件的时间顺序来依次传递事件
>
> 事件的阻断模式(可选,默认为BlockMode.ALL):
>
> * 事件绑定的函数若返回true，将阻断事件被传递到后续函数或插件中 (异步函数无效)
> * 阻断的具体行为将由阻断模式决定, 每种阻断模式的具体效果请参见上方的BlockMode枚举

如果您觉得看上去有些混乱，没关系，让我们来一步步了解一下每个参数的作用及用法。

* 首先，在第一个参数中，我们需要指定要注册哪个/哪些事件。如果您只需注册单个事件，则只需直接填入事件的名称即可，如`FriendMessageEvent`。如果您要同时注册多个事件，则需填入一个包含了不同事件名称的数组，如`[FriendMessageEvent, GroupMessageEvent]`将会同时注册好友消息与群消息事件。
* 在第二个参数中，我们需要指定事件要绑定到哪个/哪些函数。在插件接收到第一个参数指定的任一事件时，将会调用/依次调用此处指定的函数。与前面一样，如果您只需绑定到单个函数，只需直接填入函数名即可。若您要同时绑定到多个函数，则需填入一个包含了不同函数名的数组，数组内的顺序将决定插件接收到事件时函数被执行的顺序
* \[可选，默认为`0`] 第三个参数是一个整数，将决定此插件注册的此事件在全局的优先级。在多个插件同时注册了同一个事件的情况下，事件发生时，优先级数值越高的插件将会越早接收到这个事件。对于优先级相同的插件，将按照注册事件时的时间先后顺序来依次接收事件。
* \[可选，默认为`BlockMode.ALL`] 第四个参数，决定了当接收到事件后，事件所绑定到的函数返回`true`时将执行的操作。事件所绑定到的函数如果返回`true`，则RainyBot将尝试按照该参数指定的模式来阻止这个事件的向后传递。当前可用的阻止模式如下：

> enum **BlockMode**
>
> 事件在处理时被标记为停止传递后的阻断模式枚举，决定了该事件将如何被阻断传递
>
> * **DISABLE** = 0\
>   即使标记为停止传递也不会进行阻断
> * **EVENT** = 1\
>   在当前插件中的所有该事件函数处理完毕后，将阻断传递，即不会传递给后续插件
> * **FUNCTION** = 2\
>   当前函数处理完毕后，阻断事件在当前插件内的传递，但后续插件仍会接收到事件
> * **ALL** = 3\
>   当前函数处理完毕后，将完全阻断事件传递，事件后续函数及其他插件均不会收到事件

* 如果将此参数设置为`BlockMode.DISABLE`，那么即使事件所绑定到的函数返回`true`也不会执行任何操作。
* 如果将此参数设置为`BlockMode.EVENT`，在事件所绑定到的函数返回`true`时，也将继续向后调用当前插件中绑定到此事件的其它函数；在此事件绑定的所有其它函数执行完毕后，将阻断事件的传递，注册了同事件但优先级较低或注册较晚的插件将不会接收到该事件。
* 如果将此参数设置为`BlockMode.FUNCTION`，在事件所绑定到的函数返回`true`时，将不会继续向后执行当前插件中此事件绑定的其他函数，但是下一个优先级较低或注册较晚的插件依然会继续接收到该事件。
* 如果将此参数设置为`BlockMode.ALL`(默认为此模式)，在事件所绑定到的函数返回`true`时，将不会继续向后执行当前插件中此事件绑定的其他函数，且注册了同事件但优先级较低或注册较晚的插件也不会接收到该事件。

### 事件处理 <a href="#event_trigger" id="event_trigger"></a>

在注册了事件并绑定到函数后，当插件接收到该事件后，其绑定的函数将会被调用，且将被传入当次事件的实例作为参数。有了事件的实例，我们就能调用对应的事件的API中的各种函数，来获取各种的信息或进行各类的操作。例如，在各类[消息事件](/api/messageevent.md)中都拥有一个`reply()`函数用于回应消息，因此对于绑定到消息事件的函数，我们可以在其中用`事件实例.reply(消息)`来回应事件对应的消息。

```gdscript
func _on_load():
    register_event(FriendMessageEvent, "_event")
 
# 定义_event函数用于处理消息事件  
func _event(e): # 此函数被事件调用时，参数变量e中将储存该事件的实例
    e.reply("收到了你的消息哟~")
```

除了可以用自定义的函数来处理事件以外，RainyBot当前还针对`消息事件`提供了两个内置的事件处理函数，分别为用于匹配及触发注册的关键词的`trigger_keyword`函数，以及与用于响应上下文的`respond_context`函数；这两项功能的相关用法，我们将在稍后进行介绍。

### 取消注册事件 <a href="#event_unreg" id="event_unreg"></a>

RainyBot会在插件被卸载时自动取消注册该插件的所有事件，但您也可以根据需要，在任何时候执行`unregister_event`函数来取消注册某个/某些事件并解绑所有函数。

如果您只需取消注册单个事件，则只需直接填入事件的名称即可，如`FriendMessageEvent`。如果您要同时取消注册多个事件，则需填入一个包含了不同事件名称的数组，如`[FriendMessageEvent, GroupMessageEvent]`将会同时取消注册好友消息与群消息事件。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.rainybot.dev/readme/plugin-dev/event.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
