gitee.com/larksuite/oapi-sdk-go/v3@v3.0.3/README.zh.md (about)

     1  # 飞书开放接口SDK
     2  
     3  旨在让开发者便捷的调用飞书开放API、处理订阅的消息事件、处理服务端推送的卡片行为。
     4  
     5  ## 目录
     6  
     7  
     8  <!-- toc -->
     9  
    10  - [安装](#安装)
    11  - [API Client](#api-client)
    12      - [创建API Client](#创建api-client)
    13      - [配置API Client](#配置api-client)
    14  
    15  - [API调用](#api调用)
    16      - [基本用法](#基本用法)
    17      - [设置请求选项](#设置请求选项)
    18      - [原生API调用方式](#原生api调用方式)
    19  
    20  - [处理消息事件回调](#处理消息事件回调)
    21      - [基本用法](#基本用法-1)
    22      - [消息处理器内给对应租户发消息](#消息处理器内给对应租户发消息)
    23      - [集成gin框架](#集成gin框架)
    24          - [安装集成包](#安装集成包)
    25          - [集成示例](#集成示例)
    26  
    27  - [处理卡片行为回调](#处理卡片行为回调)
    28      - [基本用法](#基本用法-2)
    29      - [返回卡片消息](#返回卡片消息)
    30      - [返回自定义消息](#返回自定义消息)
    31      - [卡片行为处理器内给对应租户发消息](#卡片行为处理器内给对应租户发消息)
    32      - [集成gin框架](#集成gin框架)
    33          - [安装集成包](#安装集成包)
    34          - [集成示例](#集成示例)
    35  
    36  <!-- tocstop -->
    37  
    38  ## 安装
    39  
    40  ```go
    41  go get -u gitee.com/larksuite/oapi-sdk-go/v3@v3.0.2
    42  ```
    43  
    44  ## API Client
    45  
    46  开发者在调用 API 前,需要先创建一个 API Client,然后才可以基于 API Client 发起 API 调用。
    47  
    48  ### 创建API Client
    49  
    50  - 对于自建应用,可使用下面代码来创建一个 API Client
    51  
    52  ```go
    53  var client = lark.NewClient("appID", "appSecret") // 默认配置为自建应用
    54  ```
    55  
    56  - 对于商店应用,需在创建 API Client 时,使用 lark.WithMarketplaceApp 方法指定 AppType 为商店应用
    57  
    58  ```go
    59  var client = lark.NewClient("appID", "appSecret",lark.WithMarketplaceApp()) // 设置App为商店应用
    60  ```
    61  
    62  ### 配置API Client
    63  
    64  创建 API Client 时,可对 API Client 进行一定的配置,比如我们可以在创建 API Client 时设置日志级别、设置 http 请求超时时间等等:
    65  
    66  ```go
    67  var client = lark.NewClient("appID", "appSecret",
    68      lark.WithLogLevel(larkcore.LogLevelDebug),
    69      lark.WithReqTimeout(3*time.Second),
    70      lark.WithEnableTokenCache(true),
    71      lark.WithHelpdeskCredential("id", "token"),
    72      lark.WithHttpClient(http.DefaultClient))
    73  ```
    74  
    75  每个配置选项的具体含义,如下表格:
    76  
    77  <table>
    78    <thead align=left>
    79      <tr>
    80        <th>
    81          配置选项
    82        </th>
    83        <th>
    84          配置方式
    85        </th>
    86         <th>
    87          描述
    88        </th>
    89      </tr>
    90    </thead>
    91    <tbody align=left valign=top>
    92      <tr>
    93            <th>
    94              <code>AppType</code>
    95            </th>
    96            <td>
    97              <code>lark.WithMarketplaceApp()</code>
    98            </td>
    99            <td>
   100      设置 App 类型为 商店应用 ,ISV 开发者必须要设置该选项。
   101            </td>
   102      </tr>
   103      <tr>
   104        <th>
   105          <code>LogLevel</code>
   106        </th>
   107        <td>
   108          <code>lark.WithLogLevel(logLevel larkcore.LogLevel)</code>
   109        </td>
   110        <td>
   111  设置 API Client 的日志输出级别(默认为 Info 级别),枚举值如下:
   112  
   113  - LogLevelDebug
   114  - LogLevelInfo
   115  - LogLevelWarn
   116  - LogLevelError
   117  
   118  </td>
   119  </tr>
   120  
   121  <tr>
   122        <th>
   123          <code>Logger</code>
   124        </th>
   125        <td>
   126          <code>lark.WithLogger(logger larkcore.Logger)</code>
   127        </td>
   128        <td>
   129  设置API Client的日志器,默认日志输出到标准输出。
   130  
   131  开发者可通过实现下面的 Logger 接口,来设置自定义的日志器:
   132  
   133  ```go
   134  type Logger interface {
   135      Debug(context.Context, ...interface{})
   136      Info(context.Context, ...interface{})
   137      Warn(context.Context, ...interface{})
   138      Error(context.Context, ...interface{})
   139  }
   140  ```
   141  
   142  </td>
   143  </tr>
   144  
   145  <tr>
   146        <th>
   147          <code>LogReqAtDebug</code>
   148        </th>
   149        <td>
   150          <code>lark.WithLogReqAtDebug(printReqRespLog bool)</code>
   151        </td>
   152        <td>
   153  设置是否开启 Http 请求参数和响应参数的日志打印开关;开启后,在 debug 模式下会打印 http 请求和响应的 headers,body 等信息。
   154  
   155  在排查问题时,开启该选项,有利于问题的排查。
   156  
   157  </td>
   158  </tr>
   159  
   160  
   161  <tr>
   162        <th>
   163          <code>BaseUrl</code>
   164        </th>
   165        <td>
   166          <code>lark.WithOpenBaseUrl(baseUrl string)</code>
   167        </td>
   168        <td>
   169  设置飞书域名,默认为FeishuBaseUrl,可用域名列表为:
   170  
   171  ```go
   172  // 飞书域名
   173  var FeishuBaseUrl = "https://open.feishu.cn"
   174  
   175  // Lark域名
   176  var LarkBaseUrl = "https://open.larksuite.com"
   177  ```
   178  
   179  </td>
   180  </tr>
   181  
   182  <tr>
   183        <th>
   184          <code>TokenCache</code>
   185        </th>
   186        <td>
   187          <code>lark.WithTokenCache(cache larkcore.Cache)</code>
   188        </td>
   189        <td>
   190  设置 token 缓存器,用来缓存 token 和 appTicket, 默认实现为内存。
   191  
   192  如开发者想要定制 token 缓存器,需实现下面 Cache 接口:
   193  
   194  ```go
   195  type Cache interface {
   196    Set(ctx context.Context, key string, value string, expireTime time.Duration) error
   197    Get(ctx context.Context, key string) (string, error)
   198  }
   199  ```
   200  
   201  对于 ISV 开发者来说,如需要 SDK 来缓存 appTicket,需要实现该接口,实现提供分布式缓存。
   202  
   203  </td>
   204  </tr>
   205  
   206  
   207  <tr>
   208        <th>
   209          <code>EnableTokenCache</code>
   210        </th>
   211        <td>
   212          <code>lark.WithEnableTokenCache(enableTokenCache bool)</code>
   213        </td>
   214        <td>
   215  设置是否开启 TenantAccessToken 的自动获取与缓存。
   216  
   217  默认开启,如需要关闭可传递 false。
   218  </td>
   219  </tr>
   220  
   221  <tr>
   222        <th>
   223          <code>HelpDeskId、HelpDeskToken</code>
   224        </th>
   225        <td>
   226          <code>lark.WithHelpdeskCredential(helpdeskID, helpdeskToken string)</code>
   227        </td>
   228        <td>
   229  该选项仅在调用服务台业务的 API 时需要配置。
   230  </td>
   231  </tr>
   232  
   233  
   234  <tr>
   235        <th>
   236          <code>ReqTimeout</code>
   237        </th>
   238        <td>
   239          <code>lark.WithReqTimeout(time time.Duration)</code>
   240        </td>
   241        <td>
   242  设置 SDK 内置的 Http Client 的请求超时时间,默认为0代表永不超时。
   243  </td>
   244  </tr>
   245  
   246  <tr>
   247        <th>
   248          <code>HttpClient</code>
   249        </th>
   250        <td>
   251          <code>lark.WithHttpClient(httpClient larkcore.HttpClient)</code>
   252        </td>
   253        <td>
   254  设置 HttpClient,用于替换 SDK 提供的默认实现。
   255  
   256  开发者可通过实现下面的 HttpClient 接口来设置自定义的 HttpClient:
   257  
   258  ```go
   259  type HttpClient interface {
   260    Do(*http.Request) (*http.Response, error)
   261  }
   262  
   263  ```
   264  
   265  </td>
   266  </tr>
   267  
   268    </tbody>
   269  </table>
   270  
   271  ## API调用
   272  创建完毕 API Client,我们可以使用 ``Client.业务域.资源.方法名称`` 来定位具体的 API 方法,然后对具体的 API 发起调用。
   273  
   274  ![](doc/find_method.jpg)
   275  
   276  飞书开放平台开放的所有 API 列表,可点击[这里查看](https://open.feishu.cn/document/ukTMukTMukTM/uYTM5UjL2ETO14iNxkTN/server-api-list)
   277  
   278  ### 基本用法
   279  
   280  如下示例我们通过 client 调用文档业务的 Create 方法,创建一个文档:
   281  
   282  ``` go
   283  import (
   284  	"context"
   285  	"fmt"
   286  	"net/http"
   287  	"os"
   288  
   289  	"gitee.com/larksuite/oapi-sdk-go/v3"
   290  	"gitee.com/larksuite/oapi-sdk-go/v3/core"
   291  	"gitee.com/larksuite/oapi-sdk-go/v3/service/docx/v1"
   292  )
   293  
   294  
   295  func main() {
   296  	// 创建 client
   297  	client := lark.NewClient("appID", "appSecret")
   298  
   299  	// 发起请求
   300  	resp, err := client.Docx.Document.Create(context.Background(), larkdocx.NewCreateDocumentReqBuilder().
   301  		Body(larkdocx.NewCreateDocumentReqBodyBuilder().
   302  			FolderToken("token").
   303  			Title("title").
   304  			Build()).
   305  		Build())
   306  
   307  	//处理错误
   308  	if err != nil {
   309             // 处理err
   310             return
   311  	}
   312  
   313  	// 服务端错误处理
   314  	if !resp.Success() {
   315             fmt.Println(resp.Code, resp.Msg, resp.RequestId())
   316  	   return 
   317  	}
   318  
   319  	// 业务数据处理
   320  	fmt.Println(larkcore.Prettify(resp.Data))
   321  }
   322  ```
   323  
   324  更多 API 调用示例:[./sample/api/im.go](./sample/api/im.go)
   325  
   326  ### 设置请求选项
   327  
   328  开发者在每次发起 API 调用时,可以设置请求级别的一些参数,比如传递 UserAccessToken ,自定义 Headers 等:
   329  
   330  ```go
   331  import (
   332  	"context"
   333  	"fmt"
   334  	"net/http"
   335  	"os"
   336  
   337  	"gitee.com/larksuite/oapi-sdk-go/v3"
   338  	"gitee.com/larksuite/oapi-sdk-go/v3/core"
   339  	"gitee.com/larksuite/oapi-sdk-go/v3/service/docx/v1"
   340  )
   341  
   342  func main() {
   343  	// 创建client
   344  	client := lark.NewClient("appID", "appSecret")
   345  
   346  	// 自定义请求headers
   347  	header := make(http.Header)
   348  	header.Add("k1", "v1")
   349  	header.Add("k2", "v2")
   350  
   351  	// 发起请求
   352  	resp, err := client.Docx.Document.Create(context.Background(), larkdocx.NewCreateDocumentReqBuilder().
   353  		Body(larkdocx.NewCreateDocumentReqBodyBuilder().
   354  			FolderToken("token").
   355  			Title("title").
   356  			Build(),
   357  		).
   358  		Build(),
   359  		larkcore.WithHeaders(header), // 设置自定义headers
   360  	)
   361  
   362  	//处理错误
   363  	if err != nil {
   364  	   // 处理err
   365  	   return
   366  	}
   367  
   368  	// 服务端错误处理
   369  	if !resp.Success() {
   370  	   fmt.Println(resp.Code, resp.Msg, resp.RequestId())
   371  	   return
   372  	}
   373  
   374  	// 业务数据处理
   375  	fmt.Println(larkcore.Prettify(resp.Data))
   376  }
   377  
   378  ```
   379  
   380  如下表格,展示了所有请求级别可设置的选项:
   381  
   382  <table>
   383    <thead align=left>
   384      <tr>
   385        <th>
   386          配置选项
   387        </th>
   388        <th>
   389          配置方式
   390        </th>
   391         <th>
   392          描述
   393        </th>
   394      </tr>
   395    </thead>
   396    <tbody align=left valign=top>
   397      <tr>
   398        <th>
   399          <code>Header</code>
   400        </th>
   401        <td>
   402          <code>larkcore.WithHeaders(header http.Header)</code>
   403        </td>
   404        <td>
   405  设置自定义请求头,开发者可在发起请求时,这些请求头会被透传到飞书开放平台服务端。
   406  
   407  </td>
   408  </tr>
   409  
   410  <tr>
   411        <th>
   412          <code>UserAccessToken</code>
   413        </th>
   414        <td>
   415          <code>larkcore.WithUserAccessToken(userAccessToken string)</code>
   416        </td>
   417        <td>
   418  设置用户token,当开发者需要以用户身份发起调用时,需要设置该选项的值。
   419  
   420  </td>
   421  </tr>
   422  
   423  <tr>
   424        <th>
   425          <code>TenantAccessToken</code>
   426        </th>
   427        <td>
   428          <code>larkcore.WithTenantAccessToken(tenantAccessToken string)</code>
   429        </td>
   430        <td>
   431  设置租户 token,当开发者自己维护租户 token 时(即创建Client时EnableTokenCache设置为了false),需通过该选项传递 租户 token。
   432  
   433  </td>
   434  </tr>
   435  
   436  <tr>
   437        <th>
   438          <code>TenantKey</code>
   439        </th>
   440        <td>
   441          <code>larkcore.WithTenantKey(tenantKey string)</code>
   442        </td>
   443        <td>
   444  设置租户 key, 当开发者开发商店应用时,必须设置该选项。
   445  </td>
   446  </tr>
   447  
   448  
   449  <tr>
   450        <th>
   451          <code>RequestId</code>
   452        </th>
   453        <td>
   454          <code>larkCore.WithRequestId(requestId string)</code>
   455        </td>
   456        <td>
   457  设置请求 ID,用来做请求的唯一标识,该 ID 会被透传到飞书开放平台服务端。
   458  
   459  </td>
   460  </tr>
   461  
   462    </tbody>
   463  </table>
   464  
   465  ### 原生API调用方式
   466  
   467  有些老版本的开放接口,不能生成结构化的 API, 导致 SDK 内无法提供结构化的使用方式,这时可使用原生模式进行调用:
   468  
   469  ```go
   470  import (
   471  	"context"
   472  	"fmt"
   473  	"os"
   474  
   475  	"gitee.com/larksuite/oapi-sdk-go/v3"
   476  	"gitee.com/larksuite/oapi-sdk-go/v3/core"
   477  )
   478  
   479  func main() {
   480  	// 创建 API Client
   481  	var appID, appSecret = os.Getenv("APP_ID"), os.Getenv("APP_SECRET")
   482  	var cli = lark.NewClient(appID, appSecret, lark.WithLogReqAtDebug(true), lark.WithLogLevel(larkcore.LogLevelDebug))
   483  
   484  	// 发起请求
   485  	resp, err := cli.Do(context.Background(),
   486  		&larkcore.ApiReq{
   487  			HttpMethod:                http.MethodGet,
   488  			ApiPath:                   "https://open.feishu.cn/open-apis/contact/v3/users/:user_id",
   489  			Body:                      nil,
   490  			QueryParams:               larkcore.QueryParams{"user_id_type": []string{"open_id"}},
   491  			PathParams:                larkcore.PathParams{"user_id": "ou_c245b0a7dff2725cfa2fb104f8b48b9d"},
   492  			SupportedAccessTokenTypes: []larkcore.AccessTokenType{larkcore.AccessTokenTypeUser},
   493  		},
   494  		larkcore.WithUserAccessToken("u-3Sr1oTO4V1FWxTFTFYuFCqhk2Vs4h5IbhMG00gmw0CXh"),
   495  	)
   496  
   497  	// 错误处理
   498  	if err != nil {
   499  		fmt.Println(err)
   500  		return
   501  	}
   502  
   503  	// 获取请求 ID
   504  	fmt.Println(resp.RequestId())
   505  
   506  	// 处理请求结果
   507  	fmt.Println(resp.StatusCode)      // http status code
   508  	fmt.Println(resp.Header)          // http header
   509  	fmt.Println(string(resp.RawBody)) // http body
   510  }
   511  ```
   512  
   513  更多 API 调用示例:[./sample/callrawapi/api.go](./sample/callrawapi/api.go)
   514  
   515  ## 处理消息事件回调
   516  关于消息订阅相关的知识,可以点击[这里查看](https://open.feishu.cn/document/ukTMukTMukTM/uUTNz4SN1MjL1UzM)
   517  
   518  飞书开放平台开放的所有事件列表,可点击[这里查看](https://open.feishu.cn/document/ukTMukTMukTM/uYDNxYjL2QTM24iN0EjN/event-list)
   519  ### 基本用法
   520  
   521  开发者订阅消息事件后,可以使用下面代码,对飞书开放平台推送的消息事件进行处理,如下代码基于 go-sdk 原生 http server 启动一个 httpServer:
   522  
   523  ```go
   524  import (
   525  	"context"
   526  	"fmt"
   527  	"net/http"
   528  
   529  	"gitee.com/larksuite/oapi-sdk-go/v3/core"
   530  	"gitee.com/larksuite/oapi-sdk-go/v3/event"
   531  	"gitee.com/larksuite/oapi-sdk-go/v3/event/dispatcher"
   532  	"gitee.com/larksuite/oapi-sdk-go/v3/core/httpserverext"
   533  	"gitee.com/larksuite/oapi-sdk-go/v3/service/contact/v3"
   534  	"gitee.com/larksuite/oapi-sdk-go/v3/service/im/v1"
   535  )
   536  
   537  func main() {
   538      // 注册消息处理器
   539      handler := dispatcher.NewEventDispatcher("verificationToken", "eventEncryptKey").OnP2MessageReceiveV1(func(ctx context.Context, event *larkim.P2MessageReceiveV1) error {
   540          // 处理消息 event,这里简单打印消息的内容 
   541          fmt.Println(larkcore.Prettify(event))
   542          fmt.Println(event.RequestId())
   543          return nil
   544      }).OnP2MessageReadV1(func(ctx context.Context, event *larkim.P2MessageReadV1) error {
   545          // 处理消息 event,这里简单打印消息的内容
   546          fmt.Println(larkcore.Prettify(event))
   547          fmt.Println(event.RequestId())
   548          return nil
   549      })
   550      
   551      // 注册 http 路由
   552      http.HandleFunc("/webhook/event", httpserverext.NewEventHandlerFunc(handler, larkevent.WithLogLevel(larkcore.LogLevelDebug)))
   553      
   554      // 启动 http 服务
   555      err := http.ListenAndServe(":9999", nil)
   556      if err != nil {
   557          panic(err)
   558      }
   559  }
   560  
   561  
   562  ```
   563  
   564  其中 NewEventDispatcher 方法的参数用于签名验证和消息解密使用,默认可以传递为空串;但是如果开发者的应用在 [控制台](https://open.feishu.cn/app?lang=zh-CN) 的【事件订阅】里面开启了加密,则必须传递控制台上提供的值。
   565  
   566  ![Console](doc/console.jpeg)
   567  
   568  需要注意的是注册处理器时,比如使用 OnP2MessageReceiveV1 注册接受消息事件回调时,其中的P2为消息协议版本,当前飞书开放平台存在 [两种消息协议](https://open.feishu.cn/document/ukTMukTMukTM/uUTNz4SN1MjL1UzM#8f960a4b) ,分别为1.0和2.0。
   569  
   570  如下图开发者在注册消息处理器时,需从 [事件列表](https://open.feishu.cn/document/ukTMukTMukTM/uYDNxYjL2QTM24iN0EjN/event-list) 中查看自己需要的是哪种协议的事件。
   571  如果是1.0的消息协议,则注册处理器时,需要找以OnP1xxxx开头的。如果是2.0的消息协议,则注册处理器时,需要找以OnP2xxxx开头的。
   572  
   573  
   574  
   575  
   576  ![Console](doc/event_protocol.jpeg)
   577  
   578  更多事件订阅示例:[./sample/event/event.go](./sample/event/event.go)
   579  
   580  ## 消息处理器内给对应租户发消息
   581  针对 ISV 开发者,如果想在消息处理器内给对应租户的用户发送消息,则需先从消息事件内获取租户 key,然后使用下面方式调用消息 API 进行消息发送:
   582  
   583  ```go
   584  import (
   585  	"context"
   586  	"fmt"
   587  	"net/http"
   588  
   589  	"gitee.com/larksuite/oapi-sdk-go/v3/core"
   590  	"gitee.com/larksuite/oapi-sdk-go/v3/event"
   591  	"gitee.com/larksuite/oapi-sdk-go/v3/event/dispatcher"
   592  	"gitee.com/larksuite/oapi-sdk-go/v3/core/httpserverext"
   593  	"gitee.com/larksuite/oapi-sdk-go/v3/service/contact/v3"
   594  	"gitee.com/larksuite/oapi-sdk-go/v3/service/im/v1"
   595  )
   596  
   597  func main() {
   598      // 注册消息处理器
   599      handler := dispatcher.NewEventDispatcher("verificationToken", "eventEncryptKey").OnP2MessageReceiveV1(func(ctx context.Context, event *larkim.P2MessageReceiveV1) error {
   600          // 处理消息 event,这里简单打印消息的内容 
   601          fmt.Println(larkcore.Prettify(event))
   602          fmt.Println(event.RequestId())
   603          
   604          // 获取租户 key 并发送消息
   605          tenanKey := event.TenantKey()
   606          
   607          // ISV 给指定租户发送消息
   608          resp, err := client.Im.Message.Create(context.Background(), larkim.NewCreateMessageReqBuilder().
   609                  ReceiveIdType(larkim.ReceiveIdTypeOpenId).
   610                  Body(larkim.NewCreateMessageReqBodyBuilder().
   611                      MsgType(larkim.MsgTypePost).
   612                      ReceiveId("ou_c245b0a7dff2725cfa2fb104f8b48b9d").
   613                      Content("text").
   614                      Build(), larkcore.WithTenantKey(tenanKey)).
   615                  Build())
   616                  
   617          // 发送结果处理,resp,err
   618  		
   619          return nil
   620      })
   621      
   622      // 注册 http 路由
   623      http.HandleFunc("/webhook/event", httpserverext.NewEventHandlerFunc(handler, larkevent.WithLogLevel(larkcore.LogLevelDebug)))
   624      
   625      // 启动 http 服务
   626      err := http.ListenAndServe(":9999", nil)
   627      if err != nil {
   628          panic(err)
   629      }
   630  }
   631  
   632  ```
   633  
   634  
   635  更多事件订阅示例:[./sample/event/event.go](./sample/event/event.go)
   636  
   637  
   638  ### 集成Gin框架
   639  如果开发者当前应用使用的是 Gin Web 框架,并且不想要使用 Go-Sdk 提供的原生的 Http Server,则可使用下面方式,把当前应用的 Gin 服务与 SDK进行集成。
   640  
   641  要想把 SDK 集成已有 Gin 框架,开发者需要引入集成包 [oapi-sdk-gin](https://github.com/larksuite/oapi-sdk-gin)
   642  
   643  #### 安装集成包
   644  
   645  ```go
   646  go get -u github.com/larksuite/oapi-sdk-gin
   647  ```
   648  
   649  #### 集成示例
   650  
   651  ```go
   652  import (
   653  	"context"
   654  	"fmt"
   655  
   656  	 "github.com/gin-gonic/gin"
   657  	 "github.com/larksuite/oapi-sdk-gin"
   658  	 "gitee.com/larksuite/oapi-sdk-go/v3/card"
   659  	 "gitee.com/larksuite/oapi-sdk-go/v3/core"
   660  	 "gitee.com/larksuite/oapi-sdk-go/v3/event/dispatcher"
   661  	 "gitee.com/larksuite/oapi-sdk-go/v3/service/contact/v3"
   662  	 "gitee.com/larksuite/oapi-sdk-go/v3/service/im/v1"
   663  )
   664  
   665  func main() {
   666  	// 注册消息处理器
   667  	handler := dispatcher.NewEventDispatcher("verificationToken", "eventEncryptKey").OnP2MessageReceiveV1(func(ctx context.Context, event *larkim.P2MessageReceiveV1) error {
   668  		fmt.Println(larkcore.Prettify(event))
   669  		fmt.Println(event.RequestId())
   670  		return nil
   671  	}).OnP2MessageReadV1(func(ctx context.Context, event *larkim.P2MessageReadV1) error {
   672  		fmt.Println(larkcore.Prettify(event))
   673  		fmt.Println(event.RequestId())
   674  		return nil
   675  	}).OnP2UserCreatedV3(func(ctx context.Context, event *larkcontact.P2UserCreatedV3) error {
   676  		fmt.Println(larkcore.Prettify(event))
   677  		fmt.Println(event.RequestId())
   678  		return nil
   679  	})
   680  
   681  	...
   682  
   683  	// 在已有 Gin 实例上注册消息处理路由
   684  	gin.POST("/webhook/event", sdkginext.NewEventHandlerFunc(handler))
   685  }
   686  ```
   687  
   688  
   689  ## 处理卡片行为回调
   690  
   691  关于卡片行为相关的知识,可点击[这里查看](https://open.feishu.cn/document/ukTMukTMukTM/uczM3QjL3MzN04yNzcDN)
   692  ### 基本用法
   693  
   694  开发者配置消息卡片回调地址后,可以使用下面代码,对飞书开放平台推送的卡片行为进行处理,如下代码基于go-sdk原生http server启动一个httpServer:
   695  
   696  ```go
   697  import (
   698  	"context"
   699  	"fmt"
   700  	"net/http"
   701  
   702  	"gitee.com/larksuite/oapi-sdk-go/v3/card"
   703  	"gitee.com/larksuite/oapi-sdk-go/v3/core"
   704  	"gitee.com/larksuite/oapi-sdk-go/v3/core/httpserverext"
   705  )
   706  
   707  func main() {
   708  	// 创建 card 处理器
   709  	cardHandler := larkcard.NewCardActionHandler("v", "", func(ctx context.Context, cardAction *larkcard.CardAction) (interface{}, error) {
   710  		// 处理 cardAction, 这里简单打印卡片内容
   711  		fmt.Println(larkcore.Prettify(cardAction))
   712  	    fmt.Println(cardAction.RequestId())
   713  		// 无返回值示例
   714  		return nil, nil
   715  	})
   716  
   717  	// 注册处理器
   718  	http.HandleFunc("/webhook/card", httpserverext.NewCardActionHandlerFunc(cardHandler, larkevent.WithLogLevel(larkcore.LogLevelDebug)))
   719  
   720  	// 启动 http 服务
   721  	err := http.ListenAndServe(":9999", nil)
   722  	if err != nil {
   723  		panic(err)
   724  	}
   725  }
   726  
   727  ```
   728  
   729  如上示例,如果不需要处理器内返回业务结果给飞书服务端,则直接使用这种无返回值用法
   730  
   731  更多卡片行为处理示例:[./sample/card/card.go](./sample/card/card.go)
   732  
   733  ### 返回卡片消息
   734  
   735  如开发者需要卡片处理器内同步返回用于更新消息卡片的消息体,则可使用下面方法方式进行处理:
   736  
   737  ```go
   738  
   739  import (
   740  	"context"
   741  	"fmt"
   742  	"net/http"
   743  
   744  	"gitee.com/larksuite/oapi-sdk-go/v3/card"
   745  	"gitee.com/larksuite/oapi-sdk-go/v3/core"
   746  	"gitee.com/larksuite/oapi-sdk-go/v3/core/httpserverext"
   747  )
   748  
   749  func main() {
   750  	// 创建card处理器
   751  	cardHandler := larkcard.NewCardActionHandler("v", "", func(ctx context.Context, cardAction *larkcard.CardAction) (interface{}, error) {
   752  		fmt.Println(larkcore.Prettify(cardAction))
   753  	    fmt.Println(cardAction.RequestId())
   754  		
   755  		// 创建卡片信息
   756  		messageCard := larkcard.NewMessageCard().
   757  		Config(config).
   758  		Header(header).
   759  		Elements([]larkcard.MessageCardElement{divElement, processPersonElement}).
   760  		CardLink(cardLink).
   761  		Build()
   762  
   763  		return messageCard, nil
   764  	})
   765  
   766  	// 注册处理器
   767  	http.HandleFunc("/webhook/card", httpserverext.NewCardActionHandlerFunc(cardHandler, larkevent.WithLogLevel(larkcore.LogLevelDebug)))
   768  
   769  	// 启动http服务
   770  	err := http.ListenAndServe(":9999", nil)
   771  	if err != nil {
   772  		panic(err)
   773  	}
   774  }
   775  
   776  ```
   777  
   778  更多卡片行为处理示例:[./sample/card/card.go](./sample/card/card.go)
   779  
   780  ### 返回自定义消息
   781  
   782  如开发者需卡片处理器内返回自定义内容,则可以使用下面方式进行处理:
   783  
   784  ```go 
   785  import (
   786  	"context"
   787  	"fmt"
   788  	"net/http"
   789  
   790  	"gitee.com/larksuite/oapi-sdk-go/v3/card"
   791  	"gitee.com/larksuite/oapi-sdk-go/v3/core"
   792  	"gitee.com/larksuite/oapi-sdk-go/v3/core/httpserverext"
   793  )
   794  
   795  func main() {
   796  	// 创建 card 处理器
   797  	cardHandler := larkcard.NewCardActionHandler("v", "", func(ctx context.Context, cardAction *larkcard.CardAction) (interface{}, error) {
   798  		fmt.Println(larkcore.Prettify(cardAction))
   799  	    fmt.Println(cardAction.RequestId())
   800  		
   801  		// 创建 http body
   802  		body := make(map[string]interface{})
   803  		body["content"] = "hello"
   804  
   805  		i18n := make(map[string]string)
   806  		i18n["zh_cn"] = "你好"
   807  		i18n["en_us"] = "hello"
   808  		i18n["ja_jp"] = "こんにちは"
   809  		body["i18n"] = i18n 
   810  		
   811  		// 创建自定义消息:http状态码,body内容
   812  		resp := &larkcard.CustomResp{
   813  			StatusCode: 400,
   814  			Body:       body,
   815  		}
   816  
   817  		return resp, nil
   818  	})
   819  
   820  	// 注册处理器
   821  	http.HandleFunc("/webhook/card", httpserverext.NewCardActionHandlerFunc(cardHandler, larkevent.WithLogLevel(larkcore.LogLevelDebug)))
   822  
   823  	// 启动 http 服务
   824  	err := http.ListenAndServe(":9999", nil)
   825  	if err != nil {
   826  		panic(err)
   827  	}
   828  }
   829  
   830  ```
   831  
   832  更多卡片行为处理示例:[./sample/card/card.go](./sample/card/card.go)
   833  
   834  
   835  ### 卡片行为处理器内给对应租户发消息
   836  
   837  针对 ISV 开发者,如果想在卡片行为处理器内给对应租户的用户发送消息,则需先从卡片行为内获取租户 key ,然后使用下面方式调用消息 API 进行消息发送:
   838  
   839  
   840  ```go
   841  import (
   842  	"context"
   843  	"fmt"
   844  	"net/http"
   845  
   846  	"gitee.com/larksuite/oapi-sdk-go/v3/card"
   847  	"gitee.com/larksuite/oapi-sdk-go/v3/core"
   848  	"gitee.com/larksuite/oapi-sdk-go/v3/core/httpserverext"
   849  )
   850  
   851  func main() {
   852  	// 创建 card 处理器
   853  	cardHandler := larkcard.NewCardActionHandler("v", "", func(ctx context.Context, cardAction *larkcard.CardAction) (interface{}, error) {
   854          
   855          // 处理 cardAction, 这里简单打印卡片内容  
   856          fmt.Println(larkcore.Prettify(cardAction))
   857          fmt.Println(cardAction.RequestId())
   858  	    
   859          // 获取租户 key 并发送消息
   860          tenanKey := cardAction.TenantKey
   861          
   862          // ISV 给指定租户发送消息
   863          resp, err := client.Im.Message.Create(context.Background(), larkim.NewCreateMessageReqBuilder().
   864                  ReceiveIdType(larkim.ReceiveIdTypeOpenId).
   865                  Body(larkim.NewCreateMessageReqBodyBuilder().
   866                      MsgType(larkim.MsgTypePost).
   867                      ReceiveId("ou_c245b0a7dff2725cfa2fb104f8b48b9d").
   868                      Content("text").
   869                      Build(), larkcore.WithTenantKey(tenanKey)).
   870                  Build())
   871                  
   872          // 发送结果处理,resp,err
   873  		
   874          return nil, nil
   875  	})
   876  
   877  	// 注册处理器
   878  	http.HandleFunc("/webhook/card", httpserverext.NewCardActionHandlerFunc(cardHandler, larkevent.WithLogLevel(larkcore.LogLevelDebug)))
   879  
   880  	// 启动 http 服务
   881  	err := http.ListenAndServe(":9999", nil)
   882  	if err != nil {
   883  		panic(err)
   884  	}
   885  }
   886  
   887  ```
   888  
   889  更多卡片行为处理示例:[./sample/card/card.go](./sample/card/card.go)
   890  
   891  
   892  ### 集成gin框架
   893  
   894  如果开发者当前应用使用的是 Gin Web 框架,并且不想要使用 Go-Sdk 提供的原生的 Http Server,则可使用下面方式,把当前应用的 Gin 服务与 SDK进行集成。
   895  
   896  要想把 SDK 集成已有 Gin 框架,开发者需要引入集成包 [oapi-sdk-gin](https://github.com/larksuite/oapi-sdk-gin)
   897  
   898  #### 安装集成包
   899  
   900  ```go
   901  go get -u github.com/larksuite/oapi-sdk-gin
   902  ```
   903  
   904  #### 集成示例
   905  
   906  ```go
   907  import (
   908      "context"
   909      "fmt"
   910      
   911      "github.com/gin-gonic/gin"
   912      "github.com/larksuite/oapi-sdk-gin"
   913      "gitee.com/larksuite/oapi-sdk-go/v3/card"
   914      "gitee.com/larksuite/oapi-sdk-go/v3/core"
   915  )
   916  
   917  
   918  func main() {
   919        // 创建 card 处理器
   920        cardHandler := larkcard.NewCardActionHandler("v", "", func(ctx context.Context, cardAction *larkcard.CardAction) (interface{}, error) {
   921        fmt.Println(larkcore.Prettify(cardAction))
   922        fmt.Println(cardAction.RequestId())
   923      
   924        return nil, nil
   925        })
   926        ...
   927        // 在已有的 Gin 实例上注册卡片处理路由
   928        gin.POST("/webhook/card", sdkginext.NewCardActionHandlerFunc(cardHandler))
   929        ...
   930  }
   931  ```
   932  
   933  ## License
   934  使用 MIT
   935  
   936  
   937