rust_aliyun/
dingtalk.rs

1//! Add dependencies in the following partial Cargo.toml manifest
2//!
3//! ```cargo
4//! [dependencies]
5//! anyhow = "1.0.86"
6//! rand = "0.7.2"
7//! reqwest = "0.12.15"
8//! serde = "1.0.219"
9//! ```
10//!
11//! Note that serde is used by default with the `derive` feature.
12//! You can still reimport it if you need additional features.
13
14use reqwest::Client;
15#[derive(serde::Serialize, Debug, serde::Deserialize)]
16pub struct TextContent {
17    pub content: String,
18}
19#[derive(serde::Serialize, Debug, serde::Deserialize)]
20pub struct At {
21    #[serde(rename = "atUserIds", skip_serializing_if = "Option::is_none")]
22    pub at_ids: Option<Vec<String>>,
23    #[serde(rename = "atMobiles", skip_serializing_if = "Option::is_none")]
24    pub at_mobiles: Option<Vec<String>>,
25    #[serde(rename = "isAtAll", skip_serializing_if = "Option::is_none")]
26    pub is_at_all: Option<bool>,
27}
28#[derive(serde::Serialize, Debug, serde::Deserialize)]
29pub struct LinkContent {
30    pub title: String,
31    pub text: String,
32    #[serde(rename = "messageUrl")]
33    pub message_url: String,
34    #[serde(rename = "picUrl", skip_serializing_if = "Option::is_none")]
35    pub pic_url: Option<String>,
36}
37#[derive(serde::Serialize, Debug, serde::Deserialize)]
38pub struct MarkdownContent {
39    pub title: String,
40    pub text: String,
41}
42#[derive(serde::Serialize, Debug, serde::Deserialize)]
43pub struct ActionCardContent {
44    pub title: String,
45    pub text: String, // markdown格式的消息
46    #[serde(rename = "singleTitle")]
47    pub single_title: String,
48    #[serde(rename = "singleURL")]
49    pub single_url: String,
50    #[serde(rename = "btnOrientation")]
51    pub btn_orientation: Option<ButtonOrientation>, // 0-按钮竖直排列,1-按钮横向排列
52    #[serde(skip_serializing_if = "Option::is_none")]
53    pub btns: Option<Vec<ActionCardButton>>, // 独立跳转ActionCard按钮
54}
55#[derive(serde_repr::Serialize_repr, Debug, serde_repr::Deserialize_repr)]
56#[repr(u8)]
57pub enum ButtonOrientation {
58    Vertical = 0,
59    Horizontal = 1,
60}
61#[derive(serde::Serialize, Debug, serde::Deserialize)]
62pub struct ActionCardButton {
63    pub title: String,
64    #[serde(rename = "actionURL")]
65    pub action_url: String,
66}
67#[derive(serde::Serialize, Debug, serde::Deserialize)]
68pub struct FeedCardLink {
69    pub title: String,
70    #[serde(rename = "messageURL")]
71    pub message_url: String,
72    #[serde(rename = "picURL")]
73    pub pic_url: String,
74}
75
76#[derive(serde::Serialize, Debug, serde::Deserialize)]
77pub struct FeedCardContent {
78    pub links: Vec<FeedCardLink>,
79}
80#[derive(serde::Serialize, Debug, serde::Deserialize)]
81#[serde(tag = "msgtype", rename_all = "camelCase")]
82pub enum RobotMessage {
83    #[serde(rename = "text")]
84    Text {
85        text: TextContent,
86        #[serde(skip_serializing_if = "Option::is_none")]
87        at: Option<At>,
88    },
89    #[serde(rename = "link")]
90    Link {
91        link: LinkContent,
92    },
93    #[serde(rename = "markdown")]
94    Markdown {
95        markdown: MarkdownContent,
96        #[serde(skip_serializing_if = "Option::is_none")]
97        at: Option<At>,
98    },
99    #[serde(rename = "actionCard")]
100    ActionCard {
101        content: ActionCardContent,
102    },
103    #[serde(rename = "feedCard")]
104    FeedCard {
105        feed_card: FeedCardContent,
106    },
107}
108
109// --- 统一的发送函数 --- 
110pub async fn send_message(
111    access_token: &str,
112    message: &RobotMessage,
113) -> Result<(), Box<dyn std::error::Error>> {
114
115    let webhook_url = format!(
116        "https://oapi.dingtalk.com/robot/send?access_token={}",
117        access_token,
118    );
119
120    let client = Client::new();
121    let response = client.post(&webhook_url).json(message).send().await?;
122
123    if response.status().is_success() {
124        let response_body: serde_json::Value = response.json().await?;
125        println!("发送成功: {:?}", response_body);
126        Ok(())
127    } else {
128        let error_text = response.text().await?;
129        eprintln!("发送失败: {}", error_text);
130        Err(Box::from(format!("发送失败: {}", error_text)))
131    }
132}