From daae47405c18f9dc6d5f66a76db3e321ba04af91 Mon Sep 17 00:00:00 2001 From: Hatter Jiang Date: Sat, 16 May 2020 01:00:34 +0800 Subject: [PATCH] v2.0.0 --- Cargo.toml | 2 +- README.md | 2 + src/lib.rs | 137 ++++++++++++++++++++++++-------------------------- src/msg.rs | 20 ++++---- tests/test.rs | 6 +-- 5 files changed, 83 insertions(+), 84 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 754c16b..77efcb7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "dingtalk" -version = "1.3.2" +version = "2.0.0" authors = ["Hatter Jiang "] edition = "2018" description = "DingTalk Robot Util, Send text/markdown/link messages using DingTalk robot, 钉钉机器人" diff --git a/README.md b/README.md index 0cf2199..925adf5 100644 --- a/README.md +++ b/README.md @@ -109,6 +109,8 @@ WeChat Work config: #### Changelog +* v2.0.0 + * Remove `'a` life cycle * v1.3.2 * Add `DingTalk::from_token` * v1.3.1 diff --git a/src/lib.rs b/src/lib.rs index 3eb8959..508c2d9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -37,46 +37,46 @@ const DEFAULT_WECHAT_WORK_ROBOT_URL: &str = "https://qyapi.weixin.qq.com/cgi-bin /// Document https://ding-doc.dingtalk.com/doc#/serverapi2/qf2nxq /// /// Sample code: -/// ``` +/// ```ignore /// let dt = DingTalk::new("", ""); /// dt.send_text("Hello world!")?; /// ``` /// /// At all sample: -/// ``` +/// ```ignore /// dt.send_message(&DingTalkMessage::new_text("Hello World!").at_all())?; /// ``` #[derive(Default)] -pub struct DingTalk<'a> { +pub struct DingTalk { pub dingtalk_type: DingTalkType, - pub default_webhook_url: &'a str, - pub access_token: &'a str, - pub sec_token: &'a str, - pub direct_url: &'a str, + pub default_webhook_url: String, + pub access_token: String, + pub sec_token: String, + pub direct_url: String, } -impl <'a> DingTalkMessage<'a> { +impl DingTalkMessage { /// New text DingTalk message - pub fn new_text(text_content: &'a str) -> Self { + pub fn new_text(text_content: &str) -> Self { Self::new(DingTalkMessageType::Text).text(text_content) } /// New markdown DingTalk message - pub fn new_markdown(markdown_title: &'a str, markdown_content: &'a str) -> Self { + pub fn new_markdown(markdown_title: &str, markdown_content: &str) -> Self { Self::new(DingTalkMessageType::Markdown).markdown(markdown_title, markdown_content) } /// New link DingTalk message - pub fn new_link(link_title: &'a str, link_text: &'a str, link_pic_url: &'a str, link_message_url: &'a str) -> Self { + pub fn new_link(link_title: &str, link_text: &str, link_pic_url: &str, link_message_url: &str) -> Self { Self::new(DingTalkMessageType::Link).link(link_title, link_text, link_pic_url, link_message_url) } /// New action card DingTalk message - pub fn new_action_card(title: &'a str, text: &'a str) -> Self { + pub fn new_action_card(title: &str, text: &str) -> Self { let mut s = Self::new(DingTalkMessageType::ActionCard); - s.action_card_title = title; - s.action_card_text = text; + s.action_card_title = title.into(); + s.action_card_text = text.into(); s } @@ -94,24 +94,24 @@ impl <'a> DingTalkMessage<'a> { } /// Set text - pub fn text(mut self, text_content: &'a str) -> Self { - self.text_content = text_content; + pub fn text(mut self, text_content: &str) -> Self { + self.text_content = text_content.into(); self } /// Set markdown - pub fn markdown(mut self, markdown_title: &'a str, markdown_content: &'a str) -> Self { - self.markdown_title = markdown_title; - self.markdown_content = markdown_content; + pub fn markdown(mut self, markdown_title: &str, markdown_content: &str) -> Self { + self.markdown_title = markdown_title.into(); + self.markdown_content = markdown_content.into(); self } /// Set link - pub fn link(mut self, link_title: &'a str, link_text: &'a str, link_pic_url: &'a str, link_message_url: &'a str) -> Self { - self.link_title = link_title; - self.link_text = link_text; - self.link_pic_url = link_pic_url; - self.link_message_url = link_message_url; + pub fn link(mut self, link_title: &str, link_text: &str, link_pic_url: &str, link_message_url: &str) -> Self { + self.link_title = link_title.into(); + self.link_text = link_text.into(); + self.link_pic_url = link_pic_url.into(); + self.link_message_url = link_message_url.into(); self } @@ -158,7 +158,7 @@ impl <'a> DingTalkMessage<'a> { } /// Add feed card link detail - pub fn add_feed_card_link_detail(self, title: &'a str, message_url: &'a str, pic_url: &'a str) -> Self { + pub fn add_feed_card_link_detail(self, title: &str, message_url: &str, pic_url: &str) -> Self { self.add_feed_card_link(DingTalkMessageFeedCardLink { title: title.into(), message_url: message_url.into(), @@ -181,7 +181,7 @@ impl <'a> DingTalkMessage<'a> { } } -impl <'a> DingTalk<'a> { +impl DingTalk { /// Create `DingTalk` from token: /// wechatwork:access_token @@ -196,9 +196,9 @@ impl <'a> DingTalk<'a> { let sec_token = match token_and_or_sec_vec.next() { Some(t) => t, None => "", }; - Ok(Self::new(Self::string_to_a_str(access_token), Self::string_to_a_str(sec_token))) + Ok(Self::new(access_token, sec_token)) } else if token.starts_with("wechatwork:") { - Ok(Self::new_wechat(Self::string_to_a_str(&token["wechatwork:".len()..]))) + Ok(Self::new_wechat(&token["wechatwork:".len()..])) } else { Err(Box::new(Error::new(ErrorKind::Other, format!("Tokne format erorr: {}", token)))) } @@ -239,15 +239,15 @@ impl <'a> DingTalk<'a> { _ => DingTalkType::DingTalk, }; - let default_webhook_url = Self::string_to_a_str(json_value["default_webhook_url"].as_str().unwrap_or_else( + let default_webhook_url = json_value["default_webhook_url"].as_str().unwrap_or_else( || match dingtalk_type { DingTalkType::DingTalk => DEFAULT_DINGTALK_ROBOT_URL, DingTalkType::WeChatWork => DEFAULT_WECHAT_WORK_ROBOT_URL, } - )); - let access_token = Self::string_to_a_str(json_value["access_token"].as_str().unwrap_or_default()); - let sec_token = Self::string_to_a_str(json_value["sec_token"].as_str().unwrap_or_default()); - let direct_url = Self::string_to_a_str(json_value["direct_url"].as_str().unwrap_or_default()); + ).to_owned(); + let access_token = json_value["access_token"].as_str().unwrap_or_default().to_owned(); + let sec_token = json_value["sec_token"].as_str().unwrap_or_default().to_owned(); + let direct_url = json_value["direct_url"].as_str().unwrap_or_default().to_owned(); Ok(DingTalk { dingtalk_type, @@ -259,72 +259,72 @@ impl <'a> DingTalk<'a> { } /// Create `DingTalk` from url, for outgoing robot - pub fn from_url(direct_url: &'a str) -> Self { + pub fn from_url(direct_url: &str) -> Self { DingTalk { - direct_url, + direct_url: direct_url.into(), ..Default::default() } } /// Create `DingTalk` /// `access_token` is access token, `sec_token` can be empty `""` - pub fn new(access_token: &'a str, sec_token: &'a str) -> Self { + pub fn new(access_token: &str, sec_token: &str) -> Self { DingTalk { - default_webhook_url: DEFAULT_DINGTALK_ROBOT_URL, - access_token, - sec_token, + default_webhook_url: DEFAULT_DINGTALK_ROBOT_URL.into(), + access_token: access_token.into(), + sec_token: sec_token.into(), ..Default::default() } } /// Create `DingTalk` for WeChat Work - pub fn new_wechat(key: &'a str) -> Self { + pub fn new_wechat(key: &str) -> Self { DingTalk { - default_webhook_url: DEFAULT_WECHAT_WORK_ROBOT_URL, + default_webhook_url: DEFAULT_WECHAT_WORK_ROBOT_URL.into(), dingtalk_type: DingTalkType::WeChatWork, - access_token: key, + access_token: key.into(), ..Default::default() } } /// Set default webhook url - pub fn set_default_webhook_url(&mut self, default_webhook_url: &'a str) { - self.default_webhook_url = default_webhook_url; + pub fn set_default_webhook_url(&mut self, default_webhook_url: &str) { + self.default_webhook_url = default_webhook_url.into(); } /// Send DingTalk message /// /// 1. Create DingTalk JSON message /// 2. POST JSON message to DingTalk server - pub async fn send_message(&self, dingtalk_message: &DingTalkMessage<'_>) -> XResult<()> { + pub async fn send_message(&self, dingtalk_message: DingTalkMessage) -> XResult<()> { let mut message_json = match dingtalk_message.message_type { DingTalkMessageType::Text => serde_json::to_value(InnerTextMessage { msgtype: DingTalkMessageType::Text, text: InnerTextMessageText { - content: dingtalk_message.text_content.into(), + content: dingtalk_message.text_content, } }), DingTalkMessageType::Link => serde_json::to_value(InnerLinkMessage { msgtype: DingTalkMessageType::Link, link: InnerLinkMessageLink { - title: dingtalk_message.link_title.into(), - text: dingtalk_message.link_text.into(), - pic_url: dingtalk_message.link_pic_url.into(), - message_url: dingtalk_message.link_message_url.into(), + title: dingtalk_message.link_title, + text: dingtalk_message.link_text, + pic_url: dingtalk_message.link_pic_url, + message_url: dingtalk_message.link_message_url, } }), DingTalkMessageType::Markdown => serde_json::to_value(InnerMarkdownMessage { msgtype: DingTalkMessageType::Markdown, markdown: InnerMarkdownMessageMarkdown { - title: dingtalk_message.markdown_title.into(), - text: dingtalk_message.markdown_content.into(), + title: dingtalk_message.markdown_title, + text: dingtalk_message.markdown_content, } }), DingTalkMessageType::ActionCard => serde_json::to_value(InnerActionCardMessage { msgtype: DingTalkMessageType::ActionCard, action_card: InnerActionCardMessageActionCard { - title: dingtalk_message.action_card_title.into(), - text: dingtalk_message.action_card_text.into(), + title: dingtalk_message.action_card_title, + text: dingtalk_message.action_card_text, hide_avatar: dingtalk_message.action_card_hide_avatar, btn_orientation: dingtalk_message.action_card_btn_orientation, } @@ -381,17 +381,17 @@ impl <'a> DingTalk<'a> { /// Send text message pub async fn send_text(&self, text_message: &str) -> XResult<()> { - self.send_message(&DingTalkMessage::new_text(text_message)).await + self.send_message(DingTalkMessage::new_text(text_message)).await } /// Send markdown message pub async fn send_markdown(&self, title: &str, text: &str) -> XResult<()> { - self.send_message(&DingTalkMessage::new_markdown(title, text)).await + self.send_message(DingTalkMessage::new_markdown(title, text)).await } /// Send link message - pub async fn send_link(&self, link_title: &'a str, link_text: &'a str, link_pic_url: &'a str, link_message_url: &'a str) -> XResult<()> { - self.send_message(&DingTalkMessage::new_link(link_title, link_text, link_pic_url, link_message_url)).await + pub async fn send_link(&self, link_title: &str, link_text: &str, link_pic_url: &str, link_message_url: &str) -> XResult<()> { + self.send_message(DingTalkMessage::new_link(link_title, link_text, link_pic_url, link_message_url)).await } /// Direct send JSON message @@ -401,8 +401,9 @@ impl <'a> DingTalk<'a> { .header(CONTENT_TYPE, APPLICATION_JSON_UTF8) .body(json_message.as_bytes().to_vec()) .send().await { - Ok(r) => r, - Err(e) => return Err(Box::new(Error::new(ErrorKind::Other, format!("Unknown error: {}", e))) as Box), + Ok(r) => r, Err(e) => { + return Err(Box::new(Error::new(ErrorKind::Other, format!("Unknown error: {}", e))) as Box); + }, }; match response.status().as_u16() { @@ -414,10 +415,10 @@ impl <'a> DingTalk<'a> { /// Generate signed dingtalk webhook URL pub fn generate_signed_url(&self) -> XResult { if !self.direct_url.is_empty() { - return Ok(self.direct_url.into()); + return Ok(self.direct_url.clone()); } let mut signed_url = String::with_capacity(1024); - signed_url.push_str(self.default_webhook_url); + signed_url.push_str(&self.default_webhook_url); if self.default_webhook_url.ends_with('?') { // Just Ok @@ -433,7 +434,7 @@ impl <'a> DingTalk<'a> { DingTalkType::DingTalk => signed_url.push_str("access_token="), DingTalkType::WeChatWork => signed_url.push_str("key="), } - signed_url.push_str(&urlencoding::encode(self.access_token)); + signed_url.push_str(&urlencoding::encode(&self.access_token)); if !self.sec_token.is_empty() { let timestamp = &format!("{}", SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_millis()); @@ -448,18 +449,14 @@ impl <'a> DingTalk<'a> { Ok(signed_url) } - - // SAFE? may these codes cause memory leak? - fn string_to_a_str(s: &str) -> &'a str { - Box::leak(s.to_owned().into_boxed_str()) - } } /// 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)))) + 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()) diff --git a/src/msg.rs b/src/msg.rs index 254199c..b4f89e5 100644 --- a/src/msg.rs +++ b/src/msg.rs @@ -85,17 +85,17 @@ pub struct DingTalkMessageFeedCardLink { /// DingTalk message #[derive(Debug, Default)] -pub struct DingTalkMessage<'a> { +pub struct DingTalkMessage { pub message_type: DingTalkMessageType, - pub text_content: &'a str, - pub markdown_title: &'a str, - pub markdown_content: &'a str, - pub link_text: &'a str, - pub link_title: &'a str, - pub link_pic_url: &'a str, - pub link_message_url: &'a str, - pub action_card_title: &'a str, - pub action_card_text: &'a str, + pub text_content: String, + pub markdown_title: String, + pub markdown_content: String, + pub link_text: String, + pub link_title: String, + pub link_pic_url: String, + pub link_message_url: String, + pub action_card_title: String, + pub action_card_text: String, pub action_card_hide_avatar: DingTalkMessageActionCardHideAvatar, pub action_card_btn_orientation: DingTalkMessageActionCardBtnOrientation, pub action_card_single_btn: Option, diff --git a/tests/test.rs b/tests/test.rs index 7673d99..4b674d7 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -16,7 +16,7 @@ async fn _test_send() -> XResult<()> { dt.send_link("link title 001", "link content 001", "https://hatter.ink/favicon.png", "https://hatter.ink/").await?; - dt.send_message(&DingTalkMessage::new_feed_card() + dt.send_message(DingTalkMessage::new_feed_card() .add_feed_card_link(DingTalkMessageFeedCardLink{ title: "test feed card title 001".into(), message_url: "https://hatter.ink/".into(), @@ -29,14 +29,14 @@ async fn _test_send() -> XResult<()> { }) ).await?; - dt.send_message(&DingTalkMessage::new_action_card("action card 001", "action card text 001") + dt.send_message(DingTalkMessage::new_action_card("action card 001", "action card text 001") .set_action_card_signle_btn(DingTalkMessageActionCardBtn{ title: "test signle btn title".into(), action_url: "https://hatter.ink/".into(), }) ).await?; - dt.send_message(&DingTalkMessage::new_action_card("action card 002", "action card text 002") + dt.send_message(DingTalkMessage::new_action_card("action card 002", "action card text 002") .add_action_card_btn(DingTalkMessageActionCardBtn{ title: "test signle btn title 01".into(), action_url: "https://hatter.ink/".into(),