Skip to main content

lsys_lib_sms/sms_lib/
mod.rs

1use base64::{
2    alphabet,
3    engine::{self, general_purpose},
4};
5use rand::seq::SliceRandom;
6use reqwest::Response;
7use reqwest::StatusCode;
8
9use std::{
10    collections::HashMap,
11    time::{SystemTime, SystemTimeError},
12};
13use tracing::debug;
14const CUSTOM_ENGINE: engine::GeneralPurpose =
15    engine::GeneralPurpose::new(&alphabet::URL_SAFE, general_purpose::PAD);
16
17#[derive(Debug)]
18pub enum SendError {
19    Next(String),
20    Finish(String),
21}
22
23#[derive(Debug, Clone)]
24pub enum SendStatus {
25    Progress,     //发送中
26    Completed,    //发送成功或完成接收
27    Failed(bool), //发送失败(可重试)
28}
29
30#[derive(Debug)]
31pub struct SendResultItem {
32    pub mobile: String,
33    pub status: SendStatus,
34    pub message: String,
35    pub send_id: String,
36}
37
38pub type BranchSendResult = Result<Vec<SendResultItem>, SendError>;
39
40#[derive(Debug)]
41pub struct SendDetailItem {
42    pub send_id: String,
43    pub status: SendNotifyStatus,
44    pub message: String,
45    pub code: String,
46    pub receive_time: Option<u64>,
47    pub send_time: Option<u64>,
48    pub mobile: Option<String>,
49}
50
51pub type BranchSendDetailResult = Result<Vec<SendDetailItem>, String>;
52
53#[derive(Debug, Clone)]
54pub enum SendNotifyStatus {
55    Progress,  //发送中
56    Completed, //完成接收或发送成功
57    Failed,    //发送失败
58}
59
60#[derive(Debug)]
61pub enum SendNotifyError {
62    Msg(String),
63    Sign(String),
64    Ignore,
65}
66
67#[derive(Debug, Clone)]
68pub struct SendNotifyItem {
69    pub status: SendNotifyStatus,
70    pub message: String,
71    pub code: String,
72    pub receive_time: Option<u64>,
73    pub send_time: Option<u64>,
74    pub send_id: String,
75    pub mobile: Option<String>,
76}
77
78pub type BranchSendNotifyResult = Result<Vec<SendNotifyItem>, SendNotifyError>;
79
80pub(crate) fn rand_str(len: usize) -> String {
81    let base_str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
82    let mut rng = &mut rand::thread_rng();
83    String::from_utf8(
84        base_str
85            .as_bytes()
86            .choose_multiple(&mut rng, len)
87            .cloned()
88            .collect(),
89    )
90    .unwrap_or_default()
91}
92
93pub(crate) fn now_time() -> Result<u64, SystemTimeError> {
94    Ok(SystemTime::now()
95        .duration_since(SystemTime::UNIX_EPOCH)?
96        .as_secs())
97}
98
99pub(crate) fn phone_numbers_check<'t>(
100    phone_numbers: &'t [&'t str],
101) -> Result<Vec<&'t str>, SendError> {
102    let out = phone_numbers
103        .iter()
104        .map(|e| e.trim())
105        .filter(|e| !e.is_empty())
106        .collect::<Vec<_>>();
107    if out.is_empty() {
108        return Err(SendError::Finish("not find any mobile".to_string()));
109    }
110    Ok(out)
111}
112
113pub(crate) async fn response_check(
114    result: Response,
115    is_json: bool,
116) -> Result<(StatusCode, String), String> {
117    let status = result.status();
118    let data = result
119        .bytes()
120        .await
121        .map_err(|e| format!("request read body fail:{}", e))?;
122    let res = unsafe { String::from_utf8_unchecked(data.to_vec()) };
123    //println!("{}", res);
124    debug!("sms response succ: {}", &res);
125    if is_json && !gjson::valid(&res) {
126        return Err(format!("body not json :{}", res));
127    }
128    Ok((status, res))
129}
130
131pub(crate) fn response_msg(result: &str, paths: &[&str]) -> String {
132    for path in paths {
133        let msg = gjson::get(result, path).to_string();
134        if !msg.is_empty() {
135            return format!("api fail,msg:{}", msg);
136        }
137    }
138    format!("api fail,data:{}", result)
139}
140
141pub fn template_map_to_arr(template_var: &str, template_map: &str) -> Option<Vec<String>> {
142    if let Ok(tmp) = serde_json::from_str::<HashMap<String, String>>(template_var) {
143        let map_data = template_map.split(',');
144        let mut set_data = vec![];
145        if !tmp.is_empty() {
146            for sp in map_data {
147                if let Some(tv) = tmp.get(sp) {
148                    set_data.push(tv.to_owned())
149                }
150            }
151        }
152        if !set_data.is_empty() {
153            return Some(set_data);
154        }
155    }
156    None
157}
158#[cfg(feature = "aliyun")]
159mod sender_aliyun;
160#[cfg(feature = "aliyun")]
161pub use sender_aliyun::*;
162#[cfg(feature = "huawei")]
163mod sender_huawei;
164#[cfg(feature = "huawei")]
165pub use sender_huawei::*;
166
167#[cfg(feature = "tencent")]
168mod sender_tencent;
169#[cfg(feature = "tencent")]
170pub use sender_tencent::*;
171
172#[cfg(feature = "jdcloud")]
173mod sender_jdcloud;
174#[cfg(feature = "jdcloud")]
175pub use sender_jdcloud::*;
176
177#[cfg(feature = "netease")]
178mod sender_netease;
179#[cfg(feature = "netease")]
180pub use sender_netease::*;
181
182#[cfg(feature = "cloopen")]
183mod sender_cloopen;
184#[cfg(feature = "cloopen")]
185pub use sender_cloopen::*;