Skip to content

使用长连接接收事件

长连接是飞书 SDK 内提供的能力,你可以在本地服务器集成飞书 SDK, 与开放平台建立一条 WebSocket 全双工通道(你的服务器需要能够访问公网)。后续当应用订阅的事件发生时,开放平台会通过该通道向你的服务器发送事件消息。

功能优势

相较于传统的 Webhook 模式,长连接模式大大降低了接入成本,将原先 1 周左右的开发周期降低到 5 分钟。具体优势如下:

  • 开发调用方便快捷

    只需保证运行环境具备访问公网的能力即可,无需提供公网 IP 或域名、无需使用内网穿透工具,通过长连接模式在本地开发环境中即可接收事件消息,后续在线上部署本地服务后也可以直接生效。

  • 加密传输,无需额外处理加密解密逻辑

    长连接方式内置了通信通信加密和鉴权的逻辑,事件数据网络上传输时为加密传输,对于开发者而言无需额外解密、验签逻辑,也无需部署防火墙和配置白名单。

注意事项

在开始配置之前,你需要确保已了解以下注意事项:

  • 长连接模式仅支持企业自建应用。商店应用事件订阅方式参考将事件发送至开发者服务器
  • 长连接模式下接收到消息后,需要在 3 秒内处理完成且不抛出异常,否则会触发超时重推机制。
  • 每个应用最多建立 50 个连接(在配置长连接时,每初始化一个 client 就是一个连接)。
  • 长连接模式的消息推送为 集群模式,不支持广播,即如果同一应用部署了多个客户端(client),那么只有 其中随机一个 客户端会收到消息。

上手体验

开放平台提供了一键开发自动回复机器人的场景体验教程,你可以前往体验基于接收消息事件所搭建的机器人应用,详情参见:

步骤一:集成 SDK

以下提供了飞书 SDK 的 Golang、Python、Java、Node.js 示例代码,代码内以接收消息事件为例进行配置。通过飞书 SDK 除了可以实现事件订阅,还可以实现 API 调用、订阅回调,关于 SDK 详细介绍参见服务端 SDK

Golang

安装 SDK

go get -u github.com/larksuite/oapi-sdk-go/v3

示例代码

go
package main
import (
   "context"
   "fmt"
   larkcore "github.com/larksuite/oapi-sdk-go/v3/core"
   larkevent "github.com/larksuite/oapi-sdk-go/v3/event"
   "github.com/larksuite/oapi-sdk-go/v3/event/dispatcher"
   larkim "github.com/larksuite/oapi-sdk-go/v3/service/im/v1"
   larkws "github.com/larksuite/oapi-sdk-go/v3/ws"
)
func main() {
   // 注册事件回调,OnP2MessageReceiveV1 为接收消息 v2.0;OnCustomizedEvent 内的 message 为接收消息 v1.0。
   eventHandler := dispatcher.NewEventDispatcher("", "").
      OnP2MessageReceiveV1(func(ctx context.Context, event *larkim.P2MessageReceiveV1) error {
         fmt.Printf("[ OnP2MessageReceiveV1 access ], data: %s\n", larkcore.Prettify(event))
         return nil
      }).
      OnCustomizedEvent("这里填入你要自定义订阅的 event 的 key,例如 out_approval", func(ctx context.Context, event *larkevent.EventReq) error {
         fmt.Printf("[ OnCustomizedEvent access ], type: message, data: %s\n", string(event.Body))
         return nil
      })
   // 创建Client
   cli := larkws.NewClient("YOUR_APP_ID", "YOUR_APP_SECRET",
      larkws.WithEventHandler(eventHandler),
      larkws.WithLogLevel(larkcore.LogLevelDebug),
   )
   // 启动客户端
   err := cli.Start(context.Background())
   if err != nil {
      panic(err)
   }
}

