飞书开放接口SDK
旨在让开发者便捷地调用飞书开放API、处理订阅的消息事件、处理服务端推送的卡片行为。
目录
安装
go get -u github.com/larksuite/oapi-sdk-go/v3@v3.3.1
API客户端
开发者在调用API前,需要先创建一个API客户端,然后才可以基于API客户端发起API调用。
创建API客户端
- 对于自建应用,可使用下面代码来创建一个API客户端
var client = lark.NewClient("appID", "appSecret") // 默认配置为自建应用
- 对于商店应用,需在创建API客户端时,使用lark.WithMarketplaceApp方法指定AppType为商店应用。商店应用开发指南可点击这里查看
var client = lark.NewClient("appID", "appSecret",lark.WithMarketplaceApp()) // 设置App为商店应用
配置API客户端
创建API客户端时,可对API客户端进行一定的配置,比如我们可以在创建API客户端时设置日志级别、设置http请求超时时间等等:
var client = lark.NewClient("appID", "appSecret",
lark.WithLogLevel(larkcore.LogLevelDebug),
lark.WithReqTimeout(3*time.Second),
lark.WithEnableTokenCache(true),
lark.WithHelpdeskCredential("id", "token"),
lark.WithHttpClient(http.DefaultClient))
每个配置选项的具体含义,如下表格:
配置选项 | 配置方式 | 描述 |
---|---|---|
AppType
|
lark.WithMarketplaceApp()
| 设置App类型为商店应用,ISV开发者必须要设置该选项。 |
LogLevel
|
lark.WithLogLevel(logLevel larkcore.LogLevel)
|
设置API客户端的日志输出级别(默认为Info级别),枚举值如下:
|
Logger
|
lark.WithLogger(logger larkcore.Logger)
|
设置API客户端的日志器,默认日志输出到标准输出。
开发者可通过实现下面的Logger接口,来设置自定义的日志器:
|
LogReqAtDebug
|
lark.WithLogReqAtDebug(printReqRespLog bool)
|
设置是否开启Http请求参数和响应参数的日志打印开关;开启后,在debug模式下会打印http请求和响应的headers、body等信息。
在排查问题时,开启该选项,有利于问题的排查。 |
BaseUrl
|
lark.WithOpenBaseUrl(baseUrl string)
|
设置飞书域名,默认为FeishuBaseUrl,可用域名列表为:
|
TokenCache
|
lark.WithTokenCache(cache larkcore.Cache)
|
设置token缓存器,用来缓存token和appTicket,默认实现为内存。
如开发者想要定制token缓存器,需实现下面Cache接口:
对于 ISV 开发者来说,如需要 SDK 来缓存 appTicket,需要实现该接口,实现提供分布式缓存。 |
EnableTokenCache
|
lark.WithEnableTokenCache(enableTokenCache bool)
|
设置是否启用 TenantAccessToken 的自动获取与缓存。
默认启用,如需关闭可传递 false。 |
HelpDeskId、HelpDeskToken
|
lark.WithHelpdeskCredential(helpdeskID, helpdeskToken string)
| 此选项仅在调用服务台业务的 API 时需要配置。 |
ReqTimeout
|
lark.WithReqTimeout(time time.Duration)
| 设置 SDK 内置的 Http Client 的请求超时时间,默认为0表示永不超时。 |
HttpClient
|
lark.WithHttpClient(httpClient larkcore.HttpClient)
|
设置 HttpClient,用于替换 SDK 提供的默认实现。
开发者可通过实现以下 HttpClient 接口来设置自定义的 HttpClient:
|
API调用
创建完 API Client 后,我们可以使用 Client.业务域.资源.方法名称
来定位具体的 API 方法,然后对特定的 API 发起调用。
商店应用开发指南请 点击此处查看
飞书开放平台开放的所有 API 列表,请点击此处查看
基本用法
以下示例我们通过 client 调用文档业务的 Create 方法,创建一个文档:
import (
"context"
"fmt"
"github.com/larksuite/oapi-sdk-go/v3"
"github.com/larksuite/oapi-sdk-go/v3/core"
"github.com/larksuite/oapi-sdk-go/v3/service/docx/v1"
)
func main() {
// 创建 client
client := lark.NewClient("appID", "appSecret")
// 发起请求
resp, err := client.Docx.Document.Create(context.Background(), larkdocx.NewCreateDocumentReqBuilder().
Body(larkdocx.NewCreateDocumentReqBodyBuilder().
FolderToken("token").
Title("title").
Build()).
Build())
//处理错误
if err != nil {
// 处理err
return
}
// 服务端错误处理
if !resp.Success() {
fmt.Println(resp.Code, resp.Msg, resp.RequestId())
return
}
// 业务数据处理
fmt.Println(larkcore.Prettify(resp.Data))
}
更多 API 调用示例:./sample/api/im.go
设置请求选项
开发者在每次发起 API 调用时,可以设置请求级别的一些参数,比如传递 UserAccessToken、自定义 Headers 等:
import (
"context"
"fmt"
"net/http"
"github.com/larksuite/oapi-sdk-go/v3"
"github.com/larksuite/oapi-sdk-go/v3/core"
"github.com/larksuite/oapi-sdk-go/v3/service/docx/v1"
)
func main() {
// 创建client
client := lark.NewClient("appID", "appSecret")
// 自定义请求headers
header := make(http.Header)
header.Add("k1", "v1")
header.Add("k2", "v2")
// 发起请求
resp, err := client.Docx.Document.Create(context.Background(), larkdocx.NewCreateDocumentReqBuilder().
Body(larkdocx.NewCreateDocumentReqBodyBuilder().
FolderToken("token").
Title("title").
Build(),
).
Build(),
larkcore.WithHeaders(header), // 设置自定义headers
)
//处理错误
if err != nil {
// 处理err
return
}
// 服务端错误处理
if !resp.Success() {
fmt.Println(resp.Code, resp.Msg, resp.RequestId())
return
}
// 业务数据处理
fmt.Println(larkcore.Prettify(resp.Data))
}
下表展示了所有请求级别可设置的选项:
配置选项 | 配置方式 | 描述 |
---|---|---|
Header
|
larkcore.WithHeaders(header http.Header)
| 设置自定义请求头,开发者可在发起请求时,这些请求头会被透传到飞书开放平台服务端。 |
UserAccessToken
|
larkcore.WithUserAccessToken(userAccessToken string)
| 设置用户token,当开发者需要以用户身份发起调用时,需要设置该选项的值。 |
TenantAccessToken
|
larkcore.WithTenantAccessToken(tenantAccessToken string)
| 设置租户 token,当开发者自己维护租户 token 时(即创建Client时EnableTokenCache设置为了false),需通过该选项传递租户 token。 |
TenantKey
|
larkcore.WithTenantKey(tenantKey string)
| 设置租户 key,当开发者开发商店应用时,必须设置该选项。 |
RequestId
|
larkCore.WithRequestId(requestId string)
| 设置请求 ID,用于唯一标识请求,该 ID 会被传递到飞书开放平台服务端。 |
原生API调用方式
对于一些旧版本的开放接口,无法生成结构化的 API,导致 SDK 无法提供结构化的使用方式,此时可以使用原生模式进行调用:
import (
"context"
"fmt"
"github.com/larksuite/oapi-sdk-go/v3"
"github.com/larksuite/oapi-sdk-go/v3/core"
"net/http"
"os"
)
func main() {
// 创建 API Client
var appID, appSecret = os.Getenv("APP_ID"), os.Getenv("APP_SECRET")
var cli = lark.NewClient(appID, appSecret)
// 发起请求
resp, err := cli.Do(context.Background(),
&larkcore.ApiReq{
HttpMethod: http.MethodGet,
ApiPath: "https://open.feishu.cn/open-apis/contact/v3/users/:user_id",
Body: nil,
QueryParams: larkcore.QueryParams{"user_id_type": []string{"open_id"}},
PathParams: larkcore.PathParams{"user_id": "ou_c245b0a7dff2725cfa2fb104f8b48b9d"},
SupportedAccessTokenTypes: []larkcore.AccessTokenType{larkcore.AccessTokenTypeUser},
},
)
// 错误处理
if err != nil {
fmt.Println(err)
return
}
// 获取请求 ID
fmt.Println(resp.RequestId())
// 处理请求结果
fmt.Println(resp.StatusCode) // http 状态码
fmt.Println(resp.Header) // http 头
fmt.Println(string(resp.RawBody)) // http 正文,二进制数据
}
更多 API 调用示例:./sample/callrawapi/api.go
处理消息事件回调
关于消息订阅相关的知识,可以点击这里查看
飞书开放平台开放的所有事件列表,可点击这里查看
基本用法
开发者订阅消息事件后,可以使用下面代码,对飞书开放平台推送的消息事件进行处理,以下代码基于 go-sdk 原生 http server 启动一个 httpServer:
import (
"context"
"fmt"
"net/http"
"github.com/larksuite/oapi-sdk-go/v3/core"
"github.com/larksuite/oapi-sdk-go/v3/event"
"github.com/larksuite/oapi-sdk-go/v3/event/dispatcher"
"github.com/larksuite/oapi-sdk-go/v3/core/httpserverext"
"github.com/larksuite/oapi-sdk-go/v3/service/im/v1"
)
func main() {
// 注册消息处理器
handler := dispatcher.NewEventDispatcher("verificationToken", "eventEncryptKey").OnP2MessageReceiveV1(func(ctx context.Context, event *larkim.P2MessageReceiveV1) error {
// 处理消息 event,这里简单打印消息的内容
fmt.Println(larkcore.Prettify(event))
fmt.Println(event.RequestId())
return nil
}).OnP2MessageReadV1(func(ctx context.Context, event *larkim.P2MessageReadV1) error {
// 处理消息 event,这里简单打印消息的内容
fmt.Println(larkcore.Prettify(event))
fmt.Println(event.RequestId())
return nil
})
// 注册 http 路由
http.HandleFunc("/webhook/event", httpserverext.NewEventHandlerFunc(handler, larkevent.WithLogLevel(larkcore.LogLevelDebug)))
// 启动 http 服务
err := http.ListenAndServe(":9999", nil)
if err != nil {
panic(err)
}
}
其中 NewEventDispatcher 方法的参数用于签名验证和消息解密,默认可以传递为空串;但是如果开发者的应用在控制台的【事件订阅】里面开启了加密,则必须传递控制台上提供的值。
需要注意的是注册处理器时,比如使用 OnP2MessageReceiveV1 注册接受消息事件回调时,其中的 P2 为消息协议版本,当前飞书开放平台存在两种消息协议,分别为 1.0 和 2.0。
如下图开发者在注册消息处理器时,需从事件列表中查看自己需要的是哪种协议的事件。 如果是 1.0 的消息协议,则注册处理器时,需要找以 OnP1xxxx 开头的。如果是 2.0 的消息协议,则注册处理器时,需要找以 OnP2xxxx 开头的。
更多事件订阅示例:./sample/event/event.go
消息处理器内给对应租户发消息
针对 ISV 开发者,如果想在消息处理器内给对应租户的用户发送消息,则需先从消息事件内获取租户 key,然后使用下面方式调用消息 API 进行消息发送:
import (
"context"
"fmt"
lark "github.com/larksuite/oapi-sdk-go/v3"
"net/http"
"os"
"github.com/larksuite/oapi-sdk-go/v3/core"
"github.com/larksuite/oapi-sdk-go/v3/core/httpserverext"
"github.com/larksuite/oapi-sdk-go/v3/event"
"github.com/larksuite/oapi-sdk-go/v3/event/dispatcher"
"github.com/larksuite/oapi-sdk-go/v3/service/im/v1"
)
func main() {
// 创建 API 客户端
var appID, appSecret = os.Getenv("APP_ID"), os.Getenv("APP_SECRET")
var cli = lark.NewClient(appID, appSecret, lark.WithLogReqAtDebug(true), lark.WithLogLevel(larkcore.LogLevelDebug))
// 注册消息处理器
handler := dispatcher.NewEventDispatcher("verificationToken", "eventEncryptKey").OnP2MessageReceiveV1(func(ctx context.Context, event *larkim.P2MessageReceiveV1) error {
// 处理消息事件,这里简单打印消息的内容
fmt.Println(larkcore.Prettify(event))
fmt.Println(event.RequestId())
// 获取租户 key 并发送消息
tenantKey := event.TenantKey()
// ISV 给指定租户发送消息
resp, err := cli.Im.Message.Create(context.Background(), larkim.NewCreateMessageReqBuilder().
ReceiveIdType(larkim.ReceiveIdTypeOpenId).
Body(larkim.NewCreateMessageReqBodyBuilder().
MsgType(larkim.MsgTypePost).
ReceiveId("ou_c245b0a7dff2725cfa2fb104f8b48b9d").
Content("text").
Build()).
Build(), larkcore.WithTenantKey(tenantKey))
// 处理发送结果,resp,err
fmt.Println(resp, err)
return nil
})
// 注册 http 路由
http.HandleFunc("/webhook/event", httpserverext.NewEventHandlerFunc(handler, larkevent.WithLogLevel(larkcore.LogLevelDebug)))
// 启动 http 服务
err := http.ListenAndServe(":9999", nil)
if err != nil {
panic(err)
}
}
更多事件订阅示例:./sample/event/event.go
集成Gin框架
如果开发者当前应用使用的是 Gin Web 框架,并且不想使用 Go-Sdk 提供的原生 Http Server,则可使用以下方式,将当前应用的 Gin 服务与 SDK 集成。
要将 SDK 集成到已有的 Gin 框架中,开发者需要引入集成包 oapi-sdk-gin
安装集成包
go get -u github.com/larksuite/oapi-sdk-gin
集成示例
import (
"context"
"fmt"
"github.com/gin-gonic/gin"
"github.com/larksuite/oapi-sdk-gin"
"github.com/larksuite/oapi-sdk-go/v3/card"
"github.com/larksuite/oapi-sdk-go/v3/core"
"github.com/larksuite/oapi-sdk-go/v3/event/dispatcher"
"github.com/larksuite/oapi-sdk-go/v3/service/contact/v3"
"github.com/larksuite/oapi-sdk-go/v3/service/im/v1"
)
func main() {
// 注册消息处理器
handler := dispatcher.NewEventDispatcher("verificationToken", "eventEncryptKey").OnP2MessageReceiveV1(func(ctx context.Context, event *larkim.P2MessageReceiveV1) error {
fmt.Println(larkcore.Prettify(event))
fmt.Println(event.RequestId())
return nil
}).OnP2MessageReadV1(func(ctx context.Context, event *larkim.P2MessageReadV1) error {
fmt.Println(larkcore.Prettify(event))
fmt.Println(event.RequestId())
return nil
}).OnP2UserCreatedV3(func(ctx context.Context, event *larkcontact.P2UserCreatedV3) error {
fmt.Println(larkcore.Prettify(event))
fmt.Println(event.RequestId())
return nil
})
...
// 在已有的 Gin 实例上注册消息处理路由
gin.POST("/webhook/event", sdkginext.NewEventHandlerFunc(handler))
}
集成hertz框架
处理卡片行为回调
关于卡片行为相关的知识,请点击这里查看
基本用法
开发者配置消息卡片回调地址后,可以使用以下代码处理飞书开放平台推送的卡片行为。以下代码基于go-sdk原生http server启动一个httpServer:
import (
"context"
"fmt"
"net/http"
"github.com/larksuite/oapi-sdk-go/v3/card"
"github.com/larksuite/oapi-sdk-go/v3/core"
"github.com/larksuite/oapi-sdk-go/v3/core/httpserverext"
)
func main() {
// 创建卡片处理器
cardHandler := larkcard.NewCardActionHandler("v", "", func(ctx context.Context, cardAction *larkcard.CardAction) (interface{}, error) {
// 处理卡片行为,这里简单打印卡片内容
fmt.Println(larkcore.Prettify(cardAction))
fmt.Println(cardAction.RequestId())
// 无返回值示例
return nil, nil
})
// 注册处理器
http.HandleFunc("/webhook/card", httpserverext.NewCardActionHandlerFunc(cardHandler, larkevent.WithLogLevel(larkcore.LogLevelDebug)))
// 启动 http 服务
err := http.ListenAndServe(":9999", nil)
if err != nil {
panic(err)
}
}
如上示例,如果不需要在处理器内返回业务结果给飞书服务端,则直接使用这种无返回值用法。
更多卡片行为处理示例:./sample/card/card.go
返回卡片消息
如果开发者需要在卡片处理器内同步返回用于更新消息卡片的消息体,则可使用以下方法进行处理:
import (
"context"
"fmt"
"net/http"
"github.com/larksuite/oapi-sdk-go/v3/card"
"github.com/larksuite/oapi-sdk-go/v3/core"
"github.com/larksuite/oapi-sdk-go/v3/core/httpserverext"
)
func main() {
// 创建卡片处理器
cardHandler := larkcard.NewCardActionHandler("v", "", func(ctx context.Context, cardAction *larkcard.CardAction) (interface{}, error) {
fmt.Println(larkcore.Prettify(cardAction))
fmt.Println(cardAction.RequestId())
// 创建卡片信息
messageCard := larkcard.NewMessageCard().
Config(config).
Header(header).
Elements([]larkcard.MessageCardElement{divElement, processPersonElement}).
CardLink(cardLink).
Build()
return messageCard, nil
})
// 注册处理器
http.HandleFunc("/webhook/card", httpserverext.NewCardActionHandlerFunc(cardHandler, larkevent.WithLogLevel(larkcore.LogLevelDebug)))
// 启动http服务
err := http.ListenAndServe(":9999", nil)
if err != nil {
panic(err)
}
}
更多卡片行为处理示例:./sample/card/card.go
返回自定义消息
如果开发者需要在卡片处理器内返回自定义内容,可以使用以下方式进行处理:
import (
"context"
"fmt"
"net/http"
"github.com/larksuite/oapi-sdk-go/v3/card"
"github.com/larksuite/oapi-sdk-go/v3/core"
"github.com/larksuite/oapi-sdk-go/v3/core/httpserverext"
)
func main() {
// 创建卡片处理器
cardHandler := larkcard.NewCardActionHandler("v", "", func(ctx context.Context, cardAction *larkcard.CardAction) (interface{}, error) {
fmt.Println(larkcore.Prettify(cardAction))
fmt.Println(cardAction.RequestId())
// 创建 http body
body := make(map[string]interface{})
body["content"] = "hello"
i18n := make(map[string]string)
i18n["zh_cn"] = "你好"
i18n["en_us"] = "hello"
i18n["ja_jp"] = "こんにちは"
body["i18n"] = i18n
// 创建自定义消息:http状态码,body内容
resp := &larkcard.CustomResp{
StatusCode: 400,
Body: body,
}
return resp, nil
})
// 注册处理器
http.HandleFunc("/webhook/card", httpserverext.NewCardActionHandlerFunc(cardHandler, larkevent.WithLogLevel(larkcore.LogLevelDebug)))
// 启动 http 服务
err := http.ListenAndServe(":9999", nil)
if err != nil {
panic(err)
}
}
更多卡片行为处理示例:./sample/card/card.go
在卡片行为处理器内向对应租户发送消息
对于 ISV 开发者,如果想在卡片行为处理器内向对应租户的用户发送消息,需要先从卡片行为中获取租户 key,然后使用以下方式调用消息 API 进行消息发送:
import (
"context"
"fmt"
"net/http"
"github.com/larksuite/oapi-sdk-go/v3/card"
"github.com/larksuite/oapi-sdk-go/v3/core"
"github.com/larksuite/oapi-sdk-go/v3/core/httpserverext"
)
func main() {
// 创建卡片处理器
cardHandler := larkcard.NewCardActionHandler("v", "", func(ctx context.Context, cardAction *larkcard.CardAction) (interface{}, error) {
// 处理 cardAction,这里简单打印卡片内容
fmt.Println(larkcore.Prettify(cardAction))
fmt.Println(cardAction.RequestId())
// 获取租户 key 并发送消息
tenanKey := cardAction.TenantKey
// ISV 向指定租户发送消息
resp, err := client.Im.Message.Create(context.Background(), larkim.NewCreateMessageReqBuilder().
ReceiveIdType(larkim.ReceiveIdTypeOpenId).
Body(larkim.NewCreateMessageReqBodyBuilder().
MsgType(larkim.MsgTypePost).
ReceiveId("ou_c245b0a7dff2725cfa2fb104f8b48b9d").
Content("text").
Build(), larkcore.WithTenantKey(tenanKey)).
Build())
// 处理发送结果,resp,err
return nil, nil
})
// 注册处理器
http.HandleFunc("/webhook/card", httpserverext.NewCardActionHandlerFunc(cardHandler, larkevent.WithLogLevel(larkcore.LogLevelDebug)))
// 启动 http 服务
err := http.ListenAndServe(":9999", nil)
if err != nil {
panic(err)
}
}
更多卡片行为处理示例:./sample/card/card.go
集成 Gin 框架
如果开发者当前应用使用的是 Gin Web 框架,并且不想使用 Go-SDK 提供的原生 HTTP Server,可以使用以下方式将当前应用的 Gin 服务与 SDK 集成。
要将 SDK 集成到现有的 Gin 框架中,开发者需要引入集成包 oapi-sdk-gin
安装集成包
go get -u github.com/larksuite/oapi-sdk-gin
集成示例
import (
"context"
"fmt"
"github.com/gin-gonic/gin"
"github.com/larksuite/oapi-sdk-gin"
"github.com/larksuite/oapi-sdk-go/v3/card"
"github.com/larksuite/oapi-sdk-go/v3/core"
)
func main() {
// 创建卡片处理器
cardHandler := larkcard.NewCardActionHandler("v", "", func(ctx context.Context, cardAction *larkcard.CardAction) (interface{}, error) {
fmt.Println(larkcore.Prettify(cardAction))
fmt.Println(cardAction.RequestId())
return nil, nil
})
...
// 在现有的 Gin 实例上注册卡片处理路由
gin.POST("/webhook/card", sdkginext.NewCardActionHandlerFunc(cardHandler))
...
}
集成 Hertz 框架
扩展示例
我们还基于 SDK 封装了常用的 API 组合调用及业务场景示例,如:
- 消息
- 通讯录
- 多维表格
- 电子表格
- 教程
更多示例可参考:https://github.com/larksuite/oapi-sdk-go-demo
加入答疑群
许可证
采用 MIT 许可证