美文网首页
基于scala向钉钉群组发送消息实践

基于scala向钉钉群组发送消息实践

作者: 小赵营 | 来源:发表于2019-12-05 19:27 被阅读0次

    一个祥和的月黑风高夜,系统在有条不紊运行着. lol s9赛季群里有一句没一句的聊着,但钉钉声此起彼伏,各种系统信息接踵而至,大家把注意力从视频直播,转向钉钉消息.有针对性的各自分析问题,很快钉钉声再次停歇,大家欢庆lpl胜利,嗨皮继续.
    --- 钉钉使用实录

    钉钉有了阿里加成,成为最近几年最热门即时通信和办公软件.大有微信主导日常,钉钉支持工作的趋势.在众多的功能中,支持向群组发送消息给运维提供了方便, 引言就是运维的一个例子.

    本文实现基于scala向钉钉群组发送消息功能.

    内容导航
    • 实现原理 - 介绍实现的原理

    • 开发优缺点对比

    • 代码外部依赖

    • 功能介绍 - 分别支持功能,实现方式

    • 代码

    实现原理

    钉钉聊天群内支持的群机器人, 类似QQ 群机器人, 可以发天气, 讲笑话那样; 钉钉群机器人支持自定义机器人, 允许开发者管理机器人做预警消息通知.本文是基于该功能实现向群组发送消息功能的.

    咱们是使用自定义机器人来完成消息发送.钉钉给我们开了一扇窗,我们要好好使用.

    钉钉群机器人官方自我介绍,详见传送门机器人自我介绍, 也可以使用链接地址: https://ding-doc.dingtalk.com/doc#/serverapi3/iydd5h

    在赘述下,钉钉支持的消息样式:

    • text类型

    • link类型

    • markdown类型

    • 整体跳转ActionCard类型

    • 独立跳转ActionCard类型

    • FeedCard类型

    从简约到典雅格式,反正总有一款格式适合你.我们实现的基本的文本格式.

    开发方式
    • SDK jar的方式开发
      官方提供Jar包,包含需要的API,可以直接使用.

      • 优点

      它是官方提供封装依赖包,提供完整的功能支持和依赖包含.只需要调用api接口完成.https://ding-doc.dingtalk.com/doc#/serverapi3/iydd5h测试机器人章节还提供javaPHP的样例.

      • 缺点

      jar包并不是官方提供的标准api,发生变化如何识别,是个问题.另外,如何同步更新依赖也是个问题.

    • 通过Http的方式开发

      • 优点

        不需要依赖官方依赖包,满足官方消息格式就能完成.

      • 缺点

        重复造轮子.嘎嘎,用等哥的话说,其实我不怕.

    接下来,我们展示代码实现逻辑.

    代码依赖

    代码开发依赖库如下所示,版本使用根据自己需要选择.

    <dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>${fastjson.version}</version>
    </dependency>
    <dependency>
    <groupId>commons-configuration</groupId>
    <artifactId>commons-configuration</artifactId>
    <version>${configuration.version}</version>
    </dependency>

    <dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>${httpclient.version}</version>

    </dependency>
    <dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpcore</artifactId>
    <version>${httpcore.version}</version>
    </dependency>

    功能介绍
    • 支持代理服务器发送消息

      应用系统部署环境如果是需要公司网关,消息直接发送就会失败.代理设置是解决的思路.我们支持设置代理的功能,用户根据需要设置代理服务器.实现代码如下:

      def setProxy(proxy: String, port: Int, username: String, psswd: String): (RequestConfig, BasicCredentialsProvider) = {
      import org.apache.http.auth.{AuthScope, UsernamePasswordCredentials}
      val host = new HttpHost(proxy, port)
      val provider = new BasicCredentialsProvider()
      provider.setCredentials(new AuthScope(host), new UsernamePasswordCredentials(username, psswd))

      (RequestConfig.custom.setProxy(host).build, provider)
      }

      proxy, port, username, psswd代理设置需要参数,返回类型是RequestConfig, BasicCredentialsProvider,在后面调用是需要设置到httpClient和HttpPost中,后文会说明.

    • 支持文本格式发送

      钉钉支持固定几类消息发送,错误的格式导致响应失败.正确的消息样式如下:

      { "msg":{ "msgtype":"text", "text":{ "content":"test #keyword#" } } }

      代码实现如下:

      public class TextMessage {
      private String text;
      private TextMessage() {};
      public TextMessage(String text) {
      this.text = text;
      }

      public String toJsonString() {
      Map<String, Object> items = new HashMap<String, Object>();
      items.put("msgtype", "text");

      Map<String, String> textContent = new HashMap<String, String>();
      byte[] byt = StringUtils.getBytesUtf8(text);
      String txt = StringUtils.newStringUtf8(byt);
      textContent.put("content", txt);
      items.put("text", textContent);

      return JSON.toJSONString(items);
      }
      }

    • 支持安全方式:群组关键词

      为了安全,钉钉支持多种消息校验机制,除了access token之外,用户可选择关键词模式,消息加密模式,和过滤ip地址方式.
      这种方式在消息体内包含群组机器指定的关键字,群组可以正常接受消息.
      我们选择关键词模式.

    • 支持Http请求发送

      http请求包括构建HttpPost和send2个接口.如下所示:

      • 构建HttpPost

        def constructHttpPost(msg: String): HttpPost = {
        val post = new HttpPost(webHook + accessToken)
        val entity = new StringEntity(msg, StandardCharsets.UTF_8)
        post.setEntity(entity)
        post.addHeader("Content-Type", "application/json; charset=utf-8")

        post
        }

      • 消息发送send 接口

        val post = constructHttpPost(msg)
        val client: CloseableHttpClient = if (isProxyEnable) {
        //根据是否使用代理来决定http post参数设置
        val (config, provider) = setProxy(proxy, port, username, password)
        post.setConfig(config)
        HttpClients.custom().setDefaultCredentialsProvider(provider).build()
        } else {
        HttpClients.custom().build()
        }

        val rsp = client.execute(post) //执行send操作

    • 完整实现代码:

      //webHook,accessToken组合起来构成发送的https链接,在设置群组机器人时,记得保存哦.
      //群组机器设置方式: https://ding-doc.dingtalk.com/doc#/serverapi3/iydd5h
      //根据自己的需要传递过来
      class Robot(webHook: String, accessToken: String) {
      lazy val jsonObj = new JSONObject()

      //校验代理是否被激活
      def isProxyEnable: Boolean = proxy.nonEmpty && port != 0

      def setProxy(proxy: String, port: Int, username: String, psswd: String): (RequestConfig, BasicCredentialsProvider) = {
      import org.apache.http.auth.{AuthScope, UsernamePasswordCredentials}
      val host = new HttpHost(proxy, port)
      val provider = new BasicCredentialsProvider()
      provider.setCredentials(new AuthScope(host), new UsernamePasswordCredentials(username, psswd))

      (RequestConfig.custom.setProxy(host).build, provider)
      }

      def constructHttpPost(msg: String): HttpPost = {
      val post = new HttpPost(webHook + accessToken)
      val entity = new StringEntity(msg, StandardCharsets.UTF_8)
      post.setEntity(entity)
      post.addHeader("Content-Type", "application/json; charset=utf-8")

      post
      }

      def sendMsg(msg: String): DingTalkResponse = {
      val res = DingTalkResponse(HttpStatus.SC_NOT_FOUND)
      val post = constructHttpPost(msg)
      val client: CloseableHttpClient = if (isProxyEnable) {
      val (config, provider) = setProxy(proxy, port, username, password)
      post.setConfig(config)
      HttpClients.custom().setDefaultCredentialsProvider(provider).build()
      } else {
      HttpClients.custom().build()
      }

      try {
      val rsp = client.execute(post) //消息发送
      try {//资源释放处理
      if (rsp.getStatusLine().getStatusCode == HttpStatus.SC_OK) {
      val result: String = EntityUtils.toString(rsp.getEntity)
      val toJson = jsonObj.getJSONObject(result)
      println(result)
      val errCode = toJson.getInteger("errcode")
      val msg = toJson.getString("errmsg")
      res.copy(status = HttpStatus.SC_OK, errorCode = errCode, errMsg = msg)
      }
      }
      catch {
      case io: IOException => println(s"http response IOException {io}") case cpe: ClientProtocolException => println(s"http ClientProtocolException{cpe}")
      case ex: Exception => throw ex
      } finally if (rsp != null) rsp.close()
      } catch {
      case ex: Exception => println(s"http response exception ${ex}")
      } finally {
      if (client != null) client.close()
      }

      res
      }
      }
      //结果响应解析类
      case class DingTalkResponse(status: Int, errorCode: Int = 0, errMsg: String = "")
      把依赖库,和上面代码组合就可以直接运行了哦

    后记

    本文代码实现文本消息发送,如果看官姥爷有需要,其它方式代码也会贴出来.
    希望你能有收获,高兴的话上个赞撒.

    相关文章

      网友评论

          本文标题:基于scala向钉钉群组发送消息实践

          本文链接:https://www.haomeiwen.com/subject/uwrhgctx.html