async_wechat/official_account/
message.rs1use chrono::Utc;
2use quick_xml::{de::from_str, se};
3use serde::{Deserialize, Serialize};
4use std::pin::Pin;
5
6use actix_web::{
7 FromRequest, HttpRequest,
8 dev::Payload,
9 error,
10 web::{self},
11};
12
13use super::signature::signature;
14
15pub struct MsgType;
16
17impl MsgType {
18 pub const TEXT: &str = "text";
20 pub const IMAGE: &str = "image";
22 pub const VOICE: &str = "voice";
24 pub const VIDEO: &str = "video";
26 pub const MINIPROGRAMPAGE: &str = "miniprogrampage";
28 pub const SHORTVIDEO: &str = "shortvideo";
30 pub const LOCATION: &str = "location";
32 pub const LINK: &str = "link";
34 pub const MUSIC: &str = "music";
36 pub const NEWS: &str = "news";
38 pub const TRANSFER: &str = "transfer_customer_service";
40 pub const EVENT: &str = "event";
42}
43
44pub struct EventType;
45
46impl EventType {
47 pub const SUBSCRIBE: &str = "subscribe";
49 pub const UNSUBSCRIBE: &str = "unsubscribe";
51 pub const SCAN: &str = "SCAN";
53}
54
55#[derive(Debug, Serialize, Deserialize)]
56#[serde(rename = "xml")]
57pub struct WechatMessage {
58 #[serde(rename = "ToUserName")]
59 pub to_user_name: String, #[serde(rename = "FromUserName")]
61 pub from_user_name: String, #[serde(rename = "CreateTime")]
63 pub create_time: u64, #[serde(rename = "MsgType")]
65 pub msg_type: String, #[serde(rename = "Content")]
67 pub content: Option<String>, #[serde(rename = "MsgId")]
69 pub msg_id: Option<u64>, #[serde(rename = "Idx")]
71 pub idx: Option<u64>, #[serde(rename = "Event")]
73 pub event: Option<String>, #[serde(rename = "EventKey")]
75 pub event_key: Option<String>, #[serde(rename = "Ticket")]
77 pub ticket: Option<String>, }
79
80#[derive(Debug, Serialize, Deserialize)]
81#[serde(rename = "xml")]
82pub struct WeChatResponse {
83 #[serde(rename = "ToUserName")]
84 pub to_user_name: String,
85 #[serde(rename = "FromUserName")]
86 pub from_user_name: String,
87 #[serde(rename = "CreateTime")]
88 pub create_time: u64,
89 #[serde(rename = "MsgType")]
90 pub msg_type: String,
91 #[serde(rename = "Content")]
92 pub content: String,
93}
94
95pub struct MessageHandler {
96 pub message: WechatMessage,
97}
98
99impl MessageHandler {
100 pub fn to_string(
101 &self,
102 message: &WeChatResponse,
103 ) -> Result<String, Box<dyn std::error::Error>> {
104 match se::to_string(message) {
105 Ok(s) => Ok(s),
106 Err(e) => Err(e.into()),
107 }
108 }
109}
110
111impl WechatMessage {
112 pub fn get_open_id(&self) -> String {
113 self.from_user_name.clone()
114 }
115
116 pub fn get_gh_id(&self) -> String {
117 self.to_user_name.clone()
118 }
119
120 pub fn plaintext(&self, content: &str) -> WeChatResponse {
121 let now = Utc::now().naive_utc();
122 WeChatResponse {
123 to_user_name: self.from_user_name.clone(),
124 from_user_name: self.to_user_name.clone(),
125 create_time: now.and_utc().timestamp() as u64,
126 msg_type: MsgType::TEXT.to_string(),
127 content: content.to_string(),
128 }
129 }
130}
131
132#[derive(Debug, Serialize, Deserialize)]
133pub struct WechatQuery {
134 timestamp: String,
135 nonce: String,
136 signature: String,
137 echostr: Option<String>, }
139
140impl FromRequest for MessageHandler {
141 type Error = actix_web::Error;
142 type Future = Pin<Box<dyn Future<Output = Result<Self, Self::Error>>>>;
143
144 fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future {
145 let fut = web::Bytes::from_request(req, payload);
146
147 let query_future = web::Query::<WechatQuery>::from_query(req.query_string())
148 .map(|q| q.into_inner())
149 .map_err(|e| {
150 actix_web::error::ErrorBadRequest(format!(
151 "Failed to parse query parameters: {}",
152 e
153 ))
154 });
155
156 Box::pin(async move {
157 let query = match query_future {
158 Ok(q) => q,
159 Err(e) => return Err(e),
160 };
161
162 if query.signature != signature("wechat", &query.timestamp, &query.nonce) {
164 return Err(error::ErrorUnauthorized("Invalid signature"));
165 }
166
167 let bytes = fut.await?;
168 let xml_str = String::from_utf8_lossy(&bytes);
169
170 let message = from_str::<WechatMessage>(&xml_str)
171 .map_err(|e| error::ErrorBadRequest(format!("Invalid XML input: {}", e)))?;
172
173 Ok(MessageHandler { message })
174 })
175 }
176}