wx_work/
message.rs

1use itertools::Itertools;
2use serde::ser::{SerializeMap, Serializer};
3use serde::{Deserialize, Serialize};
4use thiserror::Error;
5
6#[derive(Debug, Deserialize)]
7pub struct MessageResponse {
8    errcode: u64,
9    errmsg: String,
10    pub invaliduser: Option<String>,
11    pub invalidparty: Option<String>,
12    pub invalidtag: Option<String>,
13}
14
15#[derive(Error, Debug)]
16pub enum MessageBuildError {
17    #[error("receiver can not be empty")]
18    EmptyReceiver,
19}
20
21pub struct MessageBuilder {
22    to_users: Vec<String>,
23    to_parties: Vec<String>,
24    to_tags: Vec<String>,
25    data: MessageType,
26    agent_id: u64,
27    safe: Option<bool>,
28    enable_id_trans: Option<bool>,
29    enable_duplicate_check: Option<bool>,
30    duplicate_check_interval: Option<u32>,
31}
32
33#[derive(Debug)]
34pub struct Message {
35    to_users: Vec<String>, //指定接收消息的成员,成员ID列表(多个接收者用‘|’分隔,最多支持1000个)。特殊情况:指定为”@all”,则向该企业应用的全部成员发送
36    to_parties: Vec<String>, //指定接收消息的部门,部门ID列表,多个接收者用‘|’分隔,最多支持100个。当touser为”@all”时忽略本参数
37    to_tags: Vec<String>, // 指定接收消息的标签,标签ID列表,多个接收者用‘|’分隔,最多支持100个。当touser为”@all”时忽略本参数
38    data: MessageType,    // 数据
39    agent_id: u64, // 企业应用的id,整型。企业内部开发,可在应用的设置页面查看;第三方服务商,可通过接口 获取企业授权信息 获取该参数值
40    safe: Option<bool>, // 表示是否是保密消息,0表示否,1表示是,默认0
41    enable_id_trans: Option<bool>, // 表示是否开启id转译,0表示否,1表示是,默认0
42    enable_duplicate_check: Option<bool>, // 表示是否开启重复消息检查,0表示否,1表示是,默认0
43    duplicate_check_interval: Option<u32>, // 表示是否重复消息检查的时间间隔,默认1800s,最大不超过4小时
44}
45
46#[derive(Debug)]
47enum MessageType {
48    Text(Text),
49    File(File),
50    Image(Image),
51}
52
53#[derive(Debug, Serialize)]
54struct Text {
55    content: String,
56}
57
58#[derive(Debug, Serialize)]
59struct File {
60    media_id: String,
61}
62
63#[derive(Debug, Serialize)]
64struct Image {
65    media_id: String,
66}
67
68impl MessageBuilder {
69    fn new(agent_id: u64, ty: MessageType) -> Self {
70        MessageBuilder {
71            agent_id,
72            to_users: vec![],
73            to_parties: vec![],
74            to_tags: vec![],
75            data: ty,
76            safe: None,
77            enable_id_trans: None,
78            enable_duplicate_check: None,
79            duplicate_check_interval: None,
80        }
81    }
82    pub fn new_text(agent_id: u64, content: String) -> Self {
83        let data = MessageType::Text(Text { content });
84        Self::new(agent_id, data)
85    }
86
87    pub fn new_file(agent_id: u64, media_id: String) -> Self {
88        let data = MessageType::File(File { media_id });
89        Self::new(agent_id, data)
90    }
91
92    pub fn new_image(agent_id: u64, media_id: String) -> Self {
93        let data = MessageType::Image(Image { media_id });
94        Self::new(agent_id, data)
95    }
96
97    pub fn with_user(mut self, user: String) -> Self {
98        self.to_users.push(user);
99        self
100    }
101
102    pub fn with_party(mut self, party: String) -> Self {
103        self.to_parties.push(party);
104        self
105    }
106
107    pub fn with_tag(mut self, tag: String) -> Self {
108        self.to_tags.push(tag);
109        self
110    }
111
112    pub fn safe(mut self, flag: bool) -> Self {
113        self.safe = Some(flag);
114        self
115    }
116
117    pub fn enable_id_trans(mut self, flag: bool) -> Self {
118        self.enable_id_trans = Some(flag);
119        self
120    }
121
122    pub fn enable_duplicate_check(mut self, flag: bool) -> Self {
123        self.enable_duplicate_check = Some(flag);
124        self
125    }
126
127    pub fn duplicate_check_interval(mut self, duration: u32) -> Self {
128        self.duplicate_check_interval = Some(duration);
129        self
130    }
131
132    pub fn build(self) -> Result<Message, MessageBuildError> {
133        if self.to_users.is_empty() && self.to_parties.is_empty() && self.to_tags.is_empty() {
134            return Err(MessageBuildError::EmptyReceiver);
135        }
136
137        // TODO: add more checks
138        let ret = Message {
139            to_users: self.to_users,
140            to_parties: self.to_parties,
141            to_tags: self.to_tags,
142            data: self.data,
143            agent_id: self.agent_id,
144            safe: self.safe,
145            enable_id_trans: self.enable_id_trans,
146            enable_duplicate_check: self.enable_duplicate_check,
147            duplicate_check_interval: self.duplicate_check_interval,
148        };
149        Ok(ret)
150    }
151}
152
153impl Serialize for Message {
154    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
155    where
156        S: Serializer,
157    {
158        use MessageType::*;
159
160        let mut map = serializer.serialize_map(None)?;
161
162        map.serialize_entry("touser", &self.to_users.iter().join("|"))?;
163        map.serialize_entry("toparty", &self.to_parties.iter().join("|"))?;
164        map.serialize_entry("totag", &self.to_tags.iter().join("|"))?;
165        map.serialize_entry("agentid", &self.agent_id)?;
166
167        if let Some(d) = self.safe {
168            map.serialize_entry("safe", &(d as u8))?;
169        }
170        if let Some(d) = self.enable_id_trans {
171            map.serialize_entry("enable_id_trans", &(d as u8))?;
172        }
173        if let Some(d) = self.enable_duplicate_check {
174            map.serialize_entry("enable_duplicate_check", &(d as u8))?;
175        }
176        if let Some(d) = self.duplicate_check_interval {
177            map.serialize_entry("duplicate_check_interval", &d)?;
178        }
179
180        match &self.data {
181            Text(t) => {
182                map.serialize_entry("msgtype", "text")?;
183                map.serialize_entry("text", t)?;
184            }
185            File(t) => {
186                map.serialize_entry("msgtype", "file")?;
187                map.serialize_entry("file", t)?;
188            }
189            Image(t) => {
190                map.serialize_entry("msgtype", "image")?;
191                map.serialize_entry("image", t)?;
192            }
193        }
194
195        map.end()
196    }
197}