wx_work/server/
recv_message.rs

1use std::str::FromStr;
2
3use xmltree::Element;
4
5use super::crypto::Crypto;
6use super::error::{MessageError, Result};
7
8#[derive(Debug, Clone)]
9pub struct RecvMessage {
10    pub to_user_name: String,
11    pub from_user_name: String,
12    pub agent_id: u64,
13    pub create_time: u64,
14    pub msg_id: u64,
15    pub msg_ty: RecvMessageType,
16}
17
18// TODO: add event types
19#[derive(Debug, Clone)]
20#[non_exhaustive]
21pub enum RecvMessageType {
22    Text(String),
23    Picture(Picture),
24    Voice(Voice),
25    Video(RecvVideo),
26    Location(Location),
27    Link(Link),
28}
29
30#[derive(Debug, Clone)]
31pub struct Picture {
32    pub pic_url: String,
33    pub media_id: String,
34}
35
36#[derive(Debug, Clone)]
37pub struct Voice {
38    pub media_id: String,
39    pub format: String,
40}
41
42#[derive(Debug, Clone)]
43pub struct RecvVideo {
44    pub media_id: String,
45    pub thumb_media_id: String,
46}
47
48#[derive(Debug, Clone)]
49pub struct Location {
50    pub location_x: f64,
51    pub location_y: f64,
52    pub scale: u32,
53    pub label: String,
54    pub ty: Option<String>,
55}
56
57#[derive(Debug, Clone)]
58pub struct Link {
59    pub title: String,
60    pub description: String,
61    pub url: String,
62    pub pic_url: String,
63}
64
65macro_rules! try_field {
66    ($name:expr, $element:expr) => {
67        match fetch($name, &$element) {
68            Some(d) => d.to_string(),
69            None => return Err(MessageError::MissingField($name)),
70        }
71    };
72}
73
74macro_rules! try_field_parse {
75    ($name:expr, $element:expr, $ty:ident) => {
76        match fetch($name, &$element) {
77            Some(d) => match $ty::from_str(d) {
78                Ok(d) => d,
79                Err(_) => {
80                    return Err(MessageError::InvalidFieldType(format!(
81                        "{} parse failed",
82                        $name
83                    )))
84                }
85            },
86            None => return Err(MessageError::MissingField($name)),
87        }
88    };
89}
90
91fn fetch<'a>(name: &str, element: &'a Element) -> Option<&'a str> {
92    let child = element.get_child(name)?;
93    child.children.get(0)?.as_text()
94}
95
96impl RecvMessage {
97    pub(crate) fn parse(
98        data: impl AsRef<[u8]>,
99        crypto: &Crypto,
100        timestamp: u64,
101        nonce: u64,
102        msg_signature: &str,
103    ) -> Result<RecvMessage> {
104        let xml = Element::parse(data.as_ref())
105            .map_err(|e| MessageError::ParseFailed(format!("{}", e)))?;
106
107        let to_user_name = try_field!("ToUserName", xml);
108        let agent_id = try_field_parse!("AgentID", xml, u64);
109        let msg_encrypt = try_field!("Encrypt", xml);
110
111        let sign = crypto.sign(msg_encrypt.clone(), timestamp, nonce);
112
113        if sign != msg_signature {
114            return Err(MessageError::InvalidSignature);
115        }
116
117        let payload = crypto
118            .decrypt(&msg_encrypt)
119            .map_err(|e| MessageError::DecryptFailed(format!("{}", e)))?;
120        let msg = payload.data;
121        let receiver_id = payload.receiver_id;
122        if &*receiver_id != to_user_name.as_bytes() {
123            return Err(MessageError::InvalidReceiver);
124        }
125
126        let inner_xml = Element::parse(&*msg)
127            .map_err(|e| MessageError::ParseFailed(format!("inner: {}", e)))?;
128
129        let from_user_name = try_field!("FromUserName", inner_xml);
130        let create_time = try_field_parse!("CreateTime", inner_xml, u64);
131        let msg_id = try_field_parse!("MsgId", inner_xml, u64);
132
133        let msg_ty = match &*try_field!("MsgType", inner_xml) {
134            "text" => {
135                let content = try_field!("Content", inner_xml);
136                RecvMessageType::Text(content)
137            }
138            "image" => {
139                let pic_url = try_field!("PicUrl", inner_xml);
140                let media_id = try_field!("MediaId", inner_xml);
141                let pic = Picture { pic_url, media_id };
142                RecvMessageType::Picture(pic)
143            }
144            "voice" => {
145                let format = try_field!("Format", inner_xml);
146                let media_id = try_field!("MediaId", inner_xml);
147                let voice = Voice { format, media_id };
148                RecvMessageType::Voice(voice)
149            }
150            "video" => {
151                let media_id = try_field!("MediaId", inner_xml);
152                let thumb_media_id = try_field!("ThumbMediaId", inner_xml);
153                let video = RecvVideo {
154                    media_id,
155                    thumb_media_id,
156                };
157                RecvMessageType::Video(video)
158            }
159            ty => return Err(MessageError::InvalidMessageType(ty.to_string())), // TODO
160        };
161
162        Ok(RecvMessage {
163            to_user_name,
164            agent_id,
165            from_user_name,
166            create_time,
167            msg_id,
168            msg_ty,
169        })
170    }
171}