| | |
| | | package com.xcong.excoin.utils.dingtalk; |
| | | |
| | | import com.alibaba.fastjson.JSONObject; |
| | | import com.dingtalk.api.DefaultDingTalkClient; |
| | | import com.dingtalk.api.DingTalkClient; |
| | | import com.dingtalk.api.request.OapiRobotSendRequest; |
| | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * 钉钉群机器人消息发送工具。 |
| | | * |
| | | * <h3>快速使用</h3> |
| | | * <pre> |
| | | * // 1. 创建发送器 |
| | | * DingTalkRobot robot = new DingTalkRobot(accessToken, secret); |
| | | * |
| | | * // 2a. 发送文本 |
| | | * robot.sendText("充值到账:1000 USDT"); |
| | | * |
| | | * // 2b. 发送 Markdown(title 会作为 @所有人 通知标题) |
| | | * robot.sendMarkdown("策略通知", "## 盈亏汇总\n- 已实现: **+2.5 USDT**"); |
| | | * |
| | | * // 2c. 发送 ActionCard |
| | | * robot.sendActionCard("止盈通知", "策略已达止盈目标", "查看详情", "https://xxx.com"); |
| | | * </pre> |
| | | * |
| | | * @author wzy |
| | | * @date 2020-04-17 22:18 |
| | | **/ |
| | | */ |
| | | @Slf4j |
| | | public class DingTalkUtils { |
| | | |
| | | private static final String SECRET = "SECe4afed333b31b66e1d16c87733a29a0b4a3051c71a2960d13e606bfc1dd88b14"; |
| | | private static final String ROBOT_URL = "https://oapi.dingtalk.com/robot/send?access_token="; |
| | | |
| | | public static void sendActionCard(int type) { |
| | | log.info("send dingtalk"); |
| | | String url = "https://oapi.dingtalk.com/robot/send?access_token=161d5e5b60ae5d6b4c80f2a9c35f9f212961a7c7154aa7e94b99503eca3886b0"; |
| | | Long timestamp = System.currentTimeMillis(); |
| | | private final String accessToken; |
| | | private final String secret; |
| | | |
| | | // ==================== 默认实例(向后兼容旧代码) ==================== |
| | | |
| | | private static volatile DingTalkUtils DEFAULT; |
| | | |
| | | public static DingTalkUtils getDefault() { |
| | | if (DEFAULT == null) { |
| | | synchronized (DingTalkUtils.class) { |
| | | if (DEFAULT == null) { |
| | | DEFAULT = new DingTalkUtils( |
| | | "57a3e695f78d7547fe20fb7aef82cf35a27de1846bbc6966e0194761976d7597", |
| | | "SECd59a93c8939eeaef0d97b5b714639df4af95d922002d0a440bc82ad42710a89e"); |
| | | // DEFAULT = new DingTalkUtils( |
| | | // "e357a3417991da86a5f79ea5bc8785b529c1da8b9d27458febed3b3d10c857c4", |
| | | // "SECf2b819e930cb4b367cf599f11a30eb8a5d0f4b0b1c069a57aa15328a3feebf8c"); |
| | | } |
| | | } |
| | | } |
| | | return DEFAULT; |
| | | } |
| | | |
| | | /** |
| | | * 向后兼容:按类型代码发送 ActionCard 通知。 |
| | | * |
| | | * @param typeIndex {@link DingTalkType} 的 index |
| | | */ |
| | | public static void sendActionCard(int typeIndex) { |
| | | DingTalkType dt = DingTalkType.byIndex(typeIndex); |
| | | if (dt == null) { |
| | | log.warn("[DingTalk] 未知通知类型: {}", typeIndex); |
| | | return; |
| | | } |
| | | getDefault().sendActionCard(dt.getName(), dt.getName(), "查看详情", "http://baidu.com"); |
| | | } |
| | | |
| | | // ==================== 构造 ==================== |
| | | |
| | | /** |
| | | * @param accessToken 钉钉机器人 Webhook 地址中的 access_token |
| | | * @param secret 钉钉机器人安全设置中的加签密钥 |
| | | */ |
| | | public DingTalkUtils(String accessToken, String secret) { |
| | | this.accessToken = accessToken; |
| | | this.secret = secret; |
| | | } |
| | | |
| | | // ==================== 文本消息 ==================== |
| | | |
| | | /** |
| | | * 发送纯文本消息。 |
| | | * |
| | | * @param content 文本内容,最大 4096 字节,支持 @手机号 提醒 |
| | | */ |
| | | public void sendText(String content) { |
| | | OapiRobotSendRequest req = new OapiRobotSendRequest(); |
| | | req.setMsgtype("text"); |
| | | OapiRobotSendRequest.Text text = new OapiRobotSendRequest.Text(); |
| | | text.setContent(content); |
| | | req.setText(text); |
| | | execute(req); |
| | | } |
| | | |
| | | // ==================== Markdown 消息 ==================== |
| | | |
| | | /** |
| | | * 发送 Markdown 消息。 |
| | | * |
| | | * @param title 消息标题,显示在会话列表和推送通知中 |
| | | * @param markdown Markdown 正文,支持标题/加粗/链接/列表等语法 |
| | | */ |
| | | public void sendMarkdown(String title, String markdown) { |
| | | OapiRobotSendRequest req = new OapiRobotSendRequest(); |
| | | req.setMsgtype("markdown"); |
| | | OapiRobotSendRequest.Markdown md = new OapiRobotSendRequest.Markdown(); |
| | | md.setTitle(title); |
| | | md.setText(markdown); |
| | | req.setMarkdown(md); |
| | | execute(req); |
| | | } |
| | | |
| | | // ==================== ActionCard 消息 ==================== |
| | | |
| | | /** |
| | | * 发送单按钮 ActionCard 消息。 |
| | | * |
| | | * @param title 卡片标题 |
| | | * @param text 卡片正文(支持 Markdown,减号开头的行会被转为列表) |
| | | * @param btnTitle 按钮文字 |
| | | * @param btnUrl 按钮跳转链接 |
| | | */ |
| | | public void sendActionCard(String title, String text, String btnTitle, String btnUrl) { |
| | | OapiRobotSendRequest req = new OapiRobotSendRequest(); |
| | | req.setMsgtype("actionCard"); |
| | | OapiRobotSendRequest.Actioncard card = new OapiRobotSendRequest.Actioncard(); |
| | | card.setTitle(title); |
| | | card.setText(text); |
| | | card.setBtnOrientation("1"); |
| | | List<OapiRobotSendRequest.Btns> btns = new ArrayList<>(); |
| | | OapiRobotSendRequest.Btns btn = new OapiRobotSendRequest.Btns(); |
| | | btn.setTitle(btnTitle); |
| | | btn.setActionURL(btnUrl); |
| | | btns.add(btn); |
| | | card.setBtns(btns); |
| | | req.setActionCard(card); |
| | | execute(req); |
| | | } |
| | | |
| | | // ==================== 内部执行 ==================== |
| | | |
| | | private void execute(OapiRobotSendRequest request) { |
| | | try { |
| | | Long timestamp = System.currentTimeMillis(); |
| | | String sign = generateSign(timestamp); |
| | | url = url + "×tamp=" + timestamp + "&sign=" + sign; |
| | | String url = ROBOT_URL + accessToken + "×tamp=" + timestamp + "&sign=" + sign; |
| | | DingTalkClient client = new DefaultDingTalkClient(url); |
| | | OapiRobotSendRequest request = new OapiRobotSendRequest(); |
| | | request.setMsgtype("actionCard"); |
| | | OapiRobotSendRequest.Actioncard actionCard = new OapiRobotSendRequest.Actioncard(); |
| | | actionCard.setTitle(DingTalkType.getName(type)); |
| | | actionCard.setBtnOrientation("1"); |
| | | actionCard.setText(DingTalkType.getName(type)); |
| | | List<OapiRobotSendRequest.Btns> btns = new ArrayList<>(); |
| | | OapiRobotSendRequest.Btns btn1 = new OapiRobotSendRequest.Btns(); |
| | | btn1.setTitle("查看详情"); |
| | | btn1.setActionURL("http://baidu.com"); |
| | | btns.add(btn1); |
| | | actionCard.setBtns(btns); |
| | | |
| | | request.setActionCard(actionCard); |
| | | OapiRobotSendResponse response = client.execute(request); |
| | | log.info(JSONObject.toJSONString(response)); |
| | | if (!response.isSuccess()) { |
| | | log.warn("[DingTalk] 发送失败, errcode:{}, errmsg:{}", response.getErrcode(), response.getErrmsg()); |
| | | } else { |
| | | log.info("[DingTalk] 发送成功"); |
| | | } |
| | | } catch (Exception e) { |
| | | log.error("#dingtalk send error#", e); |
| | | } finally { |
| | | log.error("#dingtalk finally#"); |
| | | log.error("[DingTalk] 发送异常", e); |
| | | } |
| | | } |
| | | |
| | | |
| | | private static String generateSign(Long timestamp) throws Exception { |
| | | String stringToToken = timestamp + "\n" + SECRET; |
| | | private String generateSign(Long timestamp) throws Exception { |
| | | String stringToSign = timestamp + "\n" + secret; |
| | | Mac mac = Mac.getInstance("HmacSHA256"); |
| | | mac.init(new SecretKeySpec(SECRET.getBytes("UTF-8"), "HmacSHA256")); |
| | | byte[] signData = mac.doFinal(stringToToken.getBytes("UTF-8")); |
| | | String sign = URLEncoder.encode(new String(Base64.encodeBase64(signData)), "UTF-8"); |
| | | return sign; |
| | | mac.init(new SecretKeySpec(secret.getBytes("UTF-8"), "HmacSHA256")); |
| | | byte[] signData = mac.doFinal(stringToSign.getBytes("UTF-8")); |
| | | return URLEncoder.encode(new String(Base64.encodeBase64(signData)), "UTF-8"); |
| | | } |
| | | |
| | | public static void main(String[] args) { |
| | | DingTalkUtils.getDefault().sendActionCard("风险提示", "测试123", "", ""); |
| | | } |
| | | } |