mirror of
https://github.com/crawlab-team/crawlab.git
synced 2026-01-22 17:31:03 +01:00
完成钉钉机器人消息通知
This commit is contained in:
@@ -24,9 +24,8 @@ type User struct {
|
||||
}
|
||||
|
||||
type UserSetting struct {
|
||||
NotificationTrigger string `json:"notification_trigger" bson:"notification_trigger"`
|
||||
DingTalkAppKey string `json:"ding_talk_app_key" bson:"ding_talk_app_key"`
|
||||
DingTalkAppSecret string `json:"ding_talk_app_secret" bson:"ding_talk_app_secret"`
|
||||
NotificationTrigger string `json:"notification_trigger" bson:"notification_trigger"`
|
||||
DingTalkRobotWebhook string `json:"ding_talk_robot_webhook" bson:"ding_talk_robot_webhook"`
|
||||
}
|
||||
|
||||
func (user *User) Save() error {
|
||||
|
||||
@@ -213,11 +213,10 @@ func GetMe(c *gin.Context) {
|
||||
|
||||
func PostMe(c *gin.Context) {
|
||||
type ReqBody struct {
|
||||
Email string `json:"email"`
|
||||
Password string `json:"password"`
|
||||
NotificationTrigger string `json:"notification_trigger"`
|
||||
DingTalkAppKey string `json:"ding_talk_app_key"`
|
||||
DingTalkAppSecret string `json:"ding_talk_app_secret"`
|
||||
Email string `json:"email"`
|
||||
Password string `json:"password"`
|
||||
NotificationTrigger string `json:"notification_trigger"`
|
||||
DingTalkRobotWebhook string `json:"ding_talk_robot_webhook"`
|
||||
}
|
||||
ctx := context.WithGinContext(c)
|
||||
user := ctx.User()
|
||||
@@ -239,11 +238,8 @@ func PostMe(c *gin.Context) {
|
||||
if reqBody.NotificationTrigger != "" {
|
||||
user.Setting.NotificationTrigger = reqBody.NotificationTrigger
|
||||
}
|
||||
if reqBody.DingTalkAppKey != "" {
|
||||
user.Setting.DingTalkAppKey = reqBody.DingTalkAppKey
|
||||
}
|
||||
if reqBody.DingTalkAppSecret != "" {
|
||||
user.Setting.DingTalkAppSecret = reqBody.DingTalkAppSecret
|
||||
if reqBody.DingTalkRobotWebhook != "" {
|
||||
user.Setting.DingTalkRobotWebhook = reqBody.DingTalkRobotWebhook
|
||||
}
|
||||
if err := user.Save(); err != nil {
|
||||
HandleError(http.StatusInternalServerError, c, err)
|
||||
|
||||
@@ -1,95 +1,58 @@
|
||||
package notification
|
||||
|
||||
import (
|
||||
"crawlab/model"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/apex/log"
|
||||
"github.com/imroc/req"
|
||||
"github.com/royeo/dingrobot"
|
||||
"runtime/debug"
|
||||
)
|
||||
|
||||
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)
|
||||
robot := dingrobot.NewRobot(url)
|
||||
|
||||
text := "it works"
|
||||
if err := robot.SendText(text, []string{}, false); err != nil {
|
||||
log.Errorf(err.Error())
|
||||
debug.PrintStack()
|
||||
return err
|
||||
}
|
||||
//header := req.Header{
|
||||
// "Content-Type": "application/json; charset=utf-8",
|
||||
// "timestamp": fmt.Sprintf("%d000", timestamp),
|
||||
// "sign": sign,
|
||||
//}
|
||||
//data := req.Param{
|
||||
// "msgtype": "text",
|
||||
// "text": req.Param{
|
||||
// "text": "it works",
|
||||
// },
|
||||
// "at": req.Param{
|
||||
// "atMobiles": []string{},
|
||||
// "isAtAll": false,
|
||||
// },
|
||||
//}
|
||||
//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) {
|
||||
func SendDingTalkNotification(webhook string, title string, content string) error {
|
||||
type ResBody struct {
|
||||
ErrCode int `json:"errcode"`
|
||||
ErrMsg string `json:"errmsg"`
|
||||
AccessToken string `json:"access_token"`
|
||||
ErrCode int `json:"errcode"`
|
||||
ErrMsg string `json:"errmsg"`
|
||||
}
|
||||
|
||||
// 请求头
|
||||
header := req.Header{
|
||||
"Content-Type": "application/json; charset=utf-8",
|
||||
}
|
||||
|
||||
// 请求数据
|
||||
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
|
||||
data := req.Param{
|
||||
"msgtype": "markdown",
|
||||
"markdown": req.Param{
|
||||
"title": title,
|
||||
"text": content,
|
||||
},
|
||||
"at": req.Param{
|
||||
"atMobiles": []string{},
|
||||
"isAtAll": false,
|
||||
},
|
||||
}
|
||||
|
||||
// 解析相应body
|
||||
// 发起请求
|
||||
res, err := req.Post(webhook, header, req.BodyJSON(&data))
|
||||
if err != nil {
|
||||
log.Errorf("dingtalk notification error: " + err.Error())
|
||||
debug.PrintStack()
|
||||
return err
|
||||
}
|
||||
|
||||
// 解析响应
|
||||
var resBody ResBody
|
||||
if err := res.ToJSON(&resBody); err != nil {
|
||||
log.Errorf("get dingtalk access_token error: " + err.Error())
|
||||
log.Errorf("dingtalk notification error: " + err.Error())
|
||||
debug.PrintStack()
|
||||
return "", err
|
||||
return err
|
||||
}
|
||||
|
||||
return resBody.AccessToken, nil
|
||||
// 判断响应是否报错
|
||||
if resBody.ErrCode != 0 {
|
||||
log.Errorf("dingtalk notification error: " + resBody.ErrMsg)
|
||||
debug.PrintStack()
|
||||
return errors.New(resBody.ErrMsg)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -489,9 +489,14 @@ func ExecuteTask(id int) {
|
||||
|
||||
// 如果发生错误,则发送通知
|
||||
t, _ = model.GetTask(t.Id)
|
||||
if user.Email != "" &&
|
||||
(user.Setting.NotificationTrigger == constants.NotificationTriggerOnTaskEnd || user.Setting.NotificationTrigger == constants.NotificationTriggerOnTaskError) {
|
||||
SendTaskEmail(user, t, spider)
|
||||
if user.Setting.NotificationTrigger == constants.NotificationTriggerOnTaskEnd || user.Setting.NotificationTrigger == constants.NotificationTriggerOnTaskError {
|
||||
if user.Email != "" {
|
||||
SendTaskEmail(user, t, spider)
|
||||
}
|
||||
|
||||
if user.Setting.DingTalkRobotWebhook != "" {
|
||||
SendTaskDingTalk(user, t, spider)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -516,12 +521,13 @@ func ExecuteTask(id int) {
|
||||
t.TotalDuration = t.FinishTs.Sub(t.CreateTs).Seconds() // 总时长
|
||||
|
||||
// 如果是任务结束时发送通知,则发送通知
|
||||
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()
|
||||
if user.Setting.NotificationTrigger == constants.NotificationTriggerOnTaskEnd {
|
||||
if user.Email != "" {
|
||||
SendTaskEmail(user, t, spider)
|
||||
}
|
||||
|
||||
if user.Setting.DingTalkRobotWebhook != "" {
|
||||
SendTaskDingTalk(user, t, spider)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -695,7 +701,7 @@ func HandleTaskError(t model.Task, err error) {
|
||||
debug.PrintStack()
|
||||
}
|
||||
|
||||
func GetTaskEmailContent(t model.Task, s model.Spider) string {
|
||||
func GetTaskEmailMarkdownContent(t model.Task, s model.Spider) string {
|
||||
n, _ := model.GetNode(t.NodeId)
|
||||
errMsg := ""
|
||||
statusMsg := fmt.Sprintf(`<span style="color:green">%s</span>`, t.Status)
|
||||
@@ -743,22 +749,82 @@ Please login to Crawlab to view the details.
|
||||
)
|
||||
}
|
||||
|
||||
func GetTaskDingTalkMarkdownContent(t model.Task, s model.Spider) string {
|
||||
n, _ := model.GetNode(t.NodeId)
|
||||
errMsg := ""
|
||||
statusMsg := fmt.Sprintf(`<font color=green>%s</font>`, t.Status)
|
||||
if t.Status == constants.StatusError {
|
||||
errMsg = `<font color="red">(有错误)</font>`
|
||||
statusMsg = fmt.Sprintf(`<font color=red>%s</font>`, t.Status)
|
||||
}
|
||||
return fmt.Sprintf(`
|
||||
您的任务已完成%s,请查看任务信息如下。
|
||||
|
||||
> **任务ID:** %s
|
||||
> **任务状态:** %s
|
||||
> **任务参数:** %s
|
||||
> **爬虫ID:** %s
|
||||
> **爬虫名称:** %s
|
||||
> **节点:** %s
|
||||
> **创建时间:** %s
|
||||
> **开始时间:** %s
|
||||
> **完成时间:** %s
|
||||
> **等待时间:** %.0f秒
|
||||
> **运行时间:** %.0f秒
|
||||
> **总时间:** %.0f秒
|
||||
> **结果数:** %d
|
||||
> **错误:** %s
|
||||
|
||||
请登录Crawlab查看详情。
|
||||
`,
|
||||
errMsg,
|
||||
t.Id,
|
||||
statusMsg,
|
||||
t.Param,
|
||||
s.Id.Hex(),
|
||||
s.Name,
|
||||
n.Name,
|
||||
utils.GetLocalTimeString(t.CreateTs),
|
||||
utils.GetLocalTimeString(t.StartTs),
|
||||
utils.GetLocalTimeString(t.FinishTs),
|
||||
t.WaitDuration,
|
||||
t.RuntimeDuration,
|
||||
t.TotalDuration,
|
||||
t.ResultCount,
|
||||
t.Error,
|
||||
)
|
||||
}
|
||||
|
||||
func SendTaskEmail(u model.User, t model.Task, s model.Spider) {
|
||||
statusMsg := "has finished"
|
||||
if t.Status == constants.StatusError {
|
||||
statusMsg = "has an error"
|
||||
}
|
||||
title := fmt.Sprintf("[Crawlab] Task for \"%s\" %s", s.Name, statusMsg)
|
||||
if err := notification.SendMail(
|
||||
u.Email,
|
||||
u.Username,
|
||||
fmt.Sprintf("[Crawlab] Task for \"%s\" %s", s.Name, statusMsg),
|
||||
GetTaskEmailContent(t, s),
|
||||
title,
|
||||
GetTaskEmailMarkdownContent(t, s),
|
||||
); err != nil {
|
||||
log.Errorf("mail error: " + err.Error())
|
||||
debug.PrintStack()
|
||||
}
|
||||
}
|
||||
|
||||
func SendTaskDingTalk(u model.User, t model.Task, s model.Spider) {
|
||||
statusMsg := "has finished"
|
||||
if t.Status == constants.StatusError {
|
||||
statusMsg = "has an error"
|
||||
}
|
||||
title := fmt.Sprintf("[Crawlab] Task for \"%s\" %s", s.Name, statusMsg)
|
||||
content := GetTaskDingTalkMarkdownContent(t, s)
|
||||
if err := notification.SendDingTalkNotification(u.Setting.DingTalkRobotWebhook, title, content); err != nil {
|
||||
log.Errorf(err.Error())
|
||||
debug.PrintStack()
|
||||
}
|
||||
}
|
||||
|
||||
func InitTaskExecutor() error {
|
||||
c := cron.New(cron.WithSeconds())
|
||||
Exec = &Executor{
|
||||
|
||||
@@ -353,8 +353,7 @@ export default {
|
||||
'On Task End': '当任务结束',
|
||||
'On Task Error': '当任务发生错误',
|
||||
'Never': '从不',
|
||||
'DingTalk AppKey': '钉钉 AppKey',
|
||||
'DingTalk AppSecret': '钉钉 AppSecret',
|
||||
'DingTalk Robot Webhook': '钉钉机器人 Webhook',
|
||||
|
||||
// 其他
|
||||
tagsView: {
|
||||
|
||||
Reference in New Issue
Block a user