diff --git a/src/args.rs b/src/args.rs index a70c8cf..28318cc 100644 --- a/src/args.rs +++ b/src/args.rs @@ -119,4 +119,6 @@ pub enum PostCommands { DeleteSms { msg_id: BoxStr }, /// Delete all message from inbox. DeleteAllSms, + /// Send an SMS message. + SendMessage { number: BoxStr, message: BoxStr }, } diff --git a/src/common.rs b/src/common.rs index d3f67c5..1c8acbd 100644 --- a/src/common.rs +++ b/src/common.rs @@ -41,8 +41,50 @@ pub fn ucs2_decode(hex_str: &str) -> EyreResult { Ok(str) } +pub fn ucs2_encode(s: &str) -> BoxStr { + let bytes: Vec = s.encode_utf16().flat_map(|u| u.to_be_bytes()).collect(); + hex::encode(bytes).into_boxed_str() +} + pub fn create_table() -> comfy_table::Table { let mut table = comfy_table::Table::new(); table.load_preset(comfy_table::presets::UTF8_FULL_CONDENSED); table } + +pub struct Datetime { + datetime: jiff::Zoned, +} + +impl Datetime { + /// The timezone part can be ignored because it is a lie. It + /// either says +8 or +12, but the actual time is always in GMT+3. + /// Examples + /// 26,06,05,13,06,24,+8 + /// 26,06,18,16,35,06,+12 + pub fn parse(datetime: &str) -> EyreResult { + let datetime = datetime.trim_end_matches(",+8").trim_end_matches(",+12"); + let datetime = format!("{datetime},Africa/Addis_Ababa"); + let datetime = jiff::Zoned::strptime("%y,%m,%d,%H,%M,%S,%Q", datetime)?; + Ok(Self { datetime }) + } + + pub fn now() -> Self { + Self { + datetime: jiff::Zoned::now(), + } + } + + pub fn router_time(&self) -> BoxStr { + let datetime = self.datetime.strftime("%y,%m,%d,%H,%M,%S"); + // lets use the chinese timezone for this + format!("{datetime},+8").into_boxed_str() + } +} + +impl std::fmt::Display for Datetime { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let datetime = self.datetime.strftime("%F %r"); + write!(f, "{datetime}") + } +} diff --git a/src/main.rs b/src/main.rs index b7d07d0..299cf1e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -71,6 +71,10 @@ async fn main() -> EyreResult<()> { .await?; } } + PostCommands::SendMessage { number, message } => { + let params = SendSmsParams::new(&number, &message); + router.execute::(params).await? + } }, }; router.logout().await?; diff --git a/src/proc_get.rs b/src/proc_get.rs index 55bb10e..a56674d 100644 --- a/src/proc_get.rs +++ b/src/proc_get.rs @@ -578,31 +578,6 @@ impl std::fmt::Display for MessageTagStatus { } } -struct Datetime { - datetime: jiff::Zoned, -} - -impl Datetime { - /// The timezone part can be ignored because it is a lie. It - /// either says +8 or +12, but the actual time is always in GMT+3. - /// Examples - /// 26,06,05,13,06,24,+8 - /// 26,06,18,16,35,06,+12 - pub fn parse(datetime: &str) -> EyreResult { - let datetime = datetime.trim_end_matches(",+8").trim_end_matches(",+12"); - let datetime = format!("{datetime},Africa/Addis_Ababa"); - let datetime = jiff::Zoned::strptime("%y,%m,%d,%H,%M,%S,%Q", datetime)?; - Ok(Self { datetime }) - } -} - -impl std::fmt::Display for Datetime { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let datetime = self.datetime.strftime("%F %r"); - write!(f, "{datetime}") - } -} - impl ProcGet for SmsInbox { const CMD: &str = "sms_data_total"; type Params = SmsInboxParams; diff --git a/src/proc_post.rs b/src/proc_post.rs index 110b9de..86222ee 100644 --- a/src/proc_post.rs +++ b/src/proc_post.rs @@ -91,3 +91,50 @@ impl ProcPost for DeleteSms { Ok(()) } } + +#[derive(Debug, Deserialize)] +pub struct SendSms { + pub result: BoxStr, +} + +#[derive(Debug, Serialize, Default)] +pub struct SendSmsParams { + #[serde(rename = "notCallback")] + not_callback: BoxStr, + encode_type: BoxStr, + #[serde(rename = "ID")] + id: BoxStr, + + #[serde(rename = "Number")] + number: BoxStr, + #[serde(rename = "MessageBody")] + message_body: BoxStr, + sms_time: BoxStr, +} + +impl SendSmsParams { + pub fn new(number: &str, message: &str) -> Self { + Self { + not_callback: "true".into(), + encode_type: "UNICODE".into(), + id: "-1".into(), + + number: number.into(), + sms_time: Datetime::now().router_time(), + message_body: ucs2_encode(message), + } + } +} + +impl ProcPost for SendSms { + const GOFORM_ID: &str = "SEND_SMS"; + type Params = SendSmsParams; + + fn print_table(&self) -> EyreResult<()> { + let mut table = create_table(); + table.set_header(["Send Result"]).add_row([&self.result]); + println!("{table}"); + + Ok(()) + } +}