mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-09-11 05:37:23 +08:00
186 lines
5.7 KiB
Go
186 lines
5.7 KiB
Go
// Copyright 2019 Yunion
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
package sender
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/http"
|
|
"net/url"
|
|
|
|
"yunion.io/x/jsonutils"
|
|
"yunion.io/x/pkg/errors"
|
|
"yunion.io/x/pkg/util/httputils"
|
|
|
|
api "yunion.io/x/onecloud/pkg/apis/notify"
|
|
"yunion.io/x/onecloud/pkg/monitor/notifydrivers/feishu"
|
|
"yunion.io/x/onecloud/pkg/notify/models"
|
|
)
|
|
|
|
type SFeishuSender struct {
|
|
config map[string]api.SNotifyConfigContent
|
|
}
|
|
|
|
func (self *SFeishuSender) GetSenderType() string {
|
|
return api.FEISHU
|
|
}
|
|
|
|
const ApiSendMessageForFeishuByOpenId = feishu.ApiRobotSendMessage + "?receive_id_type=open_id"
|
|
|
|
// 发送飞书消息
|
|
func (feishuSender *SFeishuSender) Send(ctx context.Context, args api.SendParams) error {
|
|
// 发送通知消息体
|
|
body := map[string]interface{}{
|
|
"open_id": args.Receivers.Contact,
|
|
"msg_type": "text",
|
|
"content": map[string]interface{}{
|
|
"text": args.Message,
|
|
},
|
|
} // 添加bearer token请求头
|
|
header := http.Header{}
|
|
header.Add("Authorization", fmt.Sprintf("Bearer %s", models.ConfigMap[fmt.Sprintf("%s-%s", api.FEISHU, args.DomainId)].Content.AccessToken))
|
|
rep, err := sendRequest(ctx, ApiSendMessageForFeishuByOpenId, httputils.POST, header, nil, jsonutils.Marshal(body))
|
|
if err == nil {
|
|
return nil
|
|
}
|
|
if rep == nil {
|
|
return errors.Wrap(err, "feishu.sendRequest")
|
|
}
|
|
// 发送通知失败的情况
|
|
code, err := rep.GetString("code")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
switch code {
|
|
case "99991663": //token过期
|
|
err = feishuSender.GetAccessToken(ctx, fmt.Sprintf("%s-%s", api.FEISHU, args.DomainId))
|
|
if err != nil {
|
|
return errors.Wrap(err, "tenant token invalid && getToken err")
|
|
}
|
|
header.Set("Authorization", fmt.Sprintf("Bearer %s", models.ConfigMap[fmt.Sprintf("%s-%s", api.FEISHU, args.DomainId)].Content.AccessToken))
|
|
_, err = sendRequest(ctx, ApiSendMessageForFeishuByOpenId, httputils.POST, header, nil, jsonutils.Marshal(body))
|
|
if err == nil {
|
|
return nil
|
|
}
|
|
case "99991672": // 未开放发送消息通知的权限
|
|
err = errors.Wrap(ErrIncompleteConfig, err.Error())
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
// 校验appId与appSecret
|
|
func (feishuSender *SFeishuSender) ValidateConfig(ctx context.Context, config api.NotifyConfig) (string, error) {
|
|
rep, err := feishuSender.getAccessToken(config.AppId, config.AppSecret)
|
|
if err == nil {
|
|
return "", nil
|
|
}
|
|
var msg string
|
|
switch rep.Code {
|
|
case 10003:
|
|
msg = "invalid AppId"
|
|
case 10014:
|
|
msg = "invalid AppSecret"
|
|
}
|
|
return msg, err
|
|
}
|
|
|
|
// 根据用户手机号获取用户的open_id
|
|
func (feishuSender *SFeishuSender) ContactByMobile(ctx context.Context, mobile, domainId string) (string, error) {
|
|
body := jsonutils.NewDict()
|
|
body.Set("mobiles", jsonutils.NewArray(jsonutils.NewString(mobile)))
|
|
header := http.Header{}
|
|
// 考虑到获取用户id需求较少,可通过直接更新token来避免token失效
|
|
err := feishuSender.GetAccessToken(ctx, domainId)
|
|
if err != nil {
|
|
return "", errors.Wrap(err, "GetAccessToken")
|
|
}
|
|
|
|
params := url.Values{}
|
|
params.Set("mobiles", mobile)
|
|
|
|
header.Set("Authorization", fmt.Sprintf("Bearer %s", models.ConfigMap[fmt.Sprintf("%s-%s", api.FEISHU, domainId)].Content.AccessToken))
|
|
resp, err := sendRequest(ctx, ApiFetchUserID, httputils.GET, header, params, body)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
mobileNotExist, _ := resp.GetArray("data", "mobiles_not_exist")
|
|
if len(mobileNotExist) != 0 {
|
|
return "", errors.Wrapf(errors.ErrNotFound, "no such user whose mobile is %s", mobile)
|
|
}
|
|
list, err := resp.GetArray("data", "mobile_users", mobile)
|
|
if err != nil {
|
|
return "", errors.Wrap(err, "jsonutils.JSONObject.GetArray")
|
|
}
|
|
// len(list) must be positive
|
|
userId, err := list[0].GetString("open_id")
|
|
if err != nil {
|
|
return "", errors.Wrapf(err, "user result:%v", resp)
|
|
}
|
|
return userId, nil
|
|
}
|
|
|
|
func (feishuSender *SFeishuSender) IsPersonal() bool {
|
|
return true
|
|
}
|
|
|
|
func (feishuSender *SFeishuSender) IsRobot() bool {
|
|
return false
|
|
}
|
|
|
|
func (feishuSender *SFeishuSender) IsValid() bool {
|
|
return len(feishuSender.config) > 0
|
|
}
|
|
|
|
func (feishuSender *SFeishuSender) IsPullType() bool {
|
|
return true
|
|
}
|
|
|
|
func (feishuSender *SFeishuSender) IsSystemConfigContactType() bool {
|
|
return true
|
|
}
|
|
|
|
func (feishuSender *SFeishuSender) RegisterConfig(config models.SConfig) {
|
|
models.ConfigMap[fmt.Sprintf("%s-%s", config.Type, config.DomainId)] = config
|
|
}
|
|
|
|
// 获取token
|
|
func (feishuSender *SFeishuSender) GetAccessToken(ctx context.Context, domainId string) error {
|
|
key := fmt.Sprintf("%s-%s", api.FEISHU, domainId)
|
|
if _, ok := models.ConfigMap[key]; !ok {
|
|
return errors.Wrapf(errors.ErrNotSupported, "contact-type:%s,domain_id:%s is missing config", api.FEISHU, domainId)
|
|
}
|
|
appId, appSecret := models.ConfigMap[key].Content.AppId, models.ConfigMap[key].Content.AppSecret
|
|
resp, err := feishuSender.getAccessToken(appId, appSecret)
|
|
if err != nil {
|
|
return errors.Wrap(err, "get accessToken")
|
|
}
|
|
models.ConfigMap[key].Content.AccessToken = resp.TenantAccessToken
|
|
return nil
|
|
}
|
|
|
|
func (feishuSender *SFeishuSender) getAccessToken(appId, appSecret string) (*feishu.TenantAccesstokenResp, error) {
|
|
resp, err := feishu.GetTenantAccessTokenInternal(appId, appSecret)
|
|
if err != nil {
|
|
return resp, errors.Wrap(err, "feishu.GetTenantAccessTokenInternal")
|
|
}
|
|
return resp, nil
|
|
}
|
|
|
|
func init() {
|
|
models.Register(&SFeishuSender{
|
|
config: map[string]api.SNotifyConfigContent{},
|
|
})
|
|
}
|