dingtalk_rs/client/message_notify/
message.rs

1use serde::{Deserialize, Serialize};
2
3// 参考: https://open.dingtalk.com/document/orgapp-server/message-types-and-data-format
4
5#[derive(Debug, Deserialize, Serialize, Default)]
6pub struct Message {
7    /// 消息类型。
8    /// 文本消息类型为:text
9    pub msgtype: String,
10    #[serde(flatten)]
11    pub content: MessageBody,
12}
13
14impl From<MessageBody> for Message {
15    fn from(body: MessageBody) -> Self {
16        let msgtype = match &body {
17            MessageBody::Text { content: _ } => "text",
18            MessageBody::File { media_id: _ } => "file",
19            MessageBody::Image { media_id: _ } => "image",
20            MessageBody::Voice {
21                media_id: _,
22                duration: _,
23            } => "voice",
24            MessageBody::Link {
25                message_url: _,
26                pic_url: _,
27                title: _,
28                text: _,
29            } => "link",
30            MessageBody::Oa {
31                message_url: _,
32                pc_message_url: _,
33                head: _,
34                status_bar: _,
35                body: _,
36            } => "oa",
37            MessageBody::Markdown { title: _, text: _ } => "markdown",
38            MessageBody::ActionCard {
39                markdown: _,
40                title: _,
41                single_title: _,
42                single_url: _,
43                btn_orientation: _,
44                btn_json_list: _,
45            } => "action_card",
46        };
47
48        Self {
49            msgtype: msgtype.to_string(),
50            content: body,
51        }
52    }
53}
54
55/// 消息体
56#[derive(Debug, Deserialize, Serialize)]
57#[serde(rename_all = "snake_case")]
58pub enum MessageBody {
59    /// 文本消息
60    Text {
61        /// 消息内容,建议500字符以内
62        content: String,
63    },
64    /// 图片消息
65    Image {
66        /// 媒体文件mediaid,建议宽600像素 x 400像素,宽高比3 : 2。
67        /// 企业内部应用,通过上传媒体文件接口获取
68        /// 第三方企业应用,通过上传媒体文件接口获取
69        media_id: String,
70    },
71    /// 语音消息
72    Voice {
73        /// 媒体文件ID。
74        /// 企业内部应用,通过上传媒体文件接口获取
75        /// 第三方企业应用,通过上传媒体文件接口获取
76        media_id: String,
77        /// 正整数,小于60,表示音频时长
78        duration: i32,
79    },
80    /// 文件消息
81    File {
82        /// 媒体文件ID。
83        /// 企业内部应用,通过上传媒体文件接口获取
84        /// 第三方企业应用,通过上传媒体文件接口获取
85        media_id: String,
86    },
87    /// 链接消息
88    Link {
89        /// 消息点击链接地址,当发送消息为小程序时支持小程序跳转链接。
90        /// 企业内部应用参考消息链接说明
91        /// 第三方企业应用参考消息链接说明
92        #[serde(rename = "messageUrl")]
93        message_url: String,
94        /// 企业内部应用通过上传媒体文件接口获取
95        /// 第三方企业应用通过上传媒体文件接口获取
96        #[serde(rename = "picUrl")]
97        pic_url: String,
98        /// 消息标题,建议100字符以内
99        title: String,
100        /// 消息描述,建议500字符以内
101        text: String,
102    },
103    /// OA消息
104    Oa {
105        /// 消息点击链接地址,当发送消息为小程序时支持小程序跳转链接。
106        /// 企业内部应用参考消息链接说明
107        /// 第三方企业应用参考消息链接说明
108        message_url: String,
109        /// PC端点击消息时跳转到的地址
110        #[serde(skip_serializing_if = "Option::is_none")]
111        pc_message_url: Option<String>,
112        /// 消息头部内容
113        head: OaHead,
114        /// 消息状态栏,只支持接收者的userid列表,userid最多不能超过5个人。
115        /// 说明 不支持部门id列表,并且to_all_user不能传true
116        #[serde(skip_serializing_if = "Option::is_none")]
117        status_bar: Option<OaStatusBar>,
118        /// 消息体
119        body: OaBody,
120    },
121    /// markdown消息
122    Markdown {
123        /// 首屏会话透出的展示内容
124        title: String,
125        /// markdown格式的消息,最大不超过5000字符
126        text: String,
127    },
128    /// 卡片消息
129    ActionCard {
130        /// 消息内容,支持markdown,语法参考标准markdown语法。建议1000个字符以内
131        markdown: String,
132        /// 透出到会话列表和通知的文案
133        #[serde(skip_serializing_if = "Option::is_none")]
134        title: Option<String>,
135        /// 使用整体跳转ActionCard样式时的标题。必须与single_url同时设置,最长20个字符。
136        /// 说明 如果是整体跳转的ActionCard样式,则single_title和single_url必须设置。
137        #[serde(skip_serializing_if = "Option::is_none")]
138        single_title: Option<String>,
139        /// 消息点击链接地址,当发送消息为小程序时支持小程序跳转链接,最长500个字符。
140        /// 企业内部应用通过上传媒体文件接口获取
141        /// 第三方企业应用通过上传媒体文件接口获取
142        #[serde(skip_serializing_if = "Option::is_none")]
143        single_url: Option<String>,
144        /// 使用独立跳转ActionCard样式时的按钮排列方式:
145        /// 0:竖直排列
146        /// 1:横向排列
147        /// 必须与btn_json_list同时设置
148        #[serde(skip_serializing_if = "Option::is_none")]
149        btn_orientation: Option<String>,
150        /// 使用独立跳转ActionCard样式时的按钮列表;必须与btn_orientation同时设置,且长度不超过1000字符。
151        /// 说明 如果是独立跳转的ActionCard样式,则btn_json_list和btn_orientation必须设置
152        #[serde(skip_serializing_if = "Option::is_none")]
153        btn_json_list: Option<Vec<BtnJson>>,
154    },
155}
156
157#[derive(Debug, Deserialize, Serialize, Default)]
158pub struct OaHead {
159    /// 消息头部的背景颜色。
160    /// 长度限制为8个英文字符,其中前2为表示透明度,后6位表示颜色值。不要添加0x
161    pub bgcolor: String,
162    /// 消息的头部标题。
163    /// 企业内部应用
164    /// 如果是发送工作通知消息,该参数会被替换为当前应用名称。
165    /// 如果是发送消息到企业群或者发送普通消息,该参数有效,长度限制为最多10个字符。
166    /// 第三方企业应用
167    /// 如果是发送工作通知消息,该参数会被替换为当前应用名称。
168    /// 如果是发送普通消息,该参数有效,长度限制为最多10个字符
169    pub text: String,
170}
171
172#[derive(Debug, Deserialize, Serialize, Default)]
173pub struct OaStatusBar {
174    /// 状态栏文案
175    #[serde(skip_serializing_if = "Option::is_none")]
176    pub status_value: Option<String>,
177    /// 状态栏背景色,默认为黑色,推荐0xFF加六位颜色值
178    #[serde(skip_serializing_if = "Option::is_none")]
179    pub status_bg: Option<String>,
180}
181
182#[derive(Debug, Deserialize, Serialize, Default)]
183pub struct OaBody {
184    /// 消息体的标题,建议50个字符以内
185    #[serde(skip_serializing_if = "Option::is_none")]
186    pub title: Option<String>,
187    /// 消息体的表单,最多显示6个,超过会被隐藏
188    #[serde(skip_serializing_if = "Option::is_none")]
189    pub form: Option<Vec<KvPair>>,
190    /// 单行富文本信息
191    #[serde(skip_serializing_if = "Option::is_none")]
192    pub rich: Option<Rich>,
193    /// 消息体的内容,最多显示3行
194    #[serde(skip_serializing_if = "Option::is_none")]
195    pub content: Option<String>,
196    /// 消息体中的图片,支持图片资源@mediaId。建议宽600像素 x 400像素,宽高比3 : 2。
197    /// 企业内部应用通过上传媒体文件接口获取
198    /// 第三方企业应用通过上传媒体文件接口获取
199    #[serde(skip_serializing_if = "Option::is_none")]
200    pub image: Option<String>,
201    /// 自定义的附件数目。此数字仅供显示,钉钉不作验证
202    #[serde(skip_serializing_if = "Option::is_none")]
203    pub file_count: Option<String>,
204    /// 自定义的作者名字
205    #[serde(skip_serializing_if = "Option::is_none")]
206    pub author: Option<String>,
207}
208
209#[derive(Debug, Deserialize, Serialize, Default)]
210pub struct KvPair {
211    /// 消息体的关键字
212    #[serde(skip_serializing_if = "Option::is_none")]
213    pub key: Option<String>,
214    /// 消息体的关键字对应的值
215    #[serde(skip_serializing_if = "Option::is_none")]
216    pub value: Option<String>,
217}
218
219#[derive(Debug, Deserialize, Serialize, Default)]
220pub struct Rich {
221    /// 单行富文本信息的数目
222    #[serde(skip_serializing_if = "Option::is_none")]
223    pub num: Option<String>,
224    /// 单行富文本信息的单位
225    #[serde(skip_serializing_if = "Option::is_none")]
226    pub unit: Option<String>,
227}
228
229#[derive(Debug, Deserialize, Serialize, Default)]
230pub struct BtnJson {
231    /// 使用独立跳转ActionCard样式时的按钮的标题,最长20个字符
232    #[serde(skip_serializing_if = "Option::is_none")]
233    pub title: Option<String>,
234    /// 使用独立跳转ActionCard样式时的跳转链接,最长700个字符
235    #[serde(skip_serializing_if = "Option::is_none")]
236    pub action_url: Option<String>,
237}
238
239impl Default for MessageBody {
240    fn default() -> Self {
241        Self::Text {
242            content: String::from("默认消息"),
243        }
244    }
245}