admin_backend/alibaba/card_example.go
2026-01-19 18:53:24 +08:00

292 lines
9.6 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package alibaba
import (
"context"
"encoding/json"
"flag"
"fmt"
"os"
"strings"
"time"
openapi "github.com/alibabacloud-go/darabonba-openapi/v2/client"
dingtalkcard_1_0 "github.com/alibabacloud-go/dingtalk/card_1_0"
dingtalkim_1_0 "github.com/alibabacloud-go/dingtalk/im_1_0"
dingtalkoauth2_1_0 "github.com/alibabacloud-go/dingtalk/oauth2_1_0"
util "github.com/alibabacloud-go/tea-utils/v2/service"
"github.com/alibabacloud-go/tea/tea"
"github.com/google/uuid"
"github.com/open-dingtalk/dingtalk-stream-sdk-go/card"
"github.com/open-dingtalk/dingtalk-stream-sdk-go/chatbot"
"github.com/open-dingtalk/dingtalk-stream-sdk-go/client"
"github.com/open-dingtalk/dingtalk-stream-sdk-go/logger"
)
type DingTalkClient struct {
ClientID string
clientSecret string
imClient *dingtalkim_1_0.Client
oauthClient *dingtalkoauth2_1_0.Client
cardClient *dingtalkcard_1_0.Client
}
var (
dingtalkClient *DingTalkClient = nil
)
func NewDingTalkClient(clientId, clientSecret string) *DingTalkClient {
config := &openapi.Config{}
config.Protocol = tea.String("https")
config.RegionId = tea.String("central")
oauthClient, _ := dingtalkoauth2_1_0.NewClient(config)
imClient, _ := dingtalkim_1_0.NewClient(config)
cardClient, _ := dingtalkcard_1_0.NewClient(config)
return &DingTalkClient{
ClientID: clientId,
clientSecret: clientSecret,
oauthClient: oauthClient,
imClient: imClient,
cardClient: cardClient,
}
}
func (c *DingTalkClient) GetAccessToken() (string, error) {
request := &dingtalkoauth2_1_0.GetAccessTokenRequest{
AppKey: tea.String(c.ClientID),
AppSecret: tea.String(c.clientSecret),
}
response, tryErr := func() (_resp *dingtalkoauth2_1_0.GetAccessTokenResponse, _e error) {
defer func() {
if r := tea.Recover(recover()); r != nil {
_e = r
}
}()
_resp, _err := c.oauthClient.GetAccessToken(request)
if _err != nil {
return nil, _err
}
return _resp, nil
}()
if tryErr != nil {
return "", tryErr
}
return *response.Body.AccessToken, nil
}
func (c *DingTalkClient) SendCard(request *dingtalkcard_1_0.CreateAndDeliverRequest) (*dingtalkcard_1_0.CreateAndDeliverResponse, error) {
accessToken, err := c.GetAccessToken()
if err != nil {
return nil, err
}
headers := &dingtalkcard_1_0.CreateAndDeliverHeaders{}
headers.XAcsDingtalkAccessToken = tea.String(accessToken)
resp, tryErr := func() (resp *dingtalkcard_1_0.CreateAndDeliverResponse, _e error) {
defer func() {
if r := tea.Recover(recover()); r != nil {
_e = r
}
}()
result, _err := c.cardClient.CreateAndDeliverWithOptions(request, headers, &util.RuntimeOptions{})
if _err != nil {
return nil, _err
}
return result, nil
}()
if tryErr != nil {
var sdkError = &tea.SDKError{}
if _t, ok := tryErr.(*tea.SDKError); ok {
sdkError = _t
} else {
sdkError.Message = tea.String(tryErr.Error())
}
if !tea.BoolValue(util.Empty(sdkError.Code)) && !tea.BoolValue(util.Empty(sdkError.Message)) {
logger.GetLogger().Errorf("CreateAndDeliverWithOptions failed, clientId=%s, err=%+v", c.ClientID, sdkError)
}
return nil, tryErr
}
return resp, nil
}
func (c *DingTalkClient) UpdateCard(request *dingtalkcard_1_0.UpdateCardRequest) (*dingtalkcard_1_0.UpdateCardResponse, error) {
accessToken, err := c.GetAccessToken()
if err != nil {
return nil, err
}
headers := &dingtalkcard_1_0.UpdateCardHeaders{}
headers.XAcsDingtalkAccessToken = tea.String(accessToken)
resp, tryErr := func() (resp *dingtalkcard_1_0.UpdateCardResponse, _e error) {
defer func() {
if r := tea.Recover(recover()); r != nil {
_e = r
}
}()
result, _err := c.cardClient.UpdateCardWithOptions(request, headers, &util.RuntimeOptions{})
if _err != nil {
return nil, _err
}
return result, nil
}()
if tryErr != nil {
var sdkError = &tea.SDKError{}
if _t, ok := tryErr.(*tea.SDKError); ok {
sdkError = _t
} else {
sdkError.Message = tea.String(tryErr.Error())
}
if !tea.BoolValue(util.Empty(sdkError.Code)) && !tea.BoolValue(util.Empty(sdkError.Message)) {
logger.GetLogger().Errorf("UpdateCardWithOptions failed, clientId=%s, err=%+v", c.ClientID, sdkError)
}
return nil, tryErr
}
return resp, nil
}
func OnChatBotMessageReceived(ctx context.Context, data *chatbot.BotCallbackDataModel) ([]byte, error) {
content := strings.TrimSpace(data.Text.Content)
logger.GetLogger().Infof("received message: %v", content)
// 卡片模板 ID
CARD_TEMPLATE_ID := "2c278d79-fc0b-41b4-b14e-8b8089dc08e8.schema" // 该模板只用于测试使用,如需投入线上使用,请导入卡片模板 json 到自己的应用下
// 卡片公有数据非字符串类型的卡片数据参考文档https://open.dingtalk.com/document/orgapp/instructions-for-filling-in-api-card-data
cardData := &dingtalkcard_1_0.CreateAndDeliverRequestCardData{
CardParamMap: make(map[string]*string),
}
cardData.CardParamMap["title"] = tea.String("钉钉互动卡片")
cardData.CardParamMap["markdown"] = tea.String(content)
cardData.CardParamMap["submitted"] = tea.String("false")
cardData.CardParamMap["tag"] = tea.String("标签")
imGroupOpenSpaceModel := &dingtalkcard_1_0.CreateAndDeliverRequestImGroupOpenSpaceModel{
SupportForward: tea.Bool(true),
}
imGroupOpenDeliverModel := &dingtalkcard_1_0.CreateAndDeliverRequestImGroupOpenDeliverModel{
Extension: make(map[string]*string),
RobotCode: tea.String(dingtalkClient.ClientID),
}
imRobotOpenSpaceModel := &dingtalkcard_1_0.CreateAndDeliverRequestImRobotOpenSpaceModel{
SupportForward: tea.Bool(true),
}
imRobotOpenDeliverModel := &dingtalkcard_1_0.CreateAndDeliverRequestImRobotOpenDeliverModel{
Extension: make(map[string]*string),
RobotCode: tea.String(dingtalkClient.ClientID),
SpaceType: tea.String("IM_ROBOT"),
}
u, _ := uuid.NewUUID()
outTrackId := u.String()
sendCardRequest := &dingtalkcard_1_0.CreateAndDeliverRequest{
UserIdType: tea.Int32(1), // 1默认userid模式2unionId模式;
CardTemplateId: tea.String(CARD_TEMPLATE_ID),
OutTrackId: tea.String(outTrackId),
CallbackType: tea.String("STREAM"), // 采用 Stream 模式接收回调事件
CardData: cardData,
}
if data.ConversationType == "2" {
// 群聊
sendCardRequest.OpenSpaceId = tea.String(fmt.Sprintf("dtv1.card//IM_GROUP.%s", data.ConversationId))
sendCardRequest.ImGroupOpenSpaceModel = imGroupOpenSpaceModel
sendCardRequest.ImGroupOpenDeliverModel = imGroupOpenDeliverModel
} else {
// 单聊
sendCardRequest.OpenSpaceId = tea.String(fmt.Sprintf("dtv1.card//IM_ROBOT.%s", data.SenderStaffId))
sendCardRequest.ImRobotOpenSpaceModel = imRobotOpenSpaceModel
sendCardRequest.ImRobotOpenDeliverModel = imRobotOpenDeliverModel
}
// 创建并投放卡片: https://open.dingtalk.com/document/isvapp/create-and-deliver-cards
sendCardResponse, err := dingtalkClient.SendCard(sendCardRequest)
if err != nil {
logger.GetLogger().Errorf("reply card failed: %+v", sendCardResponse)
return nil, err
}
logger.GetLogger().Infof("reply card: %v %+v", outTrackId, sendCardRequest.CardData)
time.Sleep(2 * time.Second)
updateCardData := &dingtalkcard_1_0.UpdateCardRequestCardData{
CardParamMap: make(map[string]*string),
}
updateCardData.CardParamMap["tag"] = tea.String("更新后的标签")
updateOptions := &dingtalkcard_1_0.UpdateCardRequestCardUpdateOptions{
UpdateCardDataByKey: tea.Bool(true),
}
updateCardRequest := &dingtalkcard_1_0.UpdateCardRequest{
OutTrackId: tea.String(outTrackId),
CardData: updateCardData,
CardUpdateOptions: updateOptions,
}
// 更新卡片: https://open.dingtalk.com/document/orgapp/interactive-card-update-interface
updateCardResponse, err := dingtalkClient.UpdateCard(updateCardRequest)
if err != nil {
logger.GetLogger().Errorf("update card failed: %+v", updateCardResponse)
return nil, err
}
logger.GetLogger().Infof("update card: %v %+v", outTrackId, updateCardRequest.CardData)
return []byte(""), nil
}
func onCardCallback(ctx context.Context, request *card.CardRequest) (*card.CardResponse, error) {
/**
* 卡片事件回调文档https://open.dingtalk.com/document/orgapp/event-callback-card
*/
logger.GetLogger().Infof("card callback message: %v", request)
userPrivateData := make(map[string]string)
params := request.CardActionData.CardPrivateData.Params
if localInput, ok := params["local_input"]; ok && localInput != nil {
userPrivateData["priavte_input"] = localInput.(string)
userPrivateData["submitted"] = "true"
}
response := &card.CardResponse{
CardUpdateOptions: &card.CardUpdateOptions{
UpdateCardDataByKey: true,
UpdatePrivateDataByKey: true,
},
UserPrivateData: &card.CardDataDto{
CardParamMap: userPrivateData,
},
}
responseJSON, err := json.MarshalIndent(response, "", " ") // 使用 MarshalIndent 来美化输出
if err == nil {
logger.GetLogger().Infof("card callback response: \n%s", string(responseJSON))
}
return response, nil
}
func main() {
var clientId, clientSecret string
flag.StringVar(&clientId, "client_id", os.Getenv("DINGTALK_APP_CLIENT_ID"), "your-client-id")
flag.StringVar(&clientSecret, "client_secret", os.Getenv("DINGTALK_APP_CLIENT_SECRET"), "your-client-secret")
flag.Parse()
if len(clientId) == 0 || len(clientSecret) == 0 {
panic("command line options --client_id and --client_secret required")
}
logger.SetLogger(logger.NewStdTestLogger())
dingtalkClient = NewDingTalkClient(clientId, clientSecret)
cli := client.NewStreamClient(client.WithAppCredential(client.NewAppCredentialConfig(clientId, clientSecret)))
cli.RegisterChatBotCallbackRouter(OnChatBotMessageReceived)
cli.RegisterCardCallbackRouter(onCardCallback)
err := cli.Start(context.Background())
if err != nil {
panic(err)
}
defer cli.Close()
select {}
}