基金估值通知小助手
由于经常忘记基金,昨天突发奇想,可以爬取当前估值信息然后利用钉钉机器人+crontab发送通知,效果图
获取估值信息
首先需要获取估值信息,组装发送的内容的格式
func fundMsg() string {
result := `
## 基金估值情况
---
`
c := colly.NewCollector(
colly.AllowedDomains("fund.eastmoney.com", "fundgz.1234567.com.cn"),
)
c.OnRequest(func(r *colly.Request) {
//fmt.Println("Visiting", r.URL.String())
})
c.OnResponse(func(response *colly.Response) {
res := string(response.Body)
if len(res) > 10 {
jsonStr := res[8 : len(res)-2]
var mapResult map[string]interface{}
if err := json.Unmarshal([]byte(jsonStr), &mapResult); err != nil {
fmt.Println(err)
}
var inc string
gszzl := fmt.Sprintf("%s", mapResult["gszzl"])
if strings.Index(gszzl, "-") == -1 {
inc = "↑"
} else {
inc = "↓"
}
result += fmt.Sprintf("* %s(%s)\t%s\t%s\n", mapResult["name"], mapResult["fundcode"], mapResult["gszzl"], inc)
}
})
for _, value := range fundCodes {
_ = c.Visit("http://fundgz.1234567.com.cn/js/" + value + ".js")
}
return result
}
发送钉钉通知
翻了下钉钉开发文档,上面没有go的sdk,不过好在文档比较详细,主要是签名算法的生成比较麻烦 (主要原因是对go不熟悉) ,代码如下:
签名
func sign(timestamp int64) string {
signStr := strconv.FormatInt(timestamp, 10) + "\n" + secret
h := hmac.New(sha256.New, []byte(secret))
h.Write([]byte(signStr))
return url.QueryEscape(base64.StdEncoding.EncodeToString(h.Sum(nil)))
}
发送通知
这是发送markdown格式的通知,也可以发送其他格式的通知,具体参考钉钉开发文档
func dingTaskNotice(fundInfo func() string) string {
timestamp := time.Now().UnixNano() / 1e6
sendUrl := fmt.Sprintf("https://oapi.dingtalk.com/robot/send?access_token=%s×tamp=%d&sign=%s", accessToken, timestamp, sign(timestamp))
data := fmt.Sprintf(`{
"msgtype": "markdown",
"markdown": {
"title": "基金估值",
"text": "%s"
}
}`, fundInfo())
buf := &bytes.Buffer{}
err := binary.Write(buf, binary.BigEndian, []byte(data))
if err != nil {
panic(err)
}
res, err := http.Post(sendUrl, "application/json", buf)
if err != nil {
panic(err)
}
defer res.Body.Close()
result, err := ioutil.ReadAll(res.Body)
if err != nil {
panic(err)
}
return string(result)
}
部署
打包成可执行文件
$ set GOOS=linux
$ go build
crontab调度
$ chmod +x fund_notice
$ crontab -e
# 可根据需求修改
*/30 10-15 * * 1-5 /data/jobs/fund_notice
:wq
$ systemctl restart crond.service
完整代码
package main
import (
"bytes"
"crypto/hmac"
"crypto/sha256"
"encoding/base64"
"encoding/binary"
"encoding/json"
"fmt"
"github.com/gocolly/colly"
"io/ioutil"
"net/http"
"net/url"
"strconv"
"strings"
"time"
)
var (
secret = "secret" // 替换
accessToken = "accessToken" // 替换
fundCodes = []string{
"162605", "260108", "009898", "110011", "005368", "007824", "110022", "001632",
"001475", "501019", "001838", "320007", "519674", "002547", "006229", "004851",
"008086", "000136", "001063", "000408", "501058", "501057", "162703", "001983",
"161725", "501018", "040025", "005918"}
)
func sign(timestamp int64) string {
signStr := strconv.FormatInt(timestamp, 10) + "\n" + secret
h := hmac.New(sha256.New, []byte(secret))
h.Write([]byte(signStr))
return url.QueryEscape(base64.StdEncoding.EncodeToString(h.Sum(nil)))
}
func dingTaskNotice(fundInfo func() string) string {
timestamp := time.Now().UnixNano() / 1e6
sendUrl := fmt.Sprintf("https://oapi.dingtalk.com/robot/send?access_token=%s×tamp=%d&sign=%s", accessToken, timestamp, sign(timestamp))
data := fmt.Sprintf(`{
"msgtype": "markdown",
"markdown": {
"title": "基金估值",
"text": "%s"
}
}`, fundInfo())
buf := &bytes.Buffer{}
err := binary.Write(buf, binary.BigEndian, []byte(data))
if err != nil {
panic(err)
}
res, err := http.Post(sendUrl, "application/json", buf)
if err != nil {
panic(err)
}
defer res.Body.Close()
result, err := ioutil.ReadAll(res.Body)
if err != nil {
panic(err)
}
return string(result)
}
func fundMsg() string {
result := `
## 基金估值情况
---
`
var funds []map[string]interface{}
c := colly.NewCollector(
colly.AllowedDomains("fund.eastmoney.com", "fundgz.1234567.com.cn"),
)
c.OnRequest(func(r *colly.Request) {
//fmt.Println("Visiting", r.URL.String())
})
c.OnResponse(func(response *colly.Response) {
res := string(response.Body)
if len(res) > 10 {
jsonStr := res[8 : len(res)-2]
var mapResult map[string]interface{}
if err := json.Unmarshal([]byte(jsonStr), &mapResult); err != nil {
fmt.Println(err)
}
funds = append(funds, mapResult)
}
})
for _, value := range fundCodes {
_ = c.Visit("http://fundgz.1234567.com.cn/js/" + value + ".js")
}
sort(funds)
for _, value := range funds {
var inc string
gszzl := fmt.Sprintf("%s", value["gszzl"])
if strings.Index(gszzl, "-") == -1 {
inc = "↑"
} else {
inc = "↓"
}
result += fmt.Sprintf("* %s(%s)\t%s\t%s\n", value["name"], value["fundcode"], value["gszzl"], inc)
}
return result
}
func sort(funds []map[string]interface{}) {
for i, value1 := range funds {
gszzl1, _ := strconv.ParseFloat(fmt.Sprintf("%s", value1["gszzl"]), 10)
for j, value2 := range funds {
gszzl2, _ := strconv.ParseFloat(fmt.Sprintf("%s", value2["gszzl"]), 10)
if gszzl1 > gszzl2 {
funds[i], funds[j] = funds[j], funds[i]
}
}
}
}
func main() {
//fmt.Println(fundMsg())
fmt.Println(dingTaskNotice(fundMsg))
}