From af1ab9ad6c0894c1ad2dd845010698360142e51e Mon Sep 17 00:00:00 2001 From: Hatter Jiang Date: Sun, 2 Jan 2022 10:35:47 +0800 Subject: [PATCH] feat dingtalk and restart nginx --- src/dingtalk.rs | 88 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 src/dingtalk.rs diff --git a/src/dingtalk.rs b/src/dingtalk.rs new file mode 100644 index 0000000..f80a2d4 --- /dev/null +++ b/src/dingtalk.rs @@ -0,0 +1,88 @@ +use std::io::{Error, ErrorKind}; +use serde::{Serialize, Deserialize}; +use std::time::SystemTime; +use rust_util::XResult; +use hmac::{Hmac, Mac}; +use sha2::Sha256; +use crate::CertConfig; + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct InnerTextMessageText { + pub content: String, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct InnerTextMessage { + pub msgtype: String, + pub text: InnerTextMessageText, +} + +pub fn send_dingtalk_message(cert_config: &CertConfig, message: &str) -> XResult<()> { + let dintalk_notify_token = get_dingtalk_notify_token(cert_config); + if let Some((access_token, sec_token)) = &dintalk_notify_token { + inner_send_dingtalk_message(access_token, sec_token, message)?; + } + Ok(()) +} + +fn inner_send_dingtalk_message(access_token: &str, sec_token: &str, message: &str) -> XResult<()> { + let dingtalk_message_json = serde_json::to_string(&InnerTextMessage { + msgtype: "text".into(), + text: InnerTextMessageText { + content: message.into(), + }, + })?; + let client = reqwest::blocking::Client::new(); + let mut webhook_url = "https://oapi.dingtalk.com/robot/send".to_string(); + webhook_url.push_str("?access_token="); + webhook_url.push_str(&urlencoding::encode(access_token)); + if !sec_token.is_empty() { + let timestamp = &format!("{}", SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_millis()); + let timestamp_and_secret = &format!("{}\n{}", timestamp, sec_token); + let hmac_sha256 = base64::encode(&calc_hmac_sha256(sec_token.as_bytes(), timestamp_and_secret.as_bytes())?[..]); + webhook_url.push_str("×tamp="); + webhook_url.push_str(timestamp); + webhook_url.push_str("&sign="); + webhook_url.push_str(&urlencoding::encode(&hmac_sha256)); + } + let response = client.post(webhook_url) + .header("Content-Type", "application/json; charset=utf-8") + .body(dingtalk_message_json.as_bytes().to_vec()) + .send()?; + + information!("Send dingtalk message: {:?}", response); + + Ok(()) +} + +// format: dingtalk:access_token?sec_token +fn get_dingtalk_notify_token(cert_config: &CertConfig) -> Option<(String, String)> { + let token = cert_config.notify_token.as_ref()?; + if token.starts_with("dingtalk:") { + let token_and_or_sec = &token["dingtalk:".len()..]; + let mut token_and_or_sec_vec = token_and_or_sec.split('?'); + let access_token = match token_and_or_sec_vec.next() { + Some(t) => t, + None => token_and_or_sec, + }; + let sec_token = match token_and_or_sec_vec.next() { + Some(t) => t, + None => "", + }; + Some((access_token.into(), sec_token.into())) + } else { + None + } +} + +/// calc hma_sha256 digest +fn calc_hmac_sha256(key: &[u8], message: &[u8]) -> XResult> { + let mut mac = match Hmac::::new_varkey(key) { + Ok(m) => m, + Err(e) => { + return Err(Box::new(Error::new(ErrorKind::Other, format!("Hmac error: {}", e)))); + } + }; + mac.input(message); + Ok(mac.result().code().to_vec()) +} \ No newline at end of file