Compare commits

...
Author SHA1 Message Date
phoenix 7873d09d3e Adding license (#18)
Release Tagging / release (push) Successful in 40s
swoosh Build / Check (push) Successful in 1m6s
swoosh Build / Rustfmt (push) Successful in 28s
swoosh Build / Test Suite (push) Successful in 1m31s
swoosh Build / Clippy (push) Successful in 1m37s
swoosh Build / build (push) Successful in 2m1s
Reviewed-on: #18
2026-07-14 11:18:40 -04:00
phoenix 84ba4c21ec Dependency name change (#17)
Release Tagging / release (push) Successful in 59s
swoosh Build / Rustfmt (push) Successful in 39s
swoosh Build / Test Suite (push) Successful in 1m41s
swoosh Build / Clippy (push) Successful in 1m33s
swoosh Build / Check (push) Successful in 3m10s
swoosh Build / build (push) Successful in 1m48s
Reviewed-on: #17
2026-07-13 11:44:24 -04:00
phoenix 1c49242bdf Update rust (#16)
Release Tagging / release (push) Successful in 1m13s
swoosh Build / Check (push) Successful in 1m34s
swoosh Build / build (push) Successful in 1m56s
swoosh Build / Rustfmt (push) Successful in 27s
swoosh Build / Clippy (push) Successful in 2m10s
swoosh Build / Test Suite (push) Successful in 4m6s
Reviewed-on: #16
2026-07-10 11:42:16 -04:00
phoenix 50e4b209f5 Update rust (#15)
swoosh pr / Check (pull_request) Successful in 1m50s
swoosh pr / Clippy (pull_request) Successful in 1m15s
swoosh pr / Rustfmt (pull_request) Successful in 1m29s
Release Tagging / release (pull_request) Successful in 1m46s
Release Tagging / release (push) Successful in 1m1s
swoosh Build / Check (push) Successful in 1m0s
swoosh Build / Rustfmt (push) Successful in 42s
swoosh Build / Test Suite (push) Successful in 2m21s
swoosh Build / Clippy (push) Successful in 1m27s
swoosh Build / build (push) Successful in 1m33s
Reviewed-on: #15
2026-07-05 17:08:08 -04:00
phoenix d36483e6f8 Fix (#14)
swoosh Build / Test Suite (push) Successful in 1m6s
swoosh Build / Rustfmt (push) Successful in 33s
swoosh Build / Check (push) Successful in 1m46s
Release Tagging / release (push) Successful in 44s
swoosh Build / Clippy (push) Successful in 1m39s
swoosh Build / build (push) Successful in 1m39s
swoosh pr / Check (pull_request) Has been cancelled
swoosh pr / Rustfmt (pull_request) Has been cancelled
swoosh pr / Clippy (pull_request) Has been cancelled
Release Tagging / release (pull_request) Successful in 42s
Reviewed-on: #14
2026-07-04 20:59:05 -04:00
phoenix 768b2b6d08 tsk-11: New code to send message (#13)
Release Tagging / release (push) Successful in 46s
swoosh Build / Rustfmt (push) Successful in 32s
swoosh Build / Test Suite (push) Successful in 1m34s
swoosh Build / Check (push) Successful in 1m52s
swoosh Build / Clippy (push) Successful in 1m28s
swoosh Build / build (push) Successful in 1m15s
Closes #11

Reviewed-on: #13
2026-07-03 00:45:59 -04:00
phoenix e1e730fd1c bump: textsender_models (#10)
Release Tagging / release (push) Successful in 59s
swoosh Build / Rustfmt (push) Successful in 28s
swoosh Build / build (push) Successful in 1m29s
swoosh Build / Clippy (push) Successful in 2m6s
swoosh Build / Check (push) Successful in 1m11s
swoosh Build / Test Suite (push) Successful in 1m37s
Reviewed-on: #10
2026-06-27 17:27:34 -04:00
phoenix 9f174da464 Rust 1.96 (#9)
swoosh Build / Test Suite (push) Successful in 1m36s
Release Tagging / release (push) Successful in 1m42s
swoosh Build / Rustfmt (push) Successful in 40s
swoosh Build / Check (push) Successful in 1m30s
swoosh Build / Clippy (push) Successful in 1m29s
swoosh Build / build (push) Successful in 2m0s
Reviewed-on: #9
2026-06-14 17:52:28 -04:00
phoenix 3cfa757a07 Updating packages (#8)
Release Tagging / release (push) Successful in 55s
swoosh Build / Check (push) Successful in 1m34s
swoosh Build / Rustfmt (push) Successful in 39s
swoosh Build / Test Suite (push) Successful in 2m2s
swoosh Build / Clippy (push) Successful in 1m24s
swoosh Build / build (push) Successful in 1m21s
Reviewed-on: #8
2026-06-13 16:43:23 -04:00
phoenix 1aa2c5870c Updating package name (#7)
Release Tagging / release (push) Successful in 40s
swoosh Build / Rustfmt (push) Successful in 37s
swoosh Build / Check (push) Successful in 1m37s
swoosh Build / Clippy (push) Successful in 1m13s
swoosh Build / build (push) Successful in 1m24s
swoosh Build / Test Suite (push) Successful in 2m14s
Reviewed-on: #7
2026-06-13 12:33:21 -04:00
phoenix 122f1183ac Bug fix (#6)
Release Tagging / release (push) Successful in 53s
swoosh Build / Check (push) Successful in 1m6s
swoosh Build / Rustfmt (push) Successful in 29s
swoosh Build / Test Suite (push) Successful in 1m14s
swoosh Build / Clippy (push) Successful in 1m1s
swoosh Build / build (push) Successful in 1m10s
Reviewed-on: #6
2026-06-07 15:21:23 -04:00
9 changed files with 1168 additions and 602 deletions
+73
View File
@@ -0,0 +1,73 @@
name: swoosh pr
on:
pull_request:
branches:
- main
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
check:
name: Check
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v6
- uses: actions-rust-lang/setup-rust-toolchain@v1
with:
toolchain: 1.97
- uses: Swatinem/rust-cache@v2
- run: |
mkdir -p ~/.ssh
echo "${{ secrets.MYREPO_TOKEN }}" > ~/.ssh/textsender-models_deploy_key
chmod 600 ~/.ssh/textsender-models_deploy_key
ssh-keyscan ${{ secrets.MY_HOST }} >> ~/.ssh/known_hosts
eval $(ssh-agent -s)
ssh-add -v ~/.ssh/textsender-models_deploy_key
cargo check
fmt:
name: Rustfmt
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v6
- uses: actions-rust-lang/setup-rust-toolchain@v1
with:
toolchain: 1.97
- uses: Swatinem/rust-cache@v2
- run: rustup component add rustfmt
- run: |
mkdir -p ~/.ssh
echo "${{ secrets.MYREPO_TOKEN }}" > ~/.ssh/textsender-models_deploy_key
chmod 600 ~/.ssh/textsender-models_deploy_key
ssh-keyscan ${{ secrets.MY_HOST }} >> ~/.ssh/known_hosts
eval $(ssh-agent -s)
ssh-add -v ~/.ssh/textsender-models_deploy_key
cargo fmt --all -- --check
clippy:
name: Clippy
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v6
- uses: actions-rust-lang/setup-rust-toolchain@v1
with:
toolchain: 1.97
- uses: Swatinem/rust-cache@v2
- run: rustup component add clippy
- run: |
mkdir -p ~/.ssh
echo "${{ secrets.MYREPO_TOKEN }}" > ~/.ssh/textsender-models_deploy_key
chmod 600 ~/.ssh/textsender-models_deploy_key
ssh-keyscan ${{ secrets.MY_HOST }} >> ~/.ssh/known_hosts
eval $(ssh-agent -s)
ssh-add -v ~/.ssh/textsender-models_deploy_key
cargo clippy -- -D warnings
+2 -2
View File
@@ -13,14 +13,14 @@ jobs:
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v5 uses: actions/checkout@v6
with: with:
fetch-depth: 0 # Important for git describe --tags fetch-depth: 0 # Important for git describe --tags
- name: Install Rust - name: Install Rust
uses: actions-rs/toolchain@v1 uses: actions-rs/toolchain@v1
with: with:
toolchain: 1.95 toolchain: 1.97
components: cargo components: cargo
- name: Extract Version from Cargo.toml - name: Extract Version from Cargo.toml
+19 -14
View File
@@ -4,19 +4,21 @@ on:
push: push:
branches: branches:
- main - main
pull_request:
branches: concurrency:
- main group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs: jobs:
check: check:
name: Check name: Check
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
- uses: actions-rust-lang/setup-rust-toolchain@v1 - uses: actions-rust-lang/setup-rust-toolchain@v1
with: with:
toolchain: 1.95 toolchain: 1.97
- uses: Swatinem/rust-cache@v2
- run: | - run: |
mkdir -p ~/.ssh mkdir -p ~/.ssh
echo "${{ secrets.MYREPO_TOKEN }}" > ~/.ssh/textsender-models_deploy_key echo "${{ secrets.MYREPO_TOKEN }}" > ~/.ssh/textsender-models_deploy_key
@@ -32,10 +34,11 @@ jobs:
name: Test Suite name: Test Suite
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
- uses: actions-rust-lang/setup-rust-toolchain@v1 - uses: actions-rust-lang/setup-rust-toolchain@v1
with: with:
toolchain: 1.95 toolchain: 1.97
- uses: Swatinem/rust-cache@v2
- run: | - run: |
mkdir -p ~/.ssh mkdir -p ~/.ssh
echo "${{ secrets.MYREPO_TOKEN }}" > ~/.ssh/textsender-models_deploy_key echo "${{ secrets.MYREPO_TOKEN }}" > ~/.ssh/textsender-models_deploy_key
@@ -51,10 +54,11 @@ jobs:
name: Rustfmt name: Rustfmt
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
- uses: actions-rust-lang/setup-rust-toolchain@v1 - uses: actions-rust-lang/setup-rust-toolchain@v1
with: with:
toolchain: 1.95 toolchain: 1.97
- uses: Swatinem/rust-cache@v2
- run: rustup component add rustfmt - run: rustup component add rustfmt
- run: | - run: |
mkdir -p ~/.ssh mkdir -p ~/.ssh
@@ -71,10 +75,11 @@ jobs:
name: Clippy name: Clippy
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
- uses: actions-rust-lang/setup-rust-toolchain@v1 - uses: actions-rust-lang/setup-rust-toolchain@v1
with: with:
toolchain: 1.95 toolchain: 1.97
- uses: Swatinem/rust-cache@v2
- run: rustup component add clippy - run: rustup component add clippy
- run: | - run: |
mkdir -p ~/.ssh mkdir -p ~/.ssh
@@ -91,10 +96,11 @@ jobs:
name: build name: build
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
- uses: actions-rust-lang/setup-rust-toolchain@v1 - uses: actions-rust-lang/setup-rust-toolchain@v1
with: with:
toolchain: 1.95 toolchain: 1.97
- uses: Swatinem/rust-cache@v2
- run: | - run: |
mkdir -p ~/.ssh mkdir -p ~/.ssh
echo "${{ secrets.MYREPO_TOKEN }}" > ~/.ssh/textsender-models_deploy_key echo "${{ secrets.MYREPO_TOKEN }}" > ~/.ssh/textsender-models_deploy_key
@@ -105,4 +111,3 @@ jobs:
ssh-add -v ~/.ssh/textsender-models_deploy_key ssh-add -v ~/.ssh/textsender-models_deploy_key
cargo build cargo build
Generated
+759 -511
View File
File diff suppressed because it is too large Load Diff
+8 -15
View File
@@ -1,24 +1,17 @@
[package] [package]
name = "swoosh" name = "swoosh"
version = "0.3.1" version = "0.5.4"
edition = "2024" edition = "2024"
rust-version = "1.95" rust-version = "1.97"
license = "MIT"
description = "Library to send text messages" description = "Library to send text messages"
[dependencies] [dependencies]
serde = { version = "1.0.228", features = ["derive"] } serde = { version = "1.0.228", features = ["derive"] }
serde_json = { version = "1.0.149" } serde_json = { version = "1.0.150" }
tokio = { version = "1.52.2", features = ["full"] } reqwest = { version = "0.13.4", features = ["form", "json", "blocking", "multipart", "stream"] }
futures = { version = "0.3.32" } time = { version = "0.3.53", features = ["formatting", "macros", "parsing", "serde"] }
http = { version = "1.4.0" } base64-ng = { version = "1.3.7" }
reqwest = { version = "0.13.3", features = ["form", "json", "blocking", "multipart", "stream"] } schedtxt_models = { git = "ssh://git@git.kundeng.us/phoenix/schedtxt_models.git", tag = "v0.5.3", features = ["config", "contact", "message"] }
rand = { version = "0.10.1" }
time = { version = "0.3.47", features = ["formatting", "macros", "parsing", "serde"] }
uuid = { version = "1.23.1", features = ["v4", "serde"] }
base64-ng = { version = "1.0.6" }
const_format = { version = "0.2.36" }
josekit = { version = "0.10.3" }
textsender_models = { git = "ssh://git@git.kundeng.us/phoenix/textsender-models.git", tag = "v0.2.5-v0.3.0-migrate-38dbcb09f7-111" }
[dev-dependencies] [dev-dependencies]
tempfile = { version = "3.27.0" }
+22
View File
@@ -0,0 +1,22 @@
Copyright (c) 2026 Kun Deng.
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
+3
View File
@@ -0,0 +1,3 @@
# swoosh
A library used to send text messages and is used in conjunction of the `schedtxt`
project.
+106
View File
@@ -1 +1,107 @@
pub mod twilio; pub mod twilio;
#[derive(Default, Debug)]
pub struct SendMsg {
config: VendorConfig,
message: Message,
recipient: Recipient,
}
impl SendMsg {
pub fn load_config(
&mut self,
auth_token: &str,
phone_number: &str,
service_sid: &str,
account_sid: &str,
) {
self.config = VendorConfig {
auth_token: auth_token.to_string(),
phone_number: phone_number.to_string(),
service_sid: service_sid.to_string(),
account_sid: account_sid.to_string(),
}
}
pub fn load_message(&mut self, content: &str) {
self.message = Message {
content: content.to_string(),
}
}
pub fn load_recipient(&mut self, recpient: &str) {
self.recipient = Recipient {
phone_number: recpient.to_string(),
}
}
pub async fn send_message(
&self,
param: &crate::twilio::types::Parameters,
) -> Result<reqwest::Response, std::io::Error> {
if self.recipient.phone_number.is_empty() {
Err(std::io::Error::other("Recipient not provided"))
} else if self.message.content.is_empty() {
Err(std::io::Error::other("Message not provided"))
} else if self.config.phone_number.is_empty()
|| self.config.service_sid.is_empty()
|| self.config.auth_token.is_empty()
|| self.config.account_sid.is_empty()
{
Err(std::io::Error::other("Config not populated"))
} else {
let client = match reqwest::Client::builder().build() {
Ok(client) => client,
Err(err) => {
return Err(std::io::Error::other(err));
}
};
let url = twilio::api::twilio_url(&self.config.account_sid);
let headers = match twilio::api::init_headers(
&self.config.account_sid,
&self.config.auth_token,
) {
Ok(headers) => headers,
Err(err) => {
return Err(std::io::Error::other(err));
}
};
let params = twilio::api::initial_params(
&self.recipient.phone_number,
&self.message.content,
&self.config.phone_number,
&self.config.service_sid,
param,
);
let request = client.post(url).headers(headers).form(&params);
match request.send().await {
Ok(response) => match response.status() {
reqwest::StatusCode::CREATED => Ok(response),
status => Err(std::io::Error::other(format!(
"Unaccounted status: {status:?}"
))),
},
Err(err) => Err(std::io::Error::other(err)),
}
}
}
}
#[derive(Default, Debug)]
pub struct VendorConfig {
pub auth_token: String,
pub phone_number: String,
pub service_sid: String,
pub account_sid: String,
}
#[derive(Default, Debug)]
pub struct Message {
pub content: String,
}
#[derive(Default, Debug)]
pub struct Recipient {
pub phone_number: String,
}
+176 -60
View File
@@ -1,5 +1,7 @@
pub mod types; pub mod types;
/// Converts the response into a json value
/// That way you do not need to share swoosh's dependency
pub async fn response_to_json(response: reqwest::Response) -> serde_json::Value { pub async fn response_to_json(response: reqwest::Response) -> serde_json::Value {
match response.json().await { match response.json().await {
Ok(json_body) => json_body, Ok(json_body) => json_body,
@@ -7,18 +9,21 @@ pub async fn response_to_json(response: reqwest::Response) -> serde_json::Value
} }
} }
pub async fn send_mssage( /// Backwards compatible
msg: &textsender_models::message::Message, pub use send_message as send_mssage;
contact: &textsender_models::contact::Contact,
param: crate::twilio::types::Parameters, /// Send a message
config: &textsender_models::config::auxiliary::TwilioConfig, pub async fn send_message(
msg: &schedtxt_models::message::Message,
contact: &schedtxt_models::contact::Contact,
param: &crate::twilio::types::Parameters,
config: &schedtxt_models::config::auxiliary::TwilioConfig,
) -> Result<reqwest::Response, std::io::Error> { ) -> Result<reqwest::Response, std::io::Error> {
if config.account_sid.is_empty() { if config.account_sid.is_empty() {
Err(std::io::Error::other("Account SID is empty")) Err(std::io::Error::other("Account SID is empty"))
} else if config.auth_token.is_empty() { } else if config.auth_token.is_empty() {
Err(std::io::Error::other(" Auth token is empty")) Err(std::io::Error::other(" Auth token is empty"))
} else { } else {
let now = time::OffsetDateTime::now_utc();
match reqwest::Client::builder().build() { match reqwest::Client::builder().build() {
Ok(client) => { Ok(client) => {
let mut headers = reqwest::header::HeaderMap::new(); let mut headers = reqwest::header::HeaderMap::new();
@@ -30,33 +35,7 @@ pub async fn send_mssage(
let auth = generate_auth(config).unwrap(); let auth = generate_auth(config).unwrap();
headers.insert("Authorization", auth.parse().unwrap()); headers.insert("Authorization", auth.parse().unwrap());
let mut params = std::collections::HashMap::new(); let params = init_params(contact, msg, config, param);
params.insert("To", contact.phone_number.as_str());
// params.insert("StatusCallback", "http://OjQozHznkhNTTR.vpnrM1zdXFuiQ");
// params.insert("MaxPrice", "1");
params.insert("ProvideFeedback", "true");
// params.insert("Attempt", "5");
// params.insert("ValidityPeriod", "1537");
params.insert("ForceDelivery", "false");
params.insert("ContentRetention", "retain");
params.insert("AddressRetention", "obfuscate");
params.insert("SmartEncoded", "true");
// params.insert("PersistentAction", "string");
// params.insert("PersistentAction", "string");
params.insert("ShortenUrls", "true");
let date = match param.schedule_at {
Some(date_value) => date_value.to_string(),
None => String::new(),
};
if param.schedule && is_scheduleable(&Some(now), &param.schedule_at) {
params.insert("ScheduleType", "fixed");
params.insert("SendAt", date.as_str());
}
params.insert("SendAsMms", "true");
params.insert("RiskCheck", "enable");
params.insert("From", config.phone_number.as_str());
params.insert("MessagingServiceSid", config.service_sid.as_str());
params.insert("Body", msg.content.as_str());
let url = format!( let url = format!(
"https://api.twilio.com/2010-04-01/Accounts/{}/Messages.json", "https://api.twilio.com/2010-04-01/Accounts/{}/Messages.json",
@@ -66,7 +45,7 @@ pub async fn send_mssage(
let request = client.post(url).headers(headers).form(&params); let request = client.post(url).headers(headers).form(&params);
match request.send().await { match request.send().await {
Ok(response) => match response.status() { Ok(response) => match response.status() {
reqwest::StatusCode::OK => Ok(response), reqwest::StatusCode::CREATED => Ok(response),
status => Err(std::io::Error::other(format!( status => Err(std::io::Error::other(format!(
"Unaccounted status: {status:?}" "Unaccounted status: {status:?}"
))), ))),
@@ -79,8 +58,162 @@ pub async fn send_mssage(
} }
} }
pub fn twilio_url(account_sid: &str) -> String {
format!(
"https://api.twilio.com/2010-04-01/Accounts/{}/Messages.json",
account_sid
)
}
pub fn init_headers(
account_sid: &str,
auth_token: &str,
) -> Result<reqwest::header::HeaderMap, std::io::Error> {
let mut headers = reqwest::header::HeaderMap::new();
headers.insert(
"Content-Type",
match "application/x-www-form-urlencoded".parse() {
Ok(value) => value,
Err(err) => {
return Err(std::io::Error::other(err));
}
},
);
let auth = match g_auth(account_sid, auth_token) {
Ok(auth) => auth,
Err(err) => {
return Err(std::io::Error::other(err));
}
};
headers.insert(
"Authorization",
match format!("Basic {auth}").parse() {
Ok(auth) => auth,
Err(err) => {
return Err(std::io::Error::other(err));
}
},
);
Ok(headers)
}
pub fn initial_params(
recpient: &str,
content: &str,
config_source_phone_number: &str,
config_service_sid: &str,
param: &crate::twilio::types::Parameters,
) -> std::collections::HashMap<String, String> {
let now = time::OffsetDateTime::now_utc();
let mut params = std::collections::HashMap::new();
params.insert(String::from("To"), recpient.to_string());
params.insert(String::from("ProvideFeedback"), String::from("true"));
params.insert(String::from("ForceDelivery"), String::from("false"));
params.insert(String::from("ContentRetention"), String::from("retain"));
params.insert(String::from("AddressRetention"), String::from("obfuscate"));
params.insert(String::from("SmartEncoded"), String::from("true"));
params.insert(String::from("ShortenUrls"), String::from("true"));
let date = match param.schedule_at {
Some(date_value) => match convert_time_to_iso(date_value) {
Ok(converted) => converted,
Err(err) => {
eprintln!("Error: {err:?}");
String::new()
}
},
None => String::new(),
};
let scheduled_at = match param.schedule_at {
Some(s) => s,
None => time::OffsetDateTime::now_utc(),
};
if param.schedule && is_scheduleable(Some(&now), Some(&scheduled_at)) {
params.insert(String::from("ScheduleType"), String::from("fixed"));
params.insert(String::from("SendAt"), date.clone());
}
params.insert(String::from("SendAsMms"), String::from("true"));
params.insert(String::from("RiskCheck"), String::from("enable"));
params.insert(String::from("From"), config_source_phone_number.to_string());
params.insert(
String::from("MessagingServiceSid"),
config_service_sid.to_string(),
);
params.insert(String::from("Body"), content.to_string());
params
}
fn init_params(
contact: &schedtxt_models::contact::Contact,
message: &schedtxt_models::message::Message,
config: &schedtxt_models::config::auxiliary::TwilioConfig,
param: &crate::twilio::types::Parameters,
) -> std::collections::HashMap<String, String> {
let now = time::OffsetDateTime::now_utc();
let mut params = std::collections::HashMap::new();
params.insert(String::from("To"), contact.phone_number.clone());
params.insert(String::from("ProvideFeedback"), String::from("true"));
params.insert(String::from("ForceDelivery"), String::from("false"));
params.insert(String::from("ContentRetention"), String::from("retain"));
params.insert(String::from("AddressRetention"), String::from("obfuscate"));
params.insert(String::from("SmartEncoded"), String::from("true"));
params.insert(String::from("ShortenUrls"), String::from("true"));
let date = match param.schedule_at {
Some(date_value) => match convert_time_to_iso(date_value) {
Ok(converted) => converted,
Err(err) => {
eprintln!("Error: {err:?}");
String::new()
}
},
None => String::new(),
};
let scheduled_at = match param.schedule_at {
Some(s) => s,
None => time::OffsetDateTime::now_utc(),
};
if param.schedule && is_scheduleable(Some(&now), Some(&scheduled_at)) {
params.insert(String::from("ScheduleType"), String::from("fixed"));
params.insert(String::from("SendAt"), date.clone());
}
params.insert(String::from("SendAsMms"), String::from("true"));
params.insert(String::from("RiskCheck"), String::from("enable"));
params.insert(String::from("From"), config.phone_number.clone());
params.insert(
String::from("MessagingServiceSid"),
config.service_sid.clone(),
);
params.insert(String::from("Body"), message.content.clone());
/*
let mut params = std::collections::HashMap::new();
params.insert("StatusCallback", "http://OjQozHznkhNTTR.vpnrM1zdXFuiQ");
params.insert("MaxPrice", "1");
params.insert("ProvideFeedback", "true");
params.insert("Attempt", "5");
params.insert("ValidityPeriod", "1537");
params.insert("ForceDelivery", "false");
params.insert("ContentRetention", "retain");
params.insert("AddressRetention", "obfuscate");
params.insert("SmartEncoded", "true");
params.insert("PersistentAction", "string");
params.insert("PersistentAction", "string");
params.insert("ShortenUrls", "true");
params.insert("SendAsMms", "true");
params.insert("RiskCheck", "enable");
*/
params
}
fn convert_time_to_iso(time: time::OffsetDateTime) -> Result<String, time::error::Format> {
use time::format_description::well_known::Iso8601;
time.format(&Iso8601::DEFAULT)
}
fn generate_auth( fn generate_auth(
config: &textsender_models::config::auxiliary::TwilioConfig, config: &schedtxt_models::config::auxiliary::TwilioConfig,
) -> Result<String, base64_ng::EncodeError> { ) -> Result<String, base64_ng::EncodeError> {
let input = format!("{}:{}", config.account_sid, config.auth_token); let input = format!("{}:{}", config.account_sid, config.auth_token);
match base64_ng::encode(input.as_bytes()) { match base64_ng::encode(input.as_bytes()) {
@@ -88,40 +221,23 @@ fn generate_auth(
Err(err) => Err(err), Err(err) => Err(err),
} }
} }
/*
let mut params = std::collections::HashMap::new();
params.insert("StatusCallback", "http://OjQozHznkhNTTR.vpnrM1zdXFuiQ");
params.insert("MaxPrice", "1");
params.insert("ProvideFeedback", "true");
params.insert("Attempt", "5");
params.insert("ValidityPeriod", "1537");
params.insert("ForceDelivery", "false");
params.insert("ContentRetention", "retain");
params.insert("AddressRetention", "obfuscate");
params.insert("SmartEncoded", "true");
params.insert("PersistentAction", "string");
params.insert("PersistentAction", "string");
params.insert("ShortenUrls", "true");
params.insert("SendAsMms", "true");
params.insert("RiskCheck", "enable");
let response = request.send().await?; fn g_auth(account_sid: &str, auth_token: &str) -> Result<String, base64_ng::EncodeError> {
let body = response.text().await?; let input = format!("{}:{}", account_sid, auth_token);
println!("{}", body); base64_ng::encode(input.as_bytes())
Ok(())
} }
*/
const DEFAULT_SCHEDULING_SECONDS: i64 = 300;
fn is_scheduleable( fn is_scheduleable(
now: &Option<time::OffsetDateTime>, now: Option<&time::OffsetDateTime>,
scheduled: &Option<time::OffsetDateTime>, scheduled: Option<&time::OffsetDateTime>,
) -> bool { ) -> bool {
match scheduled { match scheduled {
Some(schedule_at) => { Some(schedule_at) => {
let early = now let early = now
.unwrap() .unwrap()
.checked_add(time::Duration::seconds(300)) .checked_add(time::Duration::seconds(DEFAULT_SCHEDULING_SECONDS))
.unwrap(); .unwrap();
*schedule_at > early *schedule_at > early
} }