V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
not4jerk
V2EX  ›  Go 编程语言

100 行 golang 代码:钉钉机器人舆情监控

  •  3
     
  •   not4jerk ·
    mojocn · 2018-02-10 12:19:36 +08:00 · 3258 次点击
    这是一个创建于 2471 天前的主题,其中的信息可能已经有所发展或是发生改变。

    1. 资料

    1.1.第三方包
    1.2.接口

    2. 初始化项目变量

    package main
    
    import (
    	"fmt"
    	"log"
    	"github.com/PuerkitoBio/goquery"
    	"github.com/go-redis/redis"
    	"net/http"
    	"bytes"
    	"github.com/astaxie/beego/toolbox"
    )
    
    var (
    	redisClient *redis.Client //redis 缓存
            //钉钉群机器人 webhook 地址
    	dingdingURL = "https://oapi.dingtalk.com/robot/send?access_token=dingding_talk_group_bot_webhook_token"
            //百度新闻搜索关键字 URL
    	baiduNewsUrlWithSearchKeyword = "http://news.baidu.com/ns?cl=2&rn=20&tn=news&word=%E7%89%A9%E8%81%94%E7%BD%91"
    )
    
    const (
    	newsFeed = "news_feed"//爬取到的百度新闻 redis key
    	newsPost = "news_post"//已发送的百度新闻 redis key
    	newsList = "iot_news" //储存了的百度新闻 redis key
    )
    //实例化 redis 缓存
    func init() {
    	redisClient = redis.NewClient(&redis.Options{
    		Addr:     "127.0.0.1:6379",
    		Password: "ddfrfgtre4353252", // redis password
    		DB:       0,                            // redis 数据库 ID
    	})
    }
    

    在机器人管理页面选择“自定义”机器人,输入机器人名字并选择要发送消息的群。如果需要的话,可以为机器人设置一个头像。点击“完成添加”。

    点击“复制”按钮,即可获得这个机器人对应的 Webhook 地址,赋值给 dingdingURl

    3 func newsBot

    3.1 使用 goquery 和网页元素选择器语法提取有用信息
    func newsBot() error {
    	// 获取 html doc
    	doc, err := goquery.NewDocument(baiduNewsUrlWithSearchKeyword)
    	if err != nil {
    		return nil
    	}
            //使用 redis pipeline 减少 redis 连接数
    	pipe := redisClient.Pipeline()
    	// 使用 selector xpath 语法获取有用信息
            // 储存新闻到 redis 中 newsList
            // 储存新闻 ur 到 redis-set 建 newfeed 为以后是用 sdiff 找出没有发送的新闻
    
    
    	doc.Find("div.result").Each(func(i int, s *goquery.Selection) {
    		// For each item found, get the band and title
    		URL, _ := s.Find("h3 > a").Attr("href")
    		Source := s.Find("p.c-author").Text()
    		Title := s.Find("h3 > a").Text()
    		markdown := fmt.Sprintf("- [%s](%s) _%s_", Title, URL, Source)
    		pipe.HSet(newsList, URL, markdown)
    		pipe.SAdd(newsFeed, URL)
    	})
            //执行 redis pipeline
    	pipe.Exec()
    
    3.2 排除已发送的新闻,拼接 markdown 字符串
            //使用 redis sdiff 找出没有发送的新闻 url
    	unSendNewsUrls := redisClient.SDiff(newsFeed, newsPost).Val()
            //新闻按 dingding 文档 markdonw 规范拼接
            
    	content := ""
    	for _, url := range unSendNewsUrls {
    		md := redisClient.HGet(newsList, url).Val()
    		content = content + " \n " + md
                    //记录已发送新闻的 url 地址
    		pipe.SAdd(newsPost, url)
    	}
    	pipe.Exec()
    
    3.3 调用钉钉群机器人接口
            //如果有未发送新闻 请求钉钉 webhook
    	if content != "" {
    		formt := `
    		{
    			"msgtype": "markdown",
    			"markdown": {
    				"title":"IOT 每日新闻",
    				"text": "%s"
    			}
    		}`
    		body := fmt.Sprintf(formt, content)
    		jsonValue := []byte(body)
                    //发送消息到钉钉群使用 webhook
                    //xiang 见钉钉文档 https://open-doc.dingtalk.com/docs/doc.htm?spm=a219a.7629140.0.0.karFPe&treeId=257&articleId=105735&docType=1
    		resp, err := http.Post(dingdingURL, "application/json", bytes.NewBuffer(jsonValue))
    		if (err != nil) {
    			return err
    		}
    		log.Println(resp)
    	}
    	return nil
    }
    

    func newsBot函数完成

    4. 设置定时任务

    func main() {
            //销毁 redisClient
    	defer redisClient.Close()
    
    	//创建定时任务
            //每天 8 点 13 点 18 点 自动执行爬虫和机器人
            // 
    	dingdingNewsBot := toolbox.NewTask("dingding-news-bot", "0 0 8,13,18 * * *", newsBot)
    	//dingdingNewsBot := toolbox.NewTask("dingding-news-bot", "0 40 */1 * * *", newsBot)
    	//err := dingdingNewsBot.Run()
    	//检测定时任务
    	// if err != nil {
    	// 	log.Fatal(err)
    	// }
    	//添加定时任务
    	toolbox.AddTask("dingding-news-bot", dingdingNewsBot)
    	//启动定时任务
    	toolbox.StartTask()
    	defer toolbox.StopTask()
    	select {}
    }
    

    spec 格式是参照

    5 最终代码

    6 编译运行

    go build main.go
    nohup ./main &
    

    最终效果 dingding-webhook-bot

    7 最后

    6 条回复    2018-02-11 16:51:28 +08:00
    not4jerk
        1
    not4jerk  
    OP
       2018-02-10 12:24:02 +08:00
    [v2 版本支持多关键字,分批发送 main.go ]( https://gist.github.com/mojocn/9b18db2c99b01e49ce6afbbb2322e07a)
    okeydokey
        2
    okeydokey  
       2018-02-10 15:30:49 +08:00   ❤️ 1
    大野你暴露了
    hlwjia
        3
    hlwjia  
       2018-02-10 18:58:34 +08:00   ❤️ 1
    惊现 access_token,revoke 一下或者刷新一下吧
    xupefei
        4
    xupefei  
       2018-02-10 19:09:35 +08:00   ❤️ 1

    not4jerk
        5
    not4jerk  
    OP
       2018-02-11 16:10:30 +08:00
    @xupefei
    @hlwjia
    @okeydokey 死鬼 太坏了 我以为 gist 没有版本控制功能
    hlwjia
        6
    hlwjia  
       2018-02-11 16:51:28 +08:00
    @not4jerk 在人家堂堂做版本控制的公司的网站上,竟然以为没有版本控制?

    too young :doge:
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1439 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 31ms · UTC 17:36 · PVG 01:36 · LAX 09:36 · JFK 12:36
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.