代码实现说明:

  1. 通过 dispatcher.NewEventDispatcher() 初始化事件处理器(eventHandler),注意两个参数必须填空字符串

  2. 通过 eventHandler 的 OnXXXX() 方法处理不同的事件。上述示例中分别监听了「接收消息 v1.0」和「接收消息 v2.0」两个事件。

    说明:开放平台提供的事件包括 v1.0 和 v2.0 两个版本。两个版本的结构不同。在处理事件时,可通过事件列表文档或在开发者后台添加事件时,获取事件的类型与版本信息。

    如果是 v1.0 事件,则注册服务器时需要查找并使用 OnCustomizedEvent 方法;如果是 v2.0 事件,则注册服务器时需要查找并使用 onP2xxxx 开头的方法。 例如,使用 onP2MessageReceiveV1 注册接收消息事件回调时,P2便对应的是 v2.0 版本事件结构。

  3. 通过 larkws.NewClient() 初始化长连接客户端,必填参数为应用的 APP_ID 和 APP_SECRET,需登录开发者后台,在应用详情页的 凭证与基础信息 > 应用凭证 区域获取。

  4. 可选参数传入 eventHandler,同时可设置日志级别。

  5. 通过 cli.Start() 启动客户端,如连接成功,控制台会打印 "connected to wss://xxxxx",主线程将阻塞,直到进程结束。

Python

安装 SDK

pip install lark-oapi -U

示例代码

python
import lark_oapi as lark

## P2ImMessageReceiveV1 为接收消息 v2.0;CustomizedEvent 内的 message 为接收消息 v1.0。

def do_p2_im_message_receive_v1(data: lark.im.v1.P2ImMessageReceiveV1) -> None:
    print(f'[ do_p2_im_message_receive_v1 access ], data: {lark.JSON.marshal(data, indent=4)}')
def do_message_event(data: lark.CustomizedEvent) -> None:
    print(f'[ do_customized_event access ], type: message, data: {lark.JSON.marshal(data, indent=4)}')
event_handler = lark.EventDispatcherHandler.builder("", "") \
    .register_p2_im_message_receive_v1(do_p2_im_message_receive_v1) \
    .register_p1_customized_event("这里填入你要自定义订阅的 event 的 key,例如 out_approval", do_message_event) \
    .build()
def main():
    cli = lark.ws.Client("YOUR_APP_ID", "YOUR_APP_SECRET",
                         event_handler=event_handler,
                         log_level=lark.LogLevel.DEBUG)
    cli.start()
if __name__ == "__main__":
    main()

