diff --git a/backend/services/notification/ding_talk.go b/backend/services/notification/ding_talk.go
new file mode 100644
index 00000000..0a1dac14
--- /dev/null
+++ b/backend/services/notification/ding_talk.go
@@ -0,0 +1,84 @@
+package notification
+
+import (
+ "crawlab/model"
+ "crawlab/utils"
+ "errors"
+ "fmt"
+ "github.com/apex/log"
+ "github.com/imroc/req"
+ "runtime/debug"
+ "time"
+)
+
+func SendDingTalkNotification(t model.Task, s model.Spider) error {
+ // 获取用户
+ user, _ := model.GetUser(t.UserId)
+
+ // 如果AppKey或AppSecret未设置,则返回错误
+ if user.Setting.DingTalkAppKey == "" || user.Setting.DingTalkAppSecret == "" {
+ return errors.New("ding_talk_app_key or ding_talk_app_secret is empty")
+ }
+
+ // 获取access_token
+ accessToken, err := GetDingTalkAccessToken(user)
+ if err != nil {
+ return err
+ }
+
+ // 时间戳
+ timestamp := time.Now().Unix()
+
+ // 计算sign
+ signRawString := fmt.Sprintf("%d\n%s", timestamp, user.Setting.DingTalkAppSecret)
+ sign := utils.ComputeHmacSha256(signRawString, user.Setting.DingTalkAppSecret)
+
+ // 请求数据
+ url := fmt.Sprintf("https://oapi.dingtalk.com/robot/send?access_token=%s", accessToken)
+ header := req.Header{
+ "Content-Type": "application/json",
+ "timestamp": fmt.Sprintf("%d000", timestamp),
+ "sign": sign,
+ }
+ data := req.Param{
+ "msgtype": "text",
+ "text": req.Param{
+ "text": "it works",
+ },
+ }
+ res, err := req.Post(url, header, req.BodyJSON(&data))
+ if err != nil {
+ log.Errorf("dingtalk notification error: " + err.Error())
+ debug.PrintStack()
+ return err
+ }
+ log.Infof(fmt.Sprintf("%+v", res))
+ return nil
+}
+
+func GetDingTalkAccessToken(u model.User) (string, error) {
+ type ResBody struct {
+ ErrCode int `json:"errcode"`
+ ErrMsg string `json:"errmsg"`
+ AccessToken string `json:"access_token"`
+ }
+
+ // 请求数据
+ url := fmt.Sprintf("https://oapi.dingtalk.com/gettoken?appkey=%s&appsecret=%s", u.Setting.DingTalkAppKey, u.Setting.DingTalkAppSecret)
+ res, err := req.Get(url)
+ if err != nil {
+ log.Errorf("get dingtalk access_token error: " + err.Error())
+ debug.PrintStack()
+ return "", err
+ }
+
+ // 解析相应body
+ var resBody ResBody
+ if err := res.ToJSON(&resBody); err != nil {
+ log.Errorf("get dingtalk access_token error: " + err.Error())
+ debug.PrintStack()
+ return "", err
+ }
+
+ return resBody.AccessToken, nil
+}
diff --git a/backend/services/task.go b/backend/services/task.go
index 16445213..12c3580a 100644
--- a/backend/services/task.go
+++ b/backend/services/task.go
@@ -519,6 +519,10 @@ func ExecuteTask(id int) {
if user.Email != "" &&
user.Setting.NotificationTrigger == constants.NotificationTriggerOnTaskEnd {
SendTaskEmail(user, t, spider)
+ if err := notification.SendDingTalkNotification(t, spider); err != nil {
+ log.Errorf(err.Error())
+ debug.PrintStack()
+ }
}
// 保存任务
diff --git a/frontend/src/views/setting/Setting.vue b/frontend/src/views/setting/Setting.vue
index 49a38700..4ca476a5 100644
--- a/frontend/src/views/setting/Setting.vue
+++ b/frontend/src/views/setting/Setting.vue
@@ -33,14 +33,16 @@
v-model="userInfo.setting.ding_talk_app_secret"
:placeholder="$t('DingTalk AppSecret')"
/>
+
+
+
@@ -104,6 +106,9 @@ export default {
this.$message.success(this.$t('Saved successfully'))
}
})
+ },
+ toggleDingTalkAppSecret () {
+ this.isShowDingTalkAppSecret = !this.isShowDingTalkAppSecret
}
},
async created () {
@@ -121,4 +126,11 @@ export default {
.setting-form .buttons {
text-align: right;
}
+
+ .setting-form .icon {
+ top: calc(50% - 14px / 2);
+ right: 14px;
+ position: absolute;
+ color: #DCDFE6;
+ }