v1.2.0
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "dingtalk"
|
name = "dingtalk"
|
||||||
version = "1.1.2"
|
version = "1.2.0"
|
||||||
authors = ["Hatter Jiang <jht5945@gmail.com>"]
|
authors = ["Hatter Jiang <jht5945@gmail.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
description = "DingTalk Robot Util, Send text/markdown/link messages using DingTalk robot, 钉钉机器人"
|
description = "DingTalk Robot Util, Send text/markdown/link messages using DingTalk robot, 钉钉机器人"
|
||||||
@@ -13,10 +13,12 @@ license = "MIT"
|
|||||||
base64 = "0.11.0"
|
base64 = "0.11.0"
|
||||||
reqwest = "0.10.0"
|
reqwest = "0.10.0"
|
||||||
urlencoding = "1.0.0"
|
urlencoding = "1.0.0"
|
||||||
json = "0.12.0"
|
|
||||||
futures = "0.3.1"
|
futures = "0.3.1"
|
||||||
hmac = "0.7.1"
|
hmac = "0.7.1"
|
||||||
sha2 = "0.8.1"
|
sha2 = "0.8.1"
|
||||||
|
maplit = "1.0.2"
|
||||||
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
serde_json = "1.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tokio-test = "0.2.0"
|
tokio-test = "0.2.0"
|
||||||
|
|||||||
@@ -89,6 +89,8 @@ dt.send_message(&DingTalkMessage::new_action_card("action card 002", "action car
|
|||||||
|
|
||||||
#### Changelog
|
#### Changelog
|
||||||
|
|
||||||
|
* v1.2.0
|
||||||
|
* Use `serde` and `serde_json` crates, replace `json` crate
|
||||||
* v1.1.2
|
* v1.1.2
|
||||||
* Use `hmac` and `sha2` crates, replace `rust-crypto` crate
|
* Use `hmac` and `sha2` crates, replace `rust-crypto` crate
|
||||||
* v1.1.1
|
* v1.1.1
|
||||||
|
|||||||
302
src/lib.rs
302
src/lib.rs
@@ -1,5 +1,4 @@
|
|||||||
#[macro_use]
|
use serde_json::Value;
|
||||||
extern crate json;
|
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
fs,
|
fs,
|
||||||
@@ -17,6 +16,18 @@ use hmac::{
|
|||||||
Mac,
|
Mac,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
mod msg;
|
||||||
|
use msg::*;
|
||||||
|
|
||||||
|
pub use msg:: {
|
||||||
|
DingTalkMessage,
|
||||||
|
DingTalkMessageType,
|
||||||
|
DingTalkMessageActionCardHideAvatar,
|
||||||
|
DingTalkMessageActionCardBtnOrientation,
|
||||||
|
DingTalkMessageActionCardBtn,
|
||||||
|
DingTalkMessageFeedCardLink,
|
||||||
|
};
|
||||||
|
|
||||||
pub type XResult<T> = Result<T, Box<dyn std::error::Error>>;
|
pub type XResult<T> = Result<T, Box<dyn std::error::Error>>;
|
||||||
|
|
||||||
const CONTENT_TYPE: &str = "Content-Type";
|
const CONTENT_TYPE: &str = "Content-Type";
|
||||||
@@ -46,107 +57,6 @@ pub struct DingTalk<'a> {
|
|||||||
pub direct_url: &'a str,
|
pub direct_url: &'a str,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// DingTalk message type
|
|
||||||
/// * Text - text message
|
|
||||||
/// * Markdown - markdown message
|
|
||||||
/// * Link - link message
|
|
||||||
/// * ActionCard - action card message
|
|
||||||
/// * FeedCard - feed card message
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
|
||||||
pub enum DingTalkMessageType {
|
|
||||||
Text,
|
|
||||||
Markdown,
|
|
||||||
Link,
|
|
||||||
ActionCard,
|
|
||||||
FeedCard,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Default DingTalkMessageType is Text
|
|
||||||
impl Default for DingTalkMessageType {
|
|
||||||
fn default() -> Self { DingTalkMessageType::Text }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// DingTalk messge action card avatar
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
|
||||||
pub enum DingTalkMessageActionCardHideAvatar {
|
|
||||||
Hide,
|
|
||||||
Show,
|
|
||||||
}
|
|
||||||
|
|
||||||
// default value
|
|
||||||
impl Default for DingTalkMessageActionCardHideAvatar {
|
|
||||||
fn default() -> Self { DingTalkMessageActionCardHideAvatar::Show }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// into JsonValue
|
|
||||||
impl From<DingTalkMessageActionCardHideAvatar> for json::JsonValue {
|
|
||||||
fn from(a: DingTalkMessageActionCardHideAvatar) -> Self {
|
|
||||||
json::JsonValue::String(match a {
|
|
||||||
DingTalkMessageActionCardHideAvatar::Show => "0".into(),
|
|
||||||
DingTalkMessageActionCardHideAvatar::Hide => "1".into(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// DingTalk message action card orientation
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
|
||||||
pub enum DingTalkMessageActionCardBtnOrientation {
|
|
||||||
Vertical,
|
|
||||||
Landscape,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// default value
|
|
||||||
impl Default for DingTalkMessageActionCardBtnOrientation {
|
|
||||||
fn default() -> Self { DingTalkMessageActionCardBtnOrientation::Vertical }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// into JsonValue
|
|
||||||
impl From<DingTalkMessageActionCardBtnOrientation> for json::JsonValue {
|
|
||||||
fn from(o: DingTalkMessageActionCardBtnOrientation) -> Self {
|
|
||||||
json::JsonValue::String(match o {
|
|
||||||
DingTalkMessageActionCardBtnOrientation::Vertical => "0".into(),
|
|
||||||
DingTalkMessageActionCardBtnOrientation::Landscape => "1".into(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// DingTalk message action card btn
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct DingTalkMessageActionCardBtn {
|
|
||||||
pub title: String,
|
|
||||||
pub action_url: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// DingTalk message feed card link
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct DingTalkMessageFeedCardLink {
|
|
||||||
pub title: String,
|
|
||||||
pub message_url: String,
|
|
||||||
pub pic_url: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// DingTalk message
|
|
||||||
#[derive(Debug, Default)]
|
|
||||||
pub struct DingTalkMessage<'a> {
|
|
||||||
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 action_card_hide_avatar: DingTalkMessageActionCardHideAvatar,
|
|
||||||
pub action_card_btn_orientation: DingTalkMessageActionCardBtnOrientation,
|
|
||||||
pub action_card_single_btn: Option<DingTalkMessageActionCardBtn>,
|
|
||||||
pub action_card_btns: Vec<DingTalkMessageActionCardBtn>,
|
|
||||||
pub feed_card_links: Vec<DingTalkMessageFeedCardLink>,
|
|
||||||
pub at_all: bool,
|
|
||||||
pub at_mobiles: Vec<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a> DingTalkMessage<'a> {
|
impl <'a> DingTalkMessage<'a> {
|
||||||
|
|
||||||
/// New text DingTalk message
|
/// New text DingTalk message
|
||||||
@@ -300,7 +210,7 @@ impl <'a> DingTalk<'a> {
|
|||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn from_json(json: &str) -> XResult<Self> {
|
pub fn from_json(json: &str) -> XResult<Self> {
|
||||||
let json_value = json::parse(json)?;
|
let json_value: Value = serde_json::from_str(json)?;
|
||||||
if !json_value.is_object() {
|
if !json_value.is_object() {
|
||||||
return Err(Box::new(Error::new(ErrorKind::Other, format!("JSON format erorr: {}", json))));
|
return Err(Box::new(Error::new(ErrorKind::Other, format!("JSON format erorr: {}", json))));
|
||||||
}
|
}
|
||||||
@@ -348,83 +258,85 @@ impl <'a> DingTalk<'a> {
|
|||||||
/// 2. POST JSON message to DingTalk server
|
/// 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 {
|
let mut message_json = match dingtalk_message.message_type {
|
||||||
DingTalkMessageType::Text => object!{
|
DingTalkMessageType::Text => serde_json::to_value(InnerTextMessage {
|
||||||
"msgtype" => "text",
|
msgtype: DingTalkMessageType::Text,
|
||||||
"text" => object! {
|
text: InnerTextMessageText {
|
||||||
"content" => dingtalk_message.text_content,
|
content: dingtalk_message.text_content.into(),
|
||||||
}
|
}
|
||||||
},
|
}),
|
||||||
DingTalkMessageType::Link => object!{
|
DingTalkMessageType::Link => serde_json::to_value(InnerLinkMessage {
|
||||||
"msgtype" => "link",
|
msgtype: DingTalkMessageType::Link,
|
||||||
"link" => object!{
|
link: InnerLinkMessageLink {
|
||||||
"text" => dingtalk_message.link_text,
|
title: dingtalk_message.link_title.into(),
|
||||||
"title" => dingtalk_message.link_title,
|
text: dingtalk_message.link_text.into(),
|
||||||
"picUrl" => dingtalk_message.link_pic_url,
|
pic_url: dingtalk_message.link_pic_url.into(),
|
||||||
"messageUrl" => dingtalk_message.link_message_url,
|
message_url: dingtalk_message.link_message_url.into(),
|
||||||
}
|
}
|
||||||
},
|
}),
|
||||||
DingTalkMessageType::Markdown => object!{
|
DingTalkMessageType::Markdown => serde_json::to_value(InnerMarkdownMessage {
|
||||||
"msgtype" => "markdown",
|
msgtype: DingTalkMessageType::Markdown,
|
||||||
"markdown" => object! {
|
markdown: InnerMarkdownMessageMarkdown {
|
||||||
"title" => dingtalk_message.markdown_title,
|
title: dingtalk_message.markdown_title.into(),
|
||||||
"text" => dingtalk_message.markdown_content,
|
text: dingtalk_message.markdown_content.into(),
|
||||||
}
|
}
|
||||||
},
|
}),
|
||||||
DingTalkMessageType::ActionCard => object!{
|
DingTalkMessageType::ActionCard => serde_json::to_value(InnerActionCardMessage {
|
||||||
"msgtype" => "actionCard",
|
msgtype: DingTalkMessageType::ActionCard,
|
||||||
"actionCard" => object!{
|
action_card: InnerActionCardMessageActionCard {
|
||||||
"title" => dingtalk_message.action_card_title,
|
title: dingtalk_message.action_card_title.into(),
|
||||||
"text" => dingtalk_message.action_card_text,
|
text: dingtalk_message.action_card_text.into(),
|
||||||
"hideAvatar" => dingtalk_message.action_card_hide_avatar,
|
hide_avatar: dingtalk_message.action_card_hide_avatar,
|
||||||
"btnOrientation" => dingtalk_message.action_card_btn_orientation,
|
btn_orientation: dingtalk_message.action_card_btn_orientation,
|
||||||
},
|
}
|
||||||
},
|
}),
|
||||||
DingTalkMessageType::FeedCard => object!{
|
DingTalkMessageType::FeedCard => serde_json::to_value(InnerFeedCardMessage {
|
||||||
"msgtype" => "feedCard",
|
msgtype: DingTalkMessageType::FeedCard,
|
||||||
},
|
feed_card: InnerFeedCardMessageFeedCard {
|
||||||
};
|
links: {
|
||||||
|
let mut links: Vec<InnerFeedCardMessageFeedCardLink> = vec![];
|
||||||
|
for feed_card_link in &dingtalk_message.feed_card_links {
|
||||||
|
links.push(InnerFeedCardMessageFeedCardLink {
|
||||||
|
title: feed_card_link.title.clone(),
|
||||||
|
message_url: feed_card_link.message_url.clone(),
|
||||||
|
pic_url: feed_card_link.pic_url.clone(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
links
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}?;
|
||||||
if DingTalkMessageType::ActionCard == dingtalk_message.message_type {
|
if DingTalkMessageType::ActionCard == dingtalk_message.message_type {
|
||||||
if dingtalk_message.action_card_single_btn.is_some() {
|
if dingtalk_message.action_card_single_btn.is_some() {
|
||||||
let single_btn = dingtalk_message.action_card_single_btn.as_ref().unwrap();
|
if let Some(single_btn) = dingtalk_message.action_card_single_btn.as_ref() {
|
||||||
message_json["actionCard"]["singleTitle"] = single_btn.title.as_str().into();
|
message_json["actionCard"]["singleTitle"] = single_btn.title.as_str().into();
|
||||||
message_json["actionCard"]["singleURL"] = single_btn.action_url.as_str().into();
|
message_json["actionCard"]["singleURL"] = single_btn.action_url.as_str().into();
|
||||||
} else {
|
|
||||||
let mut btns: Vec<json::JsonValue> = vec![];
|
|
||||||
for action_card_btn in &dingtalk_message.action_card_btns {
|
|
||||||
let btn = object!{
|
|
||||||
"title" => action_card_btn.title.as_str(),
|
|
||||||
"actionURL" => action_card_btn.action_url.as_str(),
|
|
||||||
};
|
|
||||||
btns.push(btn);
|
|
||||||
}
|
|
||||||
message_json["actionCard"]["btns"] = json::JsonValue::Array(btns);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if DingTalkMessageType::FeedCard == dingtalk_message.message_type {
|
|
||||||
let mut links: Vec<json::JsonValue> = vec![];
|
|
||||||
for feed_card_link in &dingtalk_message.feed_card_links {
|
|
||||||
let link = object!{
|
|
||||||
"title" => feed_card_link.title.as_str(),
|
|
||||||
"messageURL" => feed_card_link.message_url.as_str(),
|
|
||||||
"picURL" => feed_card_link.pic_url.as_str(),
|
|
||||||
};
|
};
|
||||||
links.push(link);
|
} else {
|
||||||
|
let mut btns: Vec<InnerActionCardMessageBtn> = vec![];
|
||||||
|
for action_card_btn in &dingtalk_message.action_card_btns {
|
||||||
|
btns.push(InnerActionCardMessageBtn {
|
||||||
|
title: action_card_btn.title.clone(),
|
||||||
|
action_url: action_card_btn.action_url.clone(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
message_json["actionCard"]["btns"] = serde_json::to_value(btns)?;
|
||||||
}
|
}
|
||||||
message_json["feedCard"] = object!{
|
|
||||||
"links" => json::JsonValue::Array(links),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
if dingtalk_message.at_all || !dingtalk_message.at_mobiles.is_empty() {
|
if dingtalk_message.at_all || !dingtalk_message.at_mobiles.is_empty() {
|
||||||
let mut at_mobiles = json::JsonValue::new_object();
|
if let Some(m) = message_json.as_object_mut() {
|
||||||
for m in &dingtalk_message.at_mobiles {
|
let mut at_mobiles: Vec<Value> = vec![];
|
||||||
at_mobiles.push(m.clone()).ok();
|
for m in &dingtalk_message.at_mobiles {
|
||||||
|
at_mobiles.push(Value::String(m.clone()));
|
||||||
|
}
|
||||||
|
let mut at_map = serde_json::Map::new();
|
||||||
|
at_map.insert("atMobiles".into(), Value::Array(at_mobiles));
|
||||||
|
at_map.insert("isAtAll".into(), Value::Bool(dingtalk_message.at_all));
|
||||||
|
|
||||||
|
m.insert("at".into(), Value::Object(at_map));
|
||||||
}
|
}
|
||||||
message_json["at"] = object!{
|
|
||||||
"atMobiles" => at_mobiles,
|
|
||||||
"isAtAll" => dingtalk_message.at_all,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
self.send(&json::stringify(message_json)).await
|
self.send(&serde_json::to_string(&message_json)?).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Send text message
|
/// Send text message
|
||||||
@@ -509,53 +421,3 @@ fn calc_hmac_sha256(key: &[u8], message: &[u8]) -> XResult<Vec<u8>> {
|
|||||||
mac.input(message);
|
mac.input(message);
|
||||||
Ok(mac.result().code().to_vec())
|
Ok(mac.result().code().to_vec())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn run_all_tests() {
|
|
||||||
tokio_test::block_on(_test_send()).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn _test_send() -> XResult<()> {
|
|
||||||
let dt = DingTalk::from_file("~/.dingtalk-token.json")?;
|
|
||||||
dt.send_text("test message 001 ---------------------").await?;
|
|
||||||
|
|
||||||
dt.send_markdown("markdown title 001", r#"# markdown content 001
|
|
||||||
* line 0
|
|
||||||
* line 1
|
|
||||||
* line 2"#).await?;
|
|
||||||
|
|
||||||
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()
|
|
||||||
.add_feed_card_link(DingTalkMessageFeedCardLink{
|
|
||||||
title: "test feed card title 001".into(),
|
|
||||||
message_url: "https://hatter.ink/".into(),
|
|
||||||
pic_url: "https://hatter.ink/favicon.png".into(),
|
|
||||||
})
|
|
||||||
.add_feed_card_link(DingTalkMessageFeedCardLink{
|
|
||||||
title: "test feed card title 002".into(),
|
|
||||||
message_url: "https://hatter.ink/".into(),
|
|
||||||
pic_url: "https://hatter.ink/favicon.png".into(),
|
|
||||||
})
|
|
||||||
).await?;
|
|
||||||
|
|
||||||
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")
|
|
||||||
.add_action_card_btn(DingTalkMessageActionCardBtn{
|
|
||||||
title: "test signle btn title 01".into(),
|
|
||||||
action_url: "https://hatter.ink/".into(),
|
|
||||||
})
|
|
||||||
.add_action_card_btn(DingTalkMessageActionCardBtn{
|
|
||||||
title: "test signle btn title 02".into(),
|
|
||||||
action_url: "https://hatter.ink/".into(),
|
|
||||||
})
|
|
||||||
).await?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|||||||
177
src/msg.rs
Normal file
177
src/msg.rs
Normal file
@@ -0,0 +1,177 @@
|
|||||||
|
use serde::{Serialize, Deserialize};
|
||||||
|
|
||||||
|
|
||||||
|
/// DingTalk message type
|
||||||
|
/// * Text - text message
|
||||||
|
/// * Markdown - markdown message
|
||||||
|
/// * Link - link message
|
||||||
|
/// * ActionCard - action card message
|
||||||
|
/// * FeedCard - feed card message
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub enum DingTalkMessageType {
|
||||||
|
#[serde(rename = "text")]
|
||||||
|
Text,
|
||||||
|
#[serde(rename = "markdown")]
|
||||||
|
Markdown,
|
||||||
|
#[serde(rename = "link")]
|
||||||
|
Link,
|
||||||
|
#[serde(rename = "actionCard")]
|
||||||
|
ActionCard,
|
||||||
|
#[serde(rename = "feedCard")]
|
||||||
|
FeedCard,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Default DingTalkMessageType is Text
|
||||||
|
impl Default for DingTalkMessageType {
|
||||||
|
fn default() -> Self { DingTalkMessageType::Text }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// DingTalk messge action card avatar
|
||||||
|
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
|
||||||
|
pub enum DingTalkMessageActionCardHideAvatar {
|
||||||
|
#[serde(rename = "1")]
|
||||||
|
Hide,
|
||||||
|
#[serde(rename = "0")]
|
||||||
|
Show,
|
||||||
|
}
|
||||||
|
|
||||||
|
// default value
|
||||||
|
impl Default for DingTalkMessageActionCardHideAvatar {
|
||||||
|
fn default() -> Self { DingTalkMessageActionCardHideAvatar::Show }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// DingTalk message action card orientation
|
||||||
|
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
|
||||||
|
pub enum DingTalkMessageActionCardBtnOrientation {
|
||||||
|
#[serde(rename = "0")]
|
||||||
|
Vertical,
|
||||||
|
#[serde(rename = "1")]
|
||||||
|
Landscape,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// default value
|
||||||
|
impl Default for DingTalkMessageActionCardBtnOrientation {
|
||||||
|
fn default() -> Self { DingTalkMessageActionCardBtnOrientation::Vertical }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// DingTalk message action card btn
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct DingTalkMessageActionCardBtn {
|
||||||
|
pub title: String,
|
||||||
|
pub action_url: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// DingTalk message feed card link
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct DingTalkMessageFeedCardLink {
|
||||||
|
pub title: String,
|
||||||
|
pub message_url: String,
|
||||||
|
pub pic_url: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// DingTalk message
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct DingTalkMessage<'a> {
|
||||||
|
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 action_card_hide_avatar: DingTalkMessageActionCardHideAvatar,
|
||||||
|
pub action_card_btn_orientation: DingTalkMessageActionCardBtnOrientation,
|
||||||
|
pub action_card_single_btn: Option<DingTalkMessageActionCardBtn>,
|
||||||
|
pub action_card_btns: Vec<DingTalkMessageActionCardBtn>,
|
||||||
|
pub feed_card_links: Vec<DingTalkMessageFeedCardLink>,
|
||||||
|
pub at_all: bool,
|
||||||
|
pub at_mobiles: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct InnerTextMessageText {
|
||||||
|
pub content: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct InnerTextMessage {
|
||||||
|
pub msgtype: DingTalkMessageType,
|
||||||
|
pub text: InnerTextMessageText,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct InnerLinkMessageLink {
|
||||||
|
pub title: String,
|
||||||
|
pub text: String,
|
||||||
|
pub pic_url: String,
|
||||||
|
pub message_url: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct InnerLinkMessage {
|
||||||
|
pub msgtype: DingTalkMessageType,
|
||||||
|
pub link: InnerLinkMessageLink,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct InnerMarkdownMessageMarkdown {
|
||||||
|
pub title: String,
|
||||||
|
pub text: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct InnerMarkdownMessage {
|
||||||
|
pub msgtype: DingTalkMessageType,
|
||||||
|
pub markdown: InnerMarkdownMessageMarkdown,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct InnerActionCardMessageActionCard {
|
||||||
|
pub title: String,
|
||||||
|
pub text: String,
|
||||||
|
pub hide_avatar: DingTalkMessageActionCardHideAvatar,
|
||||||
|
pub btn_orientation: DingTalkMessageActionCardBtnOrientation,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct InnerActionCardMessageBtn {
|
||||||
|
pub title: String,
|
||||||
|
#[serde(rename = "actionURL")]
|
||||||
|
pub action_url: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct InnerActionCardMessage {
|
||||||
|
pub msgtype: DingTalkMessageType,
|
||||||
|
pub action_card: InnerActionCardMessageActionCard,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct InnerFeedCardMessageFeedCardLink {
|
||||||
|
pub title: String,
|
||||||
|
#[serde(rename = "messageURL")]
|
||||||
|
pub message_url: String,
|
||||||
|
#[serde(rename = "picURL")]
|
||||||
|
pub pic_url: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct InnerFeedCardMessageFeedCard {
|
||||||
|
pub links: Vec<InnerFeedCardMessageFeedCardLink>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct InnerFeedCardMessage {
|
||||||
|
pub msgtype: DingTalkMessageType,
|
||||||
|
pub feed_card: InnerFeedCardMessageFeedCard,
|
||||||
|
}
|
||||||
52
tests/test.rs
Normal file
52
tests/test.rs
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
use dingtalk::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn run_all_tests() {
|
||||||
|
tokio_test::block_on(_test_send()).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn _test_send() -> XResult<()> {
|
||||||
|
let dt = DingTalk::from_file("~/.dingtalk-token.json")?;
|
||||||
|
dt.send_text("test message 001 ---------------------").await?;
|
||||||
|
|
||||||
|
dt.send_markdown("markdown title 001", r#"# markdown content 001
|
||||||
|
* line 0
|
||||||
|
* line 1
|
||||||
|
* line 2"#).await?;
|
||||||
|
|
||||||
|
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()
|
||||||
|
.add_feed_card_link(DingTalkMessageFeedCardLink{
|
||||||
|
title: "test feed card title 001".into(),
|
||||||
|
message_url: "https://hatter.ink/".into(),
|
||||||
|
pic_url: "https://hatter.ink/favicon.png".into(),
|
||||||
|
})
|
||||||
|
.add_feed_card_link(DingTalkMessageFeedCardLink{
|
||||||
|
title: "test feed card title 002".into(),
|
||||||
|
message_url: "https://hatter.ink/".into(),
|
||||||
|
pic_url: "https://hatter.ink/favicon.png".into(),
|
||||||
|
})
|
||||||
|
).await?;
|
||||||
|
|
||||||
|
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")
|
||||||
|
.add_action_card_btn(DingTalkMessageActionCardBtn{
|
||||||
|
title: "test signle btn title 01".into(),
|
||||||
|
action_url: "https://hatter.ink/".into(),
|
||||||
|
})
|
||||||
|
.add_action_card_btn(DingTalkMessageActionCardBtn{
|
||||||
|
title: "test signle btn title 02".into(),
|
||||||
|
action_url: "https://hatter.ink/".into(),
|
||||||
|
})
|
||||||
|
).await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user