代码实现说明:

  1. 通过 lark.EventDispatcherHandler.builder() 初始化事件处理器(event_handler),该方法的两个参数必须填空字符串。

  2. 通过 event_handler 的 register_{事件版本}_{事件类型 或 customized_event} 方法处理不同的事件。

    在开发者后台应用详情页添加事件时(具体操作可参见添加事件),可获取事件的类型与版本信息。

     ![](https://sf3-cn.feishucdn.com/obj/open-platform-opendoc/4dbc1cd97d95f1af949354ce3c350461_bidnBwXPPi.png?height=380&lazyload=true&maxWidth=600&width=1666)
    
     **v2.0 版本的事件**
    
     - 使用`data: lark.im.v1.P2ImMessageReceiveV1` 指定所选事件,其中:
    
               - `lark.im.v1`表示对应的业务域以及版本,可通过事件类型的前缀获取业务域代码,例如 `im.message.receive_v1` 对应的业务域代码为 `im`。
    
               - `P2ImMessageReceiveV1` 中,**P2** 表示事件版本为 v2.0,**ImMessageReceiveV1** 对应事件类型 `im.message.receive_v1`。
    
     - 使用 `.register_p2_im_message_receive_v1()` 处理事件,其中:
    
               - **p2** 表示事件版本为 v2.0。
    
               - **im_message_receive_v1** 对应事件类型 `im.message.receive_v1`。
    
     :::note
     **说明**:代码中的方法格式与事件类型不一致,注意甄别格式上的差异,避免因格式问题导致程序运行失败。  
     :::
    

    v1.0 版本的事件

     - 固定使用 `data: lark.CustomizedEvent` 方法,表示处理自定义事件。
     - 使用 `.register_p1_customized_event("这里填入你要自定义订阅的事件类型,例如 out_approval", do_message_event)` 处理事件:
         - **p1** 表示事件版本为 v1.0。
         - **customized_event** 为固定的方法名称。
         - customized_event 方法内需要传入事件 Key(String 类型),例如[外出审批](https://open.larkoffice.com/document/ukTMukTMukTM/uIDO24iM4YjLygjN/event/out-of-office)的事件类型为 out_approval。
    

    你也可以参考代码片段注释了解如何配置:

    python
    ## P2ImMessageReceiveV1:
    ## P2 对应 v2.0 版本,
    ## ImMessageReceiveV1 对应接收消息事件类型 im.message.receive_v1。
    def do_p2_im_message_receive_v1(data: lark.im.v1.P2ImMessageReceiveV1) -> None:
        print(f'[ do_p2_im_message_receive_v1 access ], data: {lark.JSON.marshal(data, indent=4)}')
    
    ## 如果是 v1.0 版本事件,需要使用 lark.CustomizedEvent
    def do_message_event(data: lark.CustomizedEvent) -> None:
        print(f'[ do_customized_event access ], type: message, data: {lark.JSON.marshal(data, indent=4)}')
    
    event_handler = lark.EventDispatcherHandler.builder("", "") \
        ## register_p2 对应 v2.0 版本,
        ## im_message_receive_v1 对应接收消息事件类型 im.message.receive_v1。
        .register_p2_im_message_receive_v1(do_p2_im_message_receive_v1) \
        ## register_p1 对应 v1.0 版本,
        ## 参数值需要传入事件类型。例如 out_approval
        .register_p1_customized_event("这里填入你要自定义订阅的 event 的 key,例如 out_approval", do_message_event) \
        .build()
  3. 通过 lark.ws.Client() 初始化长连接客户端,必填参数为应用的 APP_ID 和 APP_SECRET,需登录开发者后台,在应用详情页的 凭证与基础信息 > 应用凭证 区域获取。

  4. 可选参数传入 event_handler,同时可设置日志级别。

  5. 通过 cli.start() 启动客户端,如连接成功,控制台会打印 "connected to wss://xxxxx",主线程将阻塞,直到进程结束。

Java

安装 SDK

SDK 最新版本可以在这里查看。

xml
<dependencies>
  ...
  <dependency>
    <groupId>com.larksuite.oapi</groupId>
    <artifactId>oapi-sdk</artifactId>
    <version>{latest version}</version>
  </dependency>
  ...
</dependencies>

示例代码

java
package com.lark.oapi.sample.ws;
import com.lark.oapi.core.request.EventReq;
import com.lark.oapi.core.utils.Jsons;
import com.lark.oapi.event.CustomEventHandler;
import com.lark.oapi.event.EventDispatcher;
import com.lark.oapi.service.im.ImService;
import com.lark.oapi.service.im.v1.model.P2MessageReceiveV1;
import com.lark.oapi.ws.Client;
import java.nio.charset.StandardCharsets;
public class Sample {
          // onP2MessageReceiveV1 为接收消息 v2.0;onCustomizedEvent 内的 message 为接收消息 v1.0。
    private static final EventDispatcher EVENT_HANDLER = EventDispatcher.newBuilder("", "")
            .onP2MessageReceiveV1(new ImService.P2MessageReceiveV1Handler() {
                @Override
                public void handle(P2MessageReceiveV1 event) throws Exception {
                    System.out.printf("[ onP2MessageReceiveV1 access ], data: %s\n", Jsons.DEFAULT.toJson(event.getEvent()));
                }
            })
            .onCustomizedEvent("这里填入你要自定义订阅的 event 的 key,例如 out_approval", new CustomEventHandler() {
                @Override
                public void handle(EventReq event) throws Exception {
                    System.out.printf("[ onCustomizedEvent access ], type: message, data: %s\n", new String(event.getBody(), StandardCharsets.UTF_8));
                }
            })
            .build();
    public static void main(String[] args) {
        Client cli = new Client.Builder("YOUR_APP_ID", "YOUR_APP_SECRET")
                .eventHandler(EVENT_HANDLER)
                .build();
        cli.start();
    }
}

代码实现说明:

  1. 通过 EventDispatcher.newBuilder() 初始化事件处理器(EVENT_HANDLER),注意两个参数必须填空字符串

  2. 通过 EVENT_HANDLER 的 onXXXX() 方法处理不同的事件。上述示例中分别监听了「接收消息 v1.0」和「接收消息 v2.0」两个事件。

    说明:开放平台提供的事件包括 v1.0 和 v2.0 两个版本。两个版本的结构不同。在处理事件时,可通过事件列表文档或在开发者后台添加事件时,获取事件的类型与版本信息。

    如果是 v1.0 事件,则注册服务器时需要查找并使用 onP1xxxx 开头的方法;如果是 v2.0 事件,则注册服务器时需要查找并使用 onP2xxxx 开头的方法。 例如,使用 onP2MessageReceiveV1 注册接收消息事件回调时,P2便对应的是 v2.0 版本事件结构。

  3. 通过 new Client.Builder() 初始化长连接客户端,必填参数为应用的 APP_ID 和 APP_SECRET,需登录开发者后台,在应用详情页的 凭证与基础信息 > 应用凭证 区域获取。

  4. 可选参数传入 EVENT_HANDLER,同时可设置日志级别,可在项目的日志框架(log4j2、logback等)配置中修改。

  5. 通过 cli.start() 启动客户端,如连接成功,控制台会打印 "connected to wss://xxxxx",主线程将阻塞,直到进程结束。

Node.js

安装 SDK

版本需大于等于1.24.0

// 用自己项目中的包管理工具,如 yarn
yarn add @larksuiteoapi/node-sdk

示例代码

js
import * as Lark from '@larksuiteoapi/node-sdk';
const baseConfig = {
  appId: 'xxx',
  appSecret: 'xxx'
}
const client = new Lark.Client(baseConfig);
const wsClient = new Lark.WSClient({...baseConfig, loggerLevel: Lark.LoggerLevel.debug});
wsClient.start({
  // 处理「接收消息」事件,事件类型为 im.message.receive_v1
  eventDispatcher: new Lark.EventDispatcher({}).register({
    'im.message.receive_v1': async (data) => {
      const {
        message: { chat_id, content}
      } = data;
      // 示例操作:接收消息后,调用「发送消息」API 进行消息回复。
      await client.im.v1.message.create({
        params: {
          receive_id_type: "chat_id"
        },
        data: {
          receive_id: chat_id,
          content: Lark.messageCard.defaultCard({
            title: `回复: ${JSON.parse(content).text}`,
            content: '新年好'
          }),
          msg_type: 'interactive'
        }
      });
    }
  })
});

代码实现说明:

  1. 初始化 WSClient。

    1. 在 baseConfig 中,传入应用的 appId、appSecret。获取方式:登录开发者后台,在应用详情页的 凭证与基础信息 页面内,获取应用凭证(包括 App ID 和 App Secret)。

    2. 可选添加 domain 等其他参数,domain 默认为 open.feishu.cn

      Client 支持配置的参数参考调用服务端 API 中的 步骤一:创建并配置 API Client 章节。

  2. 基于 Client 完成初始化时,实例上的 start 方法用于启动长连接客户端。

    其中,start 函数中的 eventDispatcher 参数接收 EventDispatcher 实例,长连接客户端启动成功后,eventDispatcher.register 注册的相关事件 handler 会被执行。

    如下示例代码部分,需要将 'im.message.receive_v1' 替换为需要处理的事件的 事件类型事件类型 可参考指定事件的文档,例如,查阅接收消息事件文档可知,该事件的事件类型为 im.message.receive_v1

    javascript
    wsClient.start({
      eventDispatcher: new Lark.EventDispatcher({}).register({
        'im.message.receive_v1': async (data) => {
          const {
            message: { chat_id, content}
          } = data;

    如成功启动长连接,会打印如下信息。

步骤二:设置订阅方式

  1. 启动本地长连接客户端。

    具体操作参考上述 步骤一:集成 SDK

  2. 登录开发者后台,选择一个企业自建应用。

    目前长连接模式不支持商店应用。

  3. 进入 事件与回调 > 事件配置 页面。

  4. 编辑订阅方式,选择 使用长连接接收事件,并保存。

    WARNING

    必须确保本地客户端启动正常,有长连接在线的情况下,才能保存成功。

常见问题

配置过程中如遇问题,可参考常见问题排查解决。

内容来源:飞书开放平台 · 自动爬取整理