写在前面
高效的团队往往会充分利用不同的工具来提升自己的工作效率,譬如通过钉钉进行沟通协同,使用 Trello 进行任务管理,使用 GitLab 或者 GitHub 来进行代码管理,使用 JIRA 来进行项目与事务跟踪等等。不同的工具分工合作,把团队的事务数字化管理流转起来,并在一定程度上实现了流程的自动化,将大家从一些繁琐的事务中解放出来,有效地提升了大家的协同能力和工作质量。
本文来讨论一下利用 node 接入机器人来实现一些日常办公小功能的自动化。
什么是群机器人?群机器人是钉钉群的高级扩展功能。群机器人可以将第三方服务的信息聚合到群聊中,实现自动化的信息同步。
首先来看一下案例:
博客机器人,cicd 触发时将新文章推送到群

bug 提醒,每天定时推送现有 bug 到群

如何接入自己的钉钉机器人?
首先打开钉钉群右上角,点群设置。找到智能群助手,点击添加机器人。

这里要选择自定义机器人,安全策略要选择加签。
这里我们拿到密钥,点击继续,拿到机器人的 webhook。
关于签名的算法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| const crypto = require('crypto');
const token = 'e5f004c907173f8188eca8696f6fa3d3a841e4174733bae334057e3e769e32ae';
const timestamp = +new Date();
const secret = 'SEC32f1a057885884cadf5eea01e713e54d4b97bccd85bcc86187ac735dba26b7c7'; const stringToSign = timestamp + '\n' + secret;
const hmac = crypto.createHmac('sha256', secret);
hmac.update(stringToSign, 'utf-8');
const sign = encodeURIComponent(hmac.digest('base64'));
const url = `https://oapi.dingtalk.com/robot/send?access_token=${token}×tamp=${timestamp}&sign=${sign}`;
|
本质上是我们向钉钉的 openAPI 发送一个 post 请求。只要我们拿到正确的 url 和请求格式,就可以向机器人发送消息。这样就完成了钉钉机器人的接入。
消息类型及数据格式
1 2 3 4 5 6 7 8 9 10 11 12
| const axios = require('axios');
axios.defaults.headers.post['Content-Type'] = 'application/json;charset=UTF-8'; try { axios.post(url, msg).then(function (response) { console.log(response.data); console.log(response.status); console.log(response.statusText); }); } catch (error) { console.error(error); }
|
Example
blog 机器人
在 cicd 中配置在build的时候执行脚本,找出新增文章推送到群。
主要问题在于如何找出新的文章。
在构建时,会临时生成 db.json
文件,这个文件不会被push,部署时会被删除。我们可以每次构建时保存一份文章列表,一起推送到git上面去。这样我们每次构建完成后,部署之前,先读取旧的文章名单,然后再读取 db.json
,对比找出新的文章。
每篇文章我们要生成摘要,方法是:找到第一个 markdown
的 ##
标记,将标题后面的文章的第一句话当作摘要。所以希望大家尽量要写 ##
标记。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| const onlineList = JSON.parse(JSON.stringify(oldList)).map(value => value = value.title);
const list = []; const reg = new RegExp(`((#)?(##)?(###)? ([\u4e00-\u9fa5A-Za-z0-9^%&',;=??$\/x22。,,.()()、]{0,})?\\n)(\\n)?([\u4e00-\u9fa5A-Za-z0-9^%&',;=?$\/x22,,.()()、 ]{0,})`); fs.readFile('./db.json', (err, data) => { if (err) throw err; JSON.parse(data).models.Post.forEach(element => { let abstract = reg.exec(element._content)[0]; abstract = abstract.split(`\n`); abstract = abstract[abstract.length - 1] ? `${abstract.pop()}...` : '暂无摘要'; let url = element.source; url = url.replace('_posts','') list.push({ title: element.title, abstract: abstract, }) });
fs.writeFile('./article.json', JSON.stringify(list), () => { });
let newArticle; for (i of list) { if (!onlineList.includes(i.title)) { newArticle = i; break; } } }
|
禅道bug机器人
目的是实现从禅道拉去 bug
清单,过滤出特定人员,发送到指定群。未来todo要实现能单独@机器人,他会把属于你的bug发给你。
难点在于如何从禅道拉取 bug
清单。禅道不是 Restful
的,他是用后端模板渲染的 html
返回的方式。要解决的问题是,模拟登录和拉取数据。
这里采用的方案是使用 puppeteer
包,作用是生成一个无头的浏览器,这样就可以操作DOM元素了。
puppeteer文档
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| const browser = await puppeteer.launch({ headless: true }); const page = await browser.newPage(); await page.goto('https://zentao.jifenn.com/pro/bug-browse-10--unclosed-0--130-500-1.html');
await new Promise(resolve => setTimeout(resolve, 800));
await page.waitForSelector('#account'); await page.waitForSelector('#submit');
await (await page.$('#account')).type(config.username); await (await page.$("input[name='password']")).type(config.password); await (await page.$('#submit')).click();
await browser.close(); ```
需要注意的是,代码执行的时候,dom树可能还没渲染,所以要记得await某个元素。 编辑消息的时候,为了简化操作,可以利用 `ejs` 配置模板。有点像 `jsp` 的语法
```jsp { %> 1. [[<%=i.id %>] <%= i.title %>](<%='https://zentao.jifenn.com'+i.url %>) - <%=i.assignedTo %> <% } %>
|
最后
希望这篇文章能给大家带来启发,不仅仅是机器人,大家都能搭建适合自己的自动化脚本。社会发展就是机器取代人,简单重复的工作被取代。而我们人最重要的一点,就是机器没有的思维。发动我们聪明绝顶的脑袋想一想,集思广益,助飞效能提升。