org.apache.dolphinscheduler.plugin.alert.dingtalk.DingTalkSender Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.plugin.alert.dingtalk;
import org.apache.dolphinscheduler.alert.api.AlertResult;
import org.apache.dolphinscheduler.spi.utils.JSONUtils;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
* https://open.dingtalk.com/document/robots/custom-robot-access
* https://open.dingtalk.com/document/robots/customize-robot-security-settings
*
*/
public final class DingTalkSender {
private static final Logger logger = LoggerFactory.getLogger(DingTalkSender.class);
private final String url;
private final String keyword;
private final String secret;
private String msgType;
private final String atMobiles;
private final String atUserIds;
private final Boolean atAll;
private final Boolean enableProxy;
private String proxy;
private Integer port;
private String user;
private String password;
DingTalkSender(Map config) {
url = config.get(DingTalkParamsConstants.NAME_DING_TALK_WEB_HOOK);
keyword = config.get(DingTalkParamsConstants.NAME_DING_TALK_KEYWORD);
secret = config.get(DingTalkParamsConstants.NAME_DING_TALK_SECRET);
msgType = config.get(DingTalkParamsConstants.NAME_DING_TALK_MSG_TYPE);
atMobiles = config.get(DingTalkParamsConstants.NAME_DING_TALK_AT_MOBILES);
atUserIds = config.get(DingTalkParamsConstants.NAME_DING_TALK_AT_USERIDS);
atAll = Boolean.valueOf(config.get(DingTalkParamsConstants.NAME_DING_TALK_AT_ALL));
enableProxy = Boolean.valueOf(config.get(DingTalkParamsConstants.NAME_DING_TALK_PROXY_ENABLE));
if (Boolean.TRUE.equals(enableProxy)) {
port = Integer.parseInt(config.get(DingTalkParamsConstants.NAME_DING_TALK_PORT));
proxy = config.get(DingTalkParamsConstants.NAME_DING_TALK_PROXY);
user = config.get(DingTalkParamsConstants.NAME_DING_TALK_USER);
password = config.get(DingTalkParamsConstants.NAME_DING_TALK_PASSWORD);
}
}
private static HttpPost constructHttpPost(String url, String msg) {
HttpPost post = new HttpPost(url);
StringEntity entity = new StringEntity(msg, StandardCharsets.UTF_8);
post.setEntity(entity);
post.addHeader("Content-Type", "application/json; charset=utf-8");
return post;
}
private static CloseableHttpClient getProxyClient(String proxy, int port, String user, String password) {
HttpHost httpProxy = new HttpHost(proxy, port);
CredentialsProvider provider = new BasicCredentialsProvider();
provider.setCredentials(new AuthScope(httpProxy), new UsernamePasswordCredentials(user, password));
return HttpClients.custom().setDefaultCredentialsProvider(provider).build();
}
private static CloseableHttpClient getDefaultClient() {
return HttpClients.createDefault();
}
private static RequestConfig getProxyConfig(String proxy, int port) {
HttpHost httpProxy = new HttpHost(proxy, port);
return RequestConfig.custom().setProxy(httpProxy).build();
}
private AlertResult checkSendDingTalkSendMsgResult(String result) {
AlertResult alertResult = new AlertResult();
alertResult.setStatus("false");
if (null == result) {
alertResult.setMessage("send ding talk msg error");
logger.info("send ding talk msg error,ding talk server resp is null");
return alertResult;
}
DingTalkSendMsgResponse sendMsgResponse = JSONUtils.parseObject(result, DingTalkSendMsgResponse.class);
if (null == sendMsgResponse) {
alertResult.setMessage("send ding talk msg fail");
logger.info("send ding talk msg error,resp error");
return alertResult;
}
if (sendMsgResponse.errcode == 0) {
alertResult.setStatus("true");
alertResult.setMessage("send ding talk msg success");
return alertResult;
}
alertResult.setMessage(String.format("alert send ding talk msg error : %s", sendMsgResponse.getErrmsg()));
logger.info("alert send ding talk msg error : {}", sendMsgResponse.getErrmsg());
return alertResult;
}
/**
* send dingtalk msg handler
*
* @param title title
* @param content content
* @return
*/
public AlertResult sendDingTalkMsg(String title, String content) {
AlertResult alertResult;
try {
String resp = sendMsg(title, content);
return checkSendDingTalkSendMsgResult(resp);
} catch (Exception e) {
logger.info("send ding talk alert msg exception : {}", e.getMessage());
alertResult = new AlertResult();
alertResult.setStatus("false");
alertResult.setMessage("send ding talk alert fail.");
}
return alertResult;
}
private String sendMsg(String title, String content) throws IOException {
String msg = generateMsgJson(title, content);
HttpPost httpPost = constructHttpPost(org.apache.dolphinscheduler.spi.utils.StringUtils.isBlank(secret) ? url : generateSignedUrl(), msg);
CloseableHttpClient httpClient;
if (Boolean.TRUE.equals(enableProxy)) {
httpClient = getProxyClient(proxy, port, user, password);
RequestConfig rcf = getProxyConfig(proxy, port);
httpPost.setConfig(rcf);
} else {
httpClient = getDefaultClient();
}
try {
CloseableHttpResponse response = httpClient.execute(httpPost);
String resp;
try {
HttpEntity entity = response.getEntity();
resp = EntityUtils.toString(entity, "UTF-8");
EntityUtils.consume(entity);
} finally {
response.close();
}
logger.info("Ding Talk send msg :{}, resp: {}", msg, resp);
return resp;
} finally {
httpClient.close();
}
}
/**
* generate msg json
*
* @param title title
* @param content content
* @return msg
*/
private String generateMsgJson(String title, String content) {
if (org.apache.dolphinscheduler.spi.utils.StringUtils.isBlank(msgType)) {
msgType = DingTalkParamsConstants.DING_TALK_MSG_TYPE_TEXT;
}
Map items = new HashMap<>();
items.put("msgtype", msgType);
Map text = new HashMap<>();
items.put(msgType, text);
if (DingTalkParamsConstants.DING_TALK_MSG_TYPE_MARKDOWN.equals(msgType)) {
generateMarkdownMsg(title, content, text);
} else {
generateTextMsg(title, content, text);
}
setMsgAt(items);
return JSONUtils.toJsonString(items);
}
/**
* generate text msg
*
* @param title title
* @param content content
* @param text text
*/
private void generateTextMsg(String title, String content, Map text) {
StringBuilder builder = new StringBuilder(title);
builder.append("\n");
builder.append(content);
if (org.apache.dolphinscheduler.spi.utils.StringUtils.isNotBlank(keyword)) {
builder.append(" ");
builder.append(keyword);
}
byte[] byt = StringUtils.getBytesUtf8(builder.toString());
String txt = StringUtils.newStringUtf8(byt);
text.put("content", txt);
}
/**
* generate markdown msg
*
* @param title title
* @param content content
* @param text text
*/
private void generateMarkdownMsg(String title, String content, Map text) {
StringBuilder builder = new StringBuilder(content);
if (org.apache.dolphinscheduler.spi.utils.StringUtils.isNotBlank(keyword)) {
builder.append(" ");
builder.append(keyword);
}
builder.append("\n\n");
if (org.apache.dolphinscheduler.spi.utils.StringUtils.isNotBlank(atMobiles)) {
Arrays.stream(atMobiles.split(",")).forEach(value -> {
builder.append("@");
builder.append(value);
builder.append(" ");
});
}
if (org.apache.dolphinscheduler.spi.utils.StringUtils.isNotBlank(atUserIds)) {
Arrays.stream(atUserIds.split(",")).forEach(value -> {
builder.append("@");
builder.append(value);
builder.append(" ");
});
}
byte[] byt = StringUtils.getBytesUtf8(builder.toString());
String txt = StringUtils.newStringUtf8(byt);
text.put("title", title);
text.put("text", txt);
}
/**
* configure msg @person
*
* @param items items
*/
private void setMsgAt(Map items) {
Map at = new HashMap<>();
String[] atMobileArray = org.apache.dolphinscheduler.spi.utils.StringUtils.isNotBlank(atMobiles) ? atMobiles.split(",") : new String[0];
String[] atUserArray = org.apache.dolphinscheduler.spi.utils.StringUtils.isNotBlank(atUserIds) ? atUserIds.split(",") : new String[0];
boolean isAtAll = Objects.isNull(atAll) ? false : atAll;
at.put("atMobiles", atMobileArray);
at.put("atUserIds", atUserArray);
at.put("isAtAll", isAtAll);
items.put("at", at);
}
/**
* generate sign url
*
* @return sign url
*/
private String generateSignedUrl() {
Long timestamp = System.currentTimeMillis();
String stringToSign = timestamp + "\n" + secret;
String sign = org.apache.dolphinscheduler.spi.utils.StringUtils.EMPTY;
try {
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(secret.getBytes("UTF-8"), "HmacSHA256"));
byte[] signData = mac.doFinal(stringToSign.getBytes("UTF-8"));
sign = URLEncoder.encode(new String(Base64.encodeBase64(signData)),"UTF-8");
} catch (Exception e) {
logger.error("generate sign error, message:{}", e);
}
return url + "×tamp=" + timestamp + "&sign=" + sign;
}
static final class DingTalkSendMsgResponse {
private Integer errcode;
private String errmsg;
public DingTalkSendMsgResponse() {
}
public Integer getErrcode() {
return this.errcode;
}
public void setErrcode(Integer errcode) {
this.errcode = errcode;
}
public String getErrmsg() {
return this.errmsg;
}
public void setErrmsg(String errmsg) {
this.errmsg = errmsg;
}
@Override
public boolean equals(final Object o) {
if (o == this) {
return true;
}
if (!(o instanceof DingTalkSendMsgResponse)) {
return false;
}
final DingTalkSendMsgResponse other = (DingTalkSendMsgResponse) o;
final Object this$errcode = this.getErrcode();
final Object other$errcode = other.getErrcode();
if (this$errcode == null ? other$errcode != null : !this$errcode.equals(other$errcode)) {
return false;
}
final Object this$errmsg = this.getErrmsg();
final Object other$errmsg = other.getErrmsg();
if (this$errmsg == null ? other$errmsg != null : !this$errmsg.equals(other$errmsg)) {
return false;
}
return true;
}
@Override
public int hashCode() {
final int PRIME = 59;
int result = 1;
final Object $errcode = this.getErrcode();
result = result * PRIME + ($errcode == null ? 43 : $errcode.hashCode());
final Object $errmsg = this.getErrmsg();
result = result * PRIME + ($errmsg == null ? 43 : $errmsg.hashCode());
return result;
}
@Override
public String toString() {
return "DingTalkSender.DingTalkSendMsgResponse(errcode=" + this.getErrcode() + ", errmsg=" + this.getErrmsg() + ")";
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy