xccjk / x-blog

学习笔记
18 stars 2 forks source link

钉钉机器人 #88

Open xccjk opened 1 year ago

xccjk commented 1 year ago

最近在做前端打包发布平台,需要在每次发布完成之后将构建信息发布到钉钉群里,后面就想到在Jenkins中接入钉钉通知,来实现相关功能

钉钉新建群聊,在群里添加机器人

  1. 桌面端钉钉发起群聊,点击群设置,智能群助手

1

  1. 机器人管理-添加机器人-自定义机器人

2

  1. 自定义机器人-添加-填写机器人相关信息

安全设置根据自己公司的需求来选择就可以了,最简单的就是 自定义关键字来测试

4

  1. 信息填写完成提交后可以看到生成的Webhook信息了

5

6

此时就可以看到群里通知创建了一个机器人了

7

Jenkins安装钉钉插件

  1. Jenkins登录后,系统管理-插件管理中搜索dingTalk即可看到对应插件

8

  1. 系统管理-系统配置中可以看到钉钉相关配置信息

填写相关配置信息,把前面获取的webhook复制过来,机器人ID记得不要重复,记得填写关键字,和插件群机器人的关键字保持一致,信息填写完成后,点击测试,就可以看到群聊里面会推送一条信息

9

10

  1. 在指定项目中使用钉钉通知

选择一个已创建的项目(如果没有创建的话创建一个就好了,没什么影响),点击配置,会发现再General中会有一个钉钉机器人的选项,勾选即可,通知人按照自己的实际情况来(全部通知或者通知部分人)

11

到了这一步,钉钉机器人通知都配置完了,但是我当时遇到了一个天坑的事情........

配置完我点击测试, 群里面会收到通知选项,但是我构架项目时不会收到相关构建信息,心态爆炸....

重要的事情说三遍:

**千万千万要记得,在配置完了后,重启一下Jenkins,不然可能会有问题

千万千万要记得,在配置完了后,重启一下Jenkins,不然可能会有问题

千万千万要记得,在配置完了后,重启一下Jenkins,不然可能会有问题**

成功通知的样子:

12

xccjk commented 1 year ago

常见错误问题

图片不展示

@指定的人不成功

以markdown格式举例,常用的@指定人的方式,一种是手机号,一种是通过userId来指定的

手机号@指定的人

手机号@指定的人,在atMobiles中填入手机号后,一定要在text字段中拼接手机号信息

  {
    "msgtype": "markdown",
    "markdown": {
      "title": `打包发布通知`,
      "text": `#### 应用发布成功 @150xxxxxxxx \n > ...`,
    },
    "at": {
      "atMobiles": ['150xxxxxxxx'],
      "atDingtalkIds": [],
      "isAtAll": false
    }
  }

userId@指定的人

userId@指定的人,官方文档好像没及时更新,使用atUserIds字段一直不会成功通知,换为atDingtalkIds就可以了。同时,userId的信息也需要拼接再text字段中

  {
    "msgtype": "markdown",
    "markdown": {
      "title": `打包发布通知`,
      "text": `#### 应用发布成功 @userId1,@userId2 \n > ...`,
    },
    "at": {
      "atMobiles": [],
      "atDingtalkIds": ['userId1', 'userId2'],
      "isAtAll": false
    }
  }

text字段中文本换行错误

对于多行文本换行,应该通过\n\n来换行

  const msgs = ['commit记录1', 'commit记录2', 'commit记录3']
  const desc = msgs.join('\n\n')
  {
    "msgtype": "markdown",
    "markdown": {
      "title": `打包发布通知`,
      "text": `#### 应用发布成功 ${desc}`,
    },
    "at": {
      "atMobiles": [],
      "atDingtalkIds": [],
      "isAtAll": false
    }
  }

使用方式

我这边是在node中做的钉钉机器人发送通知

  // robot.js
  #!/usr/bin/env node
  const https = require('https');

  module.exports = function(token) {
    return {
      send: function(message, callback) {
        const postData = JSON.stringify(message);
        const options = {
          hostname: 'api.dingtalk.com',
          port: 443,
          path: '/robot/send?access_token=' + token,
          method: 'POST',
          headers: {
            'Content-Type': 'application/json'
          }
        };
        const request = https.request(options, function(response) {
          const data = [];
          let count = 0;
          response.setEncoding('utf8');

          response.on('data', function(chunk) {
            data.push(chunk);
            count += chunk.length;
          });

          response.on('end', function() {
            let buffer;
            const length = data.length;

            if (length == 0) {
              buffer = new Buffer(0);
            } else if (length == 1) {
              buffer = data[0];
            } else {
              buffer = new Buffer(count);
              for (let index = 0, position = 0; index < length; index++) {
                let chunk = data[index];
                chunk.copy(buffer, position);
                position += chunk.length;
              }
            }

            const datastring = buffer.toString();
            const result = JSON.parse(datastring);
            if (result.errcode) {
              return callback(new Error(result.errmsg));
            }

            return callback(null, result);
          });
        });
        request.on('error', callback);

        request.write(postData);
        request.end();
      }
    };
  };
  // index.js
  #!/usr/bin/env node
  const robot = require('./robot')

  const robotConfig = {
    token: '',
    // 需要@人员手机号
    atMobiles: [],
    // 被@人的用户ID
    atUserIds: [],
    // 是否需要@所有人
    isAtAll: false,
    // 指定打包机器人
    robot: 1,
    // 打包编译开启进程数
    threads: 8,
    // 钉钉群通知关键字
    keyword: ['发布'],
  };

  const dingdingMessage = () => {
  const { token, atMobiles = [], atUserIds = [], isAtAll = false, keyword = [] } = robotConfig || {}
  const msgs = ['commit记录1', 'commit记录2', 'commit记录3'].join('\n\n')
  if (!token) {
    console.log('------钉钉机器人token不存在------')
    return false
  }
  if (!keyword.length) {
    console.log('------请填写钉钉机器人关键字------')
    return false
  }
  // 手机号列表
  const notify = atMobiles.length ? atMobiles.map(item => `@${item}`) : ''
  // userId列表
  const userIdList = atUserIds.length ? atUserIds.map(item => `@${item}`) : ''
  // 关键字列表
  const keywordList = keyword.join('')
  robot(token).send(
  {
    "msgtype": "markdown",
    "markdown": {
      "title": `打包发布通知${keywordList}`,
      "text": `#### 应用${projectConfig.projectname}发布${type ? '成功' : '失败'} ${notify} ${userIdList} \n > ${config.desc} \n\n 最近三条提交: \n\n ${msgs}`,
    },
    "at": {
      "atMobiles": atMobiles,
      "atDingtalkIds": atUserIds,
      "isAtAll": isAtAll
    }
    }, (err, data) => {
      if (err) {
        if (err.toString().indexOf('keywords not in content') !== -1) {
          console.log('------机器人关键词不匹配,请修改------')
        } else {
          console.error(err);
        }
        return
      }
      console.log(data);
      if (data.errcode === 0) {
        process.exit()
      }
    })
  }
avlee commented 1 year ago

atMobiles 和 atUserIds 是二选一还是配对使用?钉钉的文档也看不出来,一直以为是配对使用。