fix:1. 增加钉钉card客户端,增加card创建并投放卡片方法 2.增加交互卡片回调方法 3.增加钉钉建群方法,建群demo
This commit is contained in:
parent
44864cc7f0
commit
17d7b01fdf
|
|
@ -28,8 +28,11 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/alibabacloud-go/dingtalk/card_1_0"
|
||||
"github.com/alibabacloud-go/tea/tea"
|
||||
"github.com/coze-dev/coze-go"
|
||||
"github.com/gofiber/fiber/v2/log"
|
||||
"github.com/google/uuid"
|
||||
"xorm.io/builder"
|
||||
)
|
||||
|
||||
|
|
@ -43,8 +46,7 @@ type GroupConfigBiz struct {
|
|||
toolManager *tools.Manager
|
||||
conf *config.Config
|
||||
rdb *utils.Rdb
|
||||
dingtalkOauth2Client *dingtalk.Oauth2Client
|
||||
dingtalkRobotClient *dingtalk.RobotClient
|
||||
dingtalkCardClient *dingtalk.CardClient
|
||||
}
|
||||
|
||||
// NewDingTalkBotBiz
|
||||
|
|
@ -57,8 +59,7 @@ func NewGroupConfigBiz(
|
|||
reportDailyCacheImpl *impl.ReportDailyCacheImpl,
|
||||
rdb *utils.Rdb,
|
||||
toolManager *tools.Manager,
|
||||
dingtalkOauth2Client *dingtalk.Oauth2Client,
|
||||
dingtalkRobotClient *dingtalk.RobotClient,
|
||||
dingtalkCardClient *dingtalk.CardClient,
|
||||
) *GroupConfigBiz {
|
||||
return &GroupConfigBiz{
|
||||
botTools: tools.BootTools,
|
||||
|
|
@ -69,8 +70,7 @@ func NewGroupConfigBiz(
|
|||
reportDailyCacheImpl: reportDailyCacheImpl,
|
||||
rdb: rdb,
|
||||
toolManager: toolManager,
|
||||
dingtalkOauth2Client: dingtalkOauth2Client,
|
||||
dingtalkRobotClient: dingtalkRobotClient,
|
||||
dingtalkCardClient: dingtalkCardClient,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -500,16 +500,49 @@ func (g *GroupConfigBiz) handleKnowledgeV2(ctx context.Context, rec *entitys.Rec
|
|||
return
|
||||
}
|
||||
|
||||
// var DingtalkGroupCaseOwner = map[string][]string{
|
||||
// "cidwP24PLZhLVOS2dVIkEawLw==": {"付仲云", "贺泽琨", "任志远"},
|
||||
// }
|
||||
|
||||
// 未检索到匹配信息,询问是否拉群
|
||||
if !isRetrieved {
|
||||
// 获取dingtalk accessToken
|
||||
accessToken, _ := g.dingtalkOauth2Client.GetAccessToken()
|
||||
// 发送钉钉卡片
|
||||
_, err = g.dingtalkRobotClient.SendGroupMessages(accessToken, rec.UserContent.Text)
|
||||
uuid := uuid.New().String()
|
||||
_, err = g.dingtalkCardClient.CreateAndDeliver(dingtalk.AppKey{
|
||||
AppKey: "ding5wwvnf9hxeyjau7t",
|
||||
AppSecret: "FxXVlTzxrKXvJ8h-9uK0s5TjaBfOJSXumpmrHal-NmQAtku9wOPxcss0Af6WHoAK",
|
||||
}, &card_1_0.CreateAndDeliverRequest{
|
||||
CardTemplateId: tea.String("faad6d5d-726d-467f-a6ba-28c1930aa5f3.schema"),
|
||||
OutTrackId: tea.String(uuid),
|
||||
CallbackType: tea.String("STREAM"),
|
||||
CardData: &card_1_0.CreateAndDeliverRequestCardData{
|
||||
CardParamMap: map[string]*string{
|
||||
"title": tea.String("群创建提醒"),
|
||||
"content": tea.String("**确认创建群聊?**\n\n将邀请以下成员加入群聊:\n\n@张三、@李四"),
|
||||
"remark": tea.String("注:如若无需,忽略即可"),
|
||||
"button_left": tea.String("点击进群"),
|
||||
"button_left_link": tea.String(""),
|
||||
"button_right": tea.String("忽略"),
|
||||
"button_right_link": tea.String(""),
|
||||
"action_id": tea.String("create_group"),
|
||||
"_CARD_DEBUG_TOOL_ENTRY": tea.String("show"),
|
||||
},
|
||||
},
|
||||
ImGroupOpenSpaceModel: &card_1_0.CreateAndDeliverRequestImGroupOpenSpaceModel{
|
||||
SupportForward: tea.Bool(false),
|
||||
},
|
||||
OpenSpaceId: tea.String("dtv1.card//im_group.cidwP24PLZhLVOS2dVIkEawLw=="),
|
||||
ImGroupOpenDeliverModel: &card_1_0.CreateAndDeliverRequestImGroupOpenDeliverModel{
|
||||
RobotCode: tea.String("ding5wwvnf9hxeyjau7t"),
|
||||
// Recipients: []*string{
|
||||
// tea.String("17415698414368678"),
|
||||
// },
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("发送钉钉卡片失败,err: %v", err)
|
||||
}
|
||||
// entitys.ResStream(rec.Ch, "", fmt.Sprintf("已发送卡片,查询ID: %s", queryKey))
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -544,6 +577,7 @@ func (g *GroupConfigBiz) connectAndReadSSE(resp *http.Response, channel chan ent
|
|||
// 未检索到,直接返回
|
||||
dataStr := strings.TrimSpace(strings.TrimPrefix(line, "data:"))
|
||||
if dataStr != "retrieved" {
|
||||
entitys.ResStream(channel, "", fmt.Sprintf("知识库未检测到匹配信息,即将为您创建群聊解决问题?"))
|
||||
return false, nil
|
||||
}
|
||||
continue
|
||||
|
|
|
|||
|
|
@ -78,3 +78,8 @@ const (
|
|||
]
|
||||
}`
|
||||
)
|
||||
|
||||
// 交互卡片回调事件类型
|
||||
const (
|
||||
CardActionTypeCreateGroup string = "create_group" // 创建群聊
|
||||
)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,60 @@
|
|||
package dingtalk
|
||||
|
||||
import (
|
||||
"ai_scheduler/internal/config"
|
||||
errorcode "ai_scheduler/internal/data/error"
|
||||
|
||||
openapi "github.com/alibabacloud-go/darabonba-openapi/v2/client"
|
||||
card "github.com/alibabacloud-go/dingtalk/card_1_0"
|
||||
util "github.com/alibabacloud-go/tea-utils/v2/service"
|
||||
"github.com/alibabacloud-go/tea/tea"
|
||||
)
|
||||
|
||||
type CardClient struct {
|
||||
config *config.Config
|
||||
cli *card.Client
|
||||
oauth2Client *Oauth2Client
|
||||
}
|
||||
|
||||
func NewCardClient(config *config.Config, oauth2Client *Oauth2Client) (*CardClient, error) {
|
||||
cfg := &openapi.Config{
|
||||
AccessKeyId: tea.String(config.Tools.DingTalkBot.APIKey),
|
||||
AccessKeySecret: tea.String(config.Tools.DingTalkBot.APISecret),
|
||||
Protocol: tea.String("https"),
|
||||
RegionId: tea.String("central"),
|
||||
}
|
||||
c, err := card.NewClient(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &CardClient{config: config, cli: c, oauth2Client: oauth2Client}, nil
|
||||
}
|
||||
|
||||
// 创建并投放卡片
|
||||
func (c *CardClient) CreateAndDeliver(req AppKey, cardData *card.CreateAndDeliverRequest) (bool, error) {
|
||||
// 获取token
|
||||
accessToken, err := c.oauth2Client.GetAccessToken(req)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// 调用API
|
||||
resp, err := c.cli.CreateAndDeliverWithOptions(
|
||||
cardData,
|
||||
&card.CreateAndDeliverHeaders{XAcsDingtalkAccessToken: tea.String(accessToken)},
|
||||
&util.RuntimeOptions{},
|
||||
)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if resp.Body == nil {
|
||||
return false, errorcode.ParamErrf("empty response body")
|
||||
}
|
||||
if !*resp.Body.Success {
|
||||
return false, errorcode.ParamErrf("create and deliver failed")
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
|
@ -34,10 +34,16 @@ func NewOauth2Client(config *config.Config, rds *utils.Rdb) (*Oauth2Client, erro
|
|||
return &Oauth2Client{config: config, cli: c, redisCli: rds.Rdb}, nil
|
||||
}
|
||||
|
||||
func (c *Oauth2Client) GetAccessToken() (string, error) {
|
||||
// 去cache
|
||||
type AppKey struct {
|
||||
AppKey string `json:"appKey"`
|
||||
AppSecret string `json:"appSecret"`
|
||||
}
|
||||
|
||||
// GetAccessToken 获取access token
|
||||
func (c *Oauth2Client) GetAccessToken(req AppKey) (string, error) {
|
||||
// 取cache
|
||||
ctx := context.Background()
|
||||
accessToken, err := c.redisCli.Get(ctx, "dingtalk:oauth2:access_token").Result()
|
||||
accessToken, err := c.redisCli.Get(ctx, fmt.Sprintf("dingtalk:oauth2:%s:access_token", req.AppKey)).Result()
|
||||
if err == nil {
|
||||
fmt.Println("get access token from cache:", accessToken)
|
||||
return accessToken, nil
|
||||
|
|
@ -46,9 +52,10 @@ func (c *Oauth2Client) GetAccessToken() (string, error) {
|
|||
return "", err
|
||||
}
|
||||
|
||||
// 调用API
|
||||
resp, err := c.cli.GetAccessToken(&oauth2.GetAccessTokenRequest{
|
||||
AppKey: tea.String("ding5wwvnf9hxeyjau7t"),
|
||||
AppSecret: tea.String("FxXVlTzxrKXvJ8h-9uK0s5TjaBfOJSXumpmrHal-NmQAtku9wOPxcss0Af6WHoAK"),
|
||||
AppKey: tea.String(req.AppKey),
|
||||
AppSecret: tea.String(req.AppSecret),
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
|
|
@ -58,7 +65,8 @@ func (c *Oauth2Client) GetAccessToken() (string, error) {
|
|||
return "", errorcode.ParamErrf("empty response body")
|
||||
}
|
||||
|
||||
c.redisCli.Set(ctx, "dingtalk:oauth2:access_token", *resp.Body.AccessToken, time.Duration(*resp.Body.ExpireIn)*time.Second)
|
||||
// 缓存token
|
||||
c.redisCli.Set(ctx, fmt.Sprintf("dingtalk:oauth2:%s:access_token", req.AppKey), *resp.Body.AccessToken, time.Duration(*resp.Body.ExpireIn)*time.Second)
|
||||
|
||||
return *resp.Body.AccessToken, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -111,3 +111,41 @@ func (c *OldClient) QueryUserDetailsByMobile(ctx context.Context, mobile string)
|
|||
func (c *OldClient) GetAccessToken() (string, error) {
|
||||
return c.atm.GetAccessToken()
|
||||
}
|
||||
|
||||
// CreateInternalGroupConversation 创建企业内部群聊
|
||||
func (c *OldClient) CreateInternalGroupConversation(ctx context.Context, groupName string, userIds []string) (string, error) {
|
||||
body := struct {
|
||||
Name string `json:"name"`
|
||||
Owner string `json:"owner"`
|
||||
UserIds []string `json:"useridlist"`
|
||||
ShowHistoryType int `json:"showHistoryType"`
|
||||
Searchable int `json:"searchable"`
|
||||
ValidationType int `json:"validationType"`
|
||||
MentionAllAuthority int `json:"mentionAllAuthority"`
|
||||
ManagementType int `json:"managementType"`
|
||||
ChatBannedType int `json:"chatBannedType"`
|
||||
}{
|
||||
Name: groupName,
|
||||
Owner: userIds[0],
|
||||
UserIds: userIds,
|
||||
}
|
||||
b, _ := json.Marshal(body)
|
||||
res, err := c.do(ctx, http.MethodPost, "/chat/create", b)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
var resp struct {
|
||||
Code int `json:"errcode"`
|
||||
Msg string `json:"errmsg"`
|
||||
ChatId string `json:"chatid"`
|
||||
OpenConversationId string `json:"openConversationId"`
|
||||
ConversationTag int `json:"conversationTag"`
|
||||
}
|
||||
if err := json.Unmarshal(res, &resp); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if resp.Code != 0 {
|
||||
return "", errors.New(resp.Msg)
|
||||
}
|
||||
return resp.OpenConversationId, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package dingtalk
|
|||
import (
|
||||
"ai_scheduler/internal/config"
|
||||
errorcode "ai_scheduler/internal/data/error"
|
||||
"encoding/json"
|
||||
|
||||
openapi "github.com/alibabacloud-go/darabonba-openapi/v2/client"
|
||||
robot "github.com/alibabacloud-go/dingtalk/robot_1_0"
|
||||
|
|
@ -30,24 +31,22 @@ func NewRobotClient(config *config.Config) (*RobotClient, error) {
|
|||
}
|
||||
|
||||
type SendGroupMessagesReq struct {
|
||||
FullMatchField int32
|
||||
QueryWord string
|
||||
Offset int32
|
||||
Size int32
|
||||
MsgKey string
|
||||
MsgParam map[string]any
|
||||
OpenConversationId string
|
||||
RobotCode string
|
||||
}
|
||||
|
||||
type SendGroupMessagesResp struct {
|
||||
Body interface{}
|
||||
}
|
||||
|
||||
func (c *RobotClient) SendGroupMessages(accessToken string, name string) (string, error) {
|
||||
func (c *RobotClient) SendGroupMessages(accessToken string, req *SendGroupMessagesReq) (string, error) {
|
||||
headers := &robot.OrgGroupSendHeaders{}
|
||||
headers.XAcsDingtalkAccessToken = tea.String(accessToken)
|
||||
msgParamBytes, _ := json.Marshal(req.MsgParam)
|
||||
msgParamJson := string(msgParamBytes)
|
||||
resp, err := c.cli.OrgGroupSendWithOptions(&robot.OrgGroupSendRequest{
|
||||
MsgKey: tea.String("sampleText"),
|
||||
MsgParam: tea.String("{\"content\":\"今天吃肘子\"}"),
|
||||
OpenConversationId: tea.String("cidwP24PLZhLVOS2dVIkEawLw=="),
|
||||
RobotCode: tea.String("ding5wwvnf9hxeyjau7t"),
|
||||
MsgKey: tea.String(req.MsgKey),
|
||||
MsgParam: tea.String(msgParamJson),
|
||||
OpenConversationId: tea.String(req.OpenConversationId),
|
||||
RobotCode: tea.String(req.RobotCode),
|
||||
}, headers, &util.RuntimeOptions{})
|
||||
if err != nil {
|
||||
return "", err
|
||||
|
|
|
|||
|
|
@ -21,8 +21,9 @@ var ProviderSetClient = wire.NewSet(
|
|||
dingtalk.NewOldClient,
|
||||
dingtalk.NewContactClient,
|
||||
dingtalk.NewNotableClient,
|
||||
dingtalk.NewRobotClient,
|
||||
// dingtalk.NewRobotClient,
|
||||
dingtalk.NewOauth2Client,
|
||||
dingtalk.NewCardClient,
|
||||
|
||||
utils_oss.NewClient,
|
||||
lsxd.NewLogin,
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import (
|
|||
"fmt"
|
||||
"sync"
|
||||
|
||||
"gitea.cdlsxd.cn/self-tools/l-dingtalk-stream-sdk-go/card"
|
||||
"gitea.cdlsxd.cn/self-tools/l-dingtalk-stream-sdk-go/chatbot"
|
||||
"gitea.cdlsxd.cn/self-tools/l-dingtalk-stream-sdk-go/client"
|
||||
"github.com/go-kratos/kratos/v2/log"
|
||||
|
|
@ -15,6 +16,7 @@ import (
|
|||
type DingBotServiceInterface interface {
|
||||
GetServiceCfg() ([]entitys.DingTalkBot, error)
|
||||
OnChatBotMessageReceived(ctx context.Context, data *chatbot.BotCallbackDataModel) (content []byte, err error)
|
||||
OnCardMessageReceived(ctx context.Context, data *card.CardRequest) (resp *card.CardResponse, err error)
|
||||
}
|
||||
|
||||
type DingTalkBotServer struct {
|
||||
|
|
@ -51,8 +53,12 @@ func NewDingTalkBotServer(
|
|||
|
||||
func ProvideAllDingBotServices(
|
||||
dingBotSvc *services.DingBotService,
|
||||
// dingCardSvc *services.DingtalkCardService,
|
||||
) []DingBotServiceInterface {
|
||||
return []DingBotServiceInterface{dingBotSvc}
|
||||
return []DingBotServiceInterface{
|
||||
dingBotSvc,
|
||||
// dingCardSvc,
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DingTalkBotServer) Run(ctx context.Context, botIndex string) {
|
||||
|
|
@ -103,5 +109,6 @@ func (d *DingTalkBotServer) Run(ctx context.Context, botIndex string) {
|
|||
func DingBotServerInit(clientId string, clientSecret string, service DingBotServiceInterface) (cli *client.StreamClient) {
|
||||
cli = client.NewStreamClient(client.WithAppCredential(client.NewAppCredentialConfig(clientId, clientSecret)))
|
||||
cli.RegisterChatBotCallbackRouter(service.OnChatBotMessageReceived)
|
||||
cli.RegisterCardCallbackRouter(service.OnCardMessageReceived)
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,37 @@
|
|||
package services
|
||||
|
||||
// 当前不用,后续切钉钉新sdk考虑使用
|
||||
|
||||
import (
|
||||
"ai_scheduler/internal/biz"
|
||||
"ai_scheduler/internal/config"
|
||||
"ai_scheduler/internal/entitys"
|
||||
"context"
|
||||
|
||||
"gitea.cdlsxd.cn/self-tools/l-dingtalk-stream-sdk-go/card"
|
||||
"gitea.cdlsxd.cn/self-tools/l-dingtalk-stream-sdk-go/chatbot"
|
||||
)
|
||||
|
||||
type DingtalkCardService struct {
|
||||
config *config.Config
|
||||
dingtalkCardBiz *biz.DingTalkBotBiz
|
||||
}
|
||||
|
||||
func NewDingtalkCardService(config *config.Config, dingtalkCardBiz *biz.DingTalkBotBiz) *DingtalkCardService {
|
||||
return &DingtalkCardService{
|
||||
config: config,
|
||||
dingtalkCardBiz: dingtalkCardBiz,
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DingtalkCardService) GetServiceCfg() (config []entitys.DingTalkBot, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (d *DingtalkCardService) OnChatBotMessageReceived(ctx context.Context, data *chatbot.BotCallbackDataModel) (content []byte, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (d *DingtalkCardService) OnCardMessageReceived(ctx context.Context, data *card.CardRequest) (resp *card.CardResponse, err error) {
|
||||
return
|
||||
}
|
||||
|
|
@ -3,12 +3,16 @@ package services
|
|||
import (
|
||||
"ai_scheduler/internal/biz"
|
||||
"ai_scheduler/internal/config"
|
||||
"ai_scheduler/internal/data/constants"
|
||||
"ai_scheduler/internal/entitys"
|
||||
"ai_scheduler/internal/pkg/dingtalk"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"log"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"gitea.cdlsxd.cn/self-tools/l-dingtalk-stream-sdk-go/card"
|
||||
"gitea.cdlsxd.cn/self-tools/l-dingtalk-stream-sdk-go/chatbot"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
|
@ -16,12 +20,14 @@ import (
|
|||
type DingBotService struct {
|
||||
config *config.Config
|
||||
dingTalkBotBiz *biz.DingTalkBotBiz
|
||||
dingTalkOld *dingtalk.OldClient
|
||||
}
|
||||
|
||||
func NewDingBotService(config *config.Config, dingTalkBotBiz *biz.DingTalkBotBiz) *DingBotService {
|
||||
func NewDingBotService(config *config.Config, dingTalkBotBiz *biz.DingTalkBotBiz, dingTalkOld *dingtalk.OldClient) *DingBotService {
|
||||
return &DingBotService{
|
||||
config: config,
|
||||
dingTalkBotBiz: dingTalkBotBiz,
|
||||
dingTalkOld: dingTalkOld,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -140,3 +146,27 @@ func (d *DingBotService) runBackgroundTasks(ctx context.Context, data *chatbot.B
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DingBotService) OnCardMessageReceived(ctx context.Context, data *card.CardRequest) (resp *card.CardResponse, err error) {
|
||||
// 解content
|
||||
var cardActionData card.PrivateCardActionData
|
||||
if err = json.Unmarshal([]byte(data.Content), &cardActionData); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// action 处理
|
||||
for _, actionId := range cardActionData.CardPrivateData.ActionIdList {
|
||||
switch actionId {
|
||||
case constants.CardActionTypeCreateGroup:
|
||||
if cardActionData.CardPrivateData.Params["status"] != "confirm" {
|
||||
continue
|
||||
}
|
||||
// 创建群聊
|
||||
if _, err = d.dingTalkOld.CreateInternalGroupConversation(ctx, "问题处理群", []string{"17415698414368678", "17101201090101570"}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return &card.CardResponse{}, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,4 +15,5 @@ var ProviderSetServices = wire.NewSet(
|
|||
NewHistoryService,
|
||||
NewCapabilityService,
|
||||
NewCronService,
|
||||
// NewDingtalkCardService,
|
||||
)
|
||||
|
|
|
|||
Loading…
Reference in New Issue