diff --git a/backend/go.mod b/backend/go.mod index 791c6771..cbc7d75b 100644 --- a/backend/go.mod +++ b/backend/go.mod @@ -16,6 +16,7 @@ require ( github.com/matcornic/hermes v1.2.0 github.com/matcornic/hermes/v2 v2.0.2 // indirect github.com/pkg/errors v0.8.1 + github.com/royeo/dingrobot v1.0.0 github.com/satori/go.uuid v1.2.0 github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337 github.com/spf13/viper v1.4.0 diff --git a/backend/go.sum b/backend/go.sum index 37fe6119..a55ef74e 100644 --- a/backend/go.sum +++ b/backend/go.sum @@ -146,6 +146,8 @@ github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7z github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.1.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/royeo/dingrobot v1.0.0 h1:K4GrF+fOecNX0yi+oBKpfh7z0XP/8TzaIIHu1B2kKUQ= +github.com/royeo/dingrobot v1.0.0/go.mod h1:RqDM8E/hySCVwI2aUFRJAUGDcHHRnIhzNmbNG3bamQs= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= diff --git a/backend/services/notification/ding_talk.go b/backend/services/notification/ding_talk.go index 0a1dac14..ae1756aa 100644 --- a/backend/services/notification/ding_talk.go +++ b/backend/services/notification/ding_talk.go @@ -2,13 +2,12 @@ package notification import ( "crawlab/model" - "crawlab/utils" "errors" "fmt" "github.com/apex/log" "github.com/imroc/req" + "github.com/royeo/dingrobot" "runtime/debug" - "time" ) func SendDingTalkNotification(t model.Task, s model.Spider) error { @@ -27,38 +26,50 @@ func SendDingTalkNotification(t model.Task, s model.Spider) error { } // 时间戳 - timestamp := time.Now().Unix() + //timestamp := time.Now().Unix() // 计算sign - signRawString := fmt.Sprintf("%d\n%s", timestamp, user.Setting.DingTalkAppSecret) - sign := utils.ComputeHmacSha256(signRawString, user.Setting.DingTalkAppSecret) + //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()) + robot := dingrobot.NewRobot(url) + + text := "it works" + if err := robot.SendText(text, []string{}, false); err != nil { + log.Errorf(err.Error()) debug.PrintStack() return err } - log.Infof(fmt.Sprintf("%+v", res)) + //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) { type ResBody struct { - ErrCode int `json:"errcode"` + ErrCode int `json:"errcode"` ErrMsg string `json:"errmsg"` AccessToken string `json:"access_token"` } diff --git a/backend/vendor/github.com/royeo/dingrobot/LICENSE b/backend/vendor/github.com/royeo/dingrobot/LICENSE new file mode 100644 index 00000000..e2c20381 --- /dev/null +++ b/backend/vendor/github.com/royeo/dingrobot/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 Royeo + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/backend/vendor/github.com/royeo/dingrobot/README.md b/backend/vendor/github.com/royeo/dingrobot/README.md new file mode 100644 index 00000000..c76c6313 --- /dev/null +++ b/backend/vendor/github.com/royeo/dingrobot/README.md @@ -0,0 +1,110 @@ +# Dingrobot + +钉钉自定义机器人 Golang API. + +支持的消息类型: +- 文本类型 +- link 类型 +- markdown 类型 +- 整体跳转 ActionCard 类型 + +## Installation + +Install: + +```sh +go get -u github.com/royeo/dingrobot +``` + +Import: + +```go +import "github.com/royeo/dingrobot" +``` + +## Quick start + +发送文本类型的消息: + +```go +func main() { + // You should replace the webhook here with your own. + webhook := "https://oapi.dingtalk.com/robot/send?access_token=xxx" + robot := dingrobot.NewRobot(webhook) + + content := "我就是我, @1825718XXXX 是不一样的烟火" + atMobiles := []string{"1825718XXXX"} + isAtAll := false + + err := robot.SendText(content, atMobiles, isAtAll) + if err != nil { + log.Fatal(err) + } +} +``` + +发送 link 类型的消息: + +```go +func main() { + // You should replace the webhook here with your own. + webhook := "https://oapi.dingtalk.com/robot/send?access_token=xxx" + robot := dingrobot.NewRobot(webhook) + + title := "自定义机器人协议" + text := "群机器人是钉钉群的高级扩展功能。群机器人可以将第三方服务的信息聚合到群聊中,实现自动化的信息同步。例如:通过聚合GitHub,GitLab等源码管理服务,实现源码更新同步;通过聚合Trello,JIRA等项目协调服务,实现项目信息同步。不仅如此,群机器人支持Webhook协议的自定义接入,支持更多可能性,例如:你可将运维报警提醒通过自定义机器人聚合到钉钉群。" + messageUrl := "https://open-doc.dingtalk.com/docs/doc.htm?spm=a219a.7629140.0.0.Rqyvqo&treeId=257&articleId=105735&docType=1" + picUrl := "" + + err := robot.SendLink(title, text, messageUrl, picUrl) + if err != nil { + log.Fatal(err) + } +} +``` + +发送 markdown 类型的消息: + +```go +func main() { + // You should replace the webhook here with your own. + webhook := "https://oapi.dingtalk.com/robot/send?access_token=xxx" + robot := dingrobot.NewRobot(webhook) + + title := "杭州天气" + text := "#### 杭州天气  \n > 9度,@1825718XXXX 西北风1级,空气良89,相对温度73%\n\n > ![screenshot](http://i01.lw.aliimg.com/media/lALPBbCc1ZhJGIvNAkzNBLA_1200_588.png)\n  > ###### 10点20分发布 [天气](http://www.thinkpage.cn/) " + atMobiles := []string{"1825718XXXX"} + isAtAll := false + + err := robot.SendMarkdown(title, text, atMobiles, isAtAll) + if err != nil { + log.Fatal(err) + } +} +``` + +发送整体跳转 ActionCard 类型的消息: + +```go +func main() { + // You should replace the webhook here with your own. + webhook := "https://oapi.dingtalk.com/robot/send?access_token=xxx" + robot := dingrobot.NewRobot(webhook) + + title := "乔布斯 20 年前想打造一间苹果咖啡厅,而它正是 Apple Store 的前身" + text := "![screenshot](@lADOpwk3K80C0M0FoA) \n #### 乔布斯 20 年前想打造的苹果咖啡厅 \n\n Apple Store 的设计正从原来满满的科技感走向生活化,而其生活化的走向其实可以追溯到 20 年前苹果一个建立咖啡馆的计划" + singleTitle := "阅读全文" + singleURL := "https://www.dingtalk.com/" + btnOrientation := "0" + hideAvatar := "0" + + err := robot.SendActionCard(title, text, singleTitle, singleURL, btnOrientation, hideAvatar) + if err != nil { + log.Fatal(err) + } +} +``` + +## License + +MIT Copyright (c) 2018 Royeo diff --git a/backend/vendor/github.com/royeo/dingrobot/dingrobot.go b/backend/vendor/github.com/royeo/dingrobot/dingrobot.go new file mode 100644 index 00000000..cd5b65b0 --- /dev/null +++ b/backend/vendor/github.com/royeo/dingrobot/dingrobot.go @@ -0,0 +1,118 @@ +package dingrobot + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" +) + +// Roboter is the interface implemented by Robot that can send multiple types of messages. +type Roboter interface { + SendText(content string, atMobiles []string, isAtAll bool) error + SendLink(title, text, messageURL, picURL string) error + SendMarkdown(title, text string, atMobiles []string, isAtAll bool) error + SendActionCard(title, text, singleTitle, singleURL, btnOrientation, hideAvatar string) error +} + +// Robot represents a dingtalk custom robot that can send messages to groups. +type Robot struct { + Webhook string +} + +// NewRobot returns a roboter that can send messages. +func NewRobot(webhook string) Roboter { + return Robot{Webhook: webhook} +} + +// SendText send a text type message. +func (r Robot) SendText(content string, atMobiles []string, isAtAll bool) error { + return r.send(&textMessage{ + MsgType: msgTypeText, + Text: textParams{ + Content: content, + }, + At: atParams{ + AtMobiles: atMobiles, + IsAtAll: isAtAll, + }, + }) +} + +// SendLink send a link type message. +func (r Robot) SendLink(title, text, messageURL, picURL string) error { + return r.send(&linkMessage{ + MsgType: msgTypeLink, + Link: linkParams{ + Title: title, + Text: text, + MessageURL: messageURL, + PicURL: picURL, + }, + }) +} + +// SendMarkdown send a markdown type message. +func (r Robot) SendMarkdown(title, text string, atMobiles []string, isAtAll bool) error { + return r.send(&markdownMessage{ + MsgType: msgTypeMarkdown, + Markdown: markdownParams{ + Title: title, + Text: text, + }, + At: atParams{ + AtMobiles: atMobiles, + IsAtAll: isAtAll, + }, + }) +} + +// SendActionCard send a action card type message. +func (r Robot) SendActionCard(title, text, singleTitle, singleURL, btnOrientation, hideAvatar string) error { + return r.send(&actionCardMessage{ + MsgType: msgTypeActionCard, + ActionCard: actionCardParams{ + Title: title, + Text: text, + SingleTitle: singleTitle, + SingleURL: singleURL, + BtnOrientation: btnOrientation, + HideAvatar: hideAvatar, + }, + }) +} + +type dingResponse struct { + Errcode int + Errmsg string +} + +func (r Robot) send(msg interface{}) error { + m, err := json.Marshal(msg) + if err != nil { + return err + } + + resp, err := http.Post(r.Webhook, "application/json", bytes.NewReader(m)) + if err != nil { + return err + } + defer resp.Body.Close() + + data, err := ioutil.ReadAll(resp.Body) + if err != nil { + return err + } + + var dr dingResponse + err = json.Unmarshal(data, &dr) + if err != nil { + return err + } + if dr.Errcode != 0 { + return fmt.Errorf("dingrobot send failed: %v", dr.Errmsg) + } + + return nil +} diff --git a/backend/vendor/github.com/royeo/dingrobot/message.go b/backend/vendor/github.com/royeo/dingrobot/message.go new file mode 100644 index 00000000..82f286f8 --- /dev/null +++ b/backend/vendor/github.com/royeo/dingrobot/message.go @@ -0,0 +1,60 @@ +package dingrobot + +const ( + msgTypeText = "text" + msgTypeLink = "link" + msgTypeMarkdown = "markdown" + msgTypeActionCard = "actionCard" +) + +type textMessage struct { + MsgType string `json:"msgtype"` + Text textParams `json:"text"` + At atParams `json:"at"` +} + +type textParams struct { + Content string `json:"content"` +} + +type atParams struct { + AtMobiles []string `json:"atMobiles,omitempty"` + IsAtAll bool `json:"isAtAll,omitempty"` +} + +type linkMessage struct { + MsgType string `json:"msgtype"` + Link linkParams `json:"link"` +} + +type linkParams struct { + Title string `json:"title"` + Text string `json:"text"` + MessageURL string `json:"messageUrl"` + PicURL string `json:"picUrl,omitempty"` +} + +type markdownMessage struct { + MsgType string `json:"msgtype"` + Markdown markdownParams `json:"markdown"` + At atParams `json:"at"` +} + +type markdownParams struct { + Title string `json:"title"` + Text string `json:"text"` +} + +type actionCardMessage struct { + MsgType string `json:"msgtype"` + ActionCard actionCardParams `json:"actionCard"` +} + +type actionCardParams struct { + Title string `json:"title"` + Text string `json:"text"` + SingleTitle string `json:"singleTitle"` + SingleURL string `json:"singleURL"` + BtnOrientation string `json:"btnOrientation,omitempty"` + HideAvatar string `json:"hideAvatar,omitempty"` +} diff --git a/backend/vendor/modules.txt b/backend/vendor/modules.txt index a73e7de6..dd2a41b3 100644 --- a/backend/vendor/modules.txt +++ b/backend/vendor/modules.txt @@ -82,6 +82,8 @@ github.com/olekukonko/tablewriter github.com/pelletier/go-toml # github.com/pkg/errors v0.8.1 github.com/pkg/errors +# github.com/royeo/dingrobot v1.0.0 +github.com/royeo/dingrobot # github.com/satori/go.uuid v1.2.0 github.com/satori/go.uuid # github.com/shurcooL/sanitized_anchor_name v1.0.0