Compare commits

..
Author SHA1 Message Date
phoenix d21d72f596 Fixed variable in workflow
swoosh Build / Check (pull_request) Successful in 34s
Release Tagging / release (pull_request) Successful in 39s
swoosh Build / Test Suite (pull_request) Successful in 34s
swoosh Build / Rustfmt (pull_request) Successful in 34s
swoosh Build / Clippy (pull_request) Successful in 33s
swoosh Build / build (pull_request) Successful in 33s
2026-06-03 20:43:32 -04:00
phoenix 29fcf41a9c Need to try this out
swoosh Build / Check (pull_request) Successful in 36s
Release Tagging / release (pull_request) Successful in 39s
swoosh Build / Rustfmt (pull_request) Successful in 30s
swoosh Build / Test Suite (pull_request) Successful in 35s
swoosh Build / build (pull_request) Successful in 32s
swoosh Build / Clippy (pull_request) Successful in 37s
2026-06-03 20:40:25 -04:00
phoenix f0ffc580e6 Adding crate
Release Tagging / release (pull_request) Successful in 32s
swoosh Build / Test Suite (pull_request) Successful in 25s
swoosh Build / Check (pull_request) Successful in 1m22s
swoosh Build / Rustfmt (pull_request) Successful in 43s
swoosh Build / build (pull_request) Successful in 26s
swoosh Build / Clippy (pull_request) Successful in 1m0s
2026-06-03 20:29:02 -04:00
phoenix 57d16c6996 Fixed some things 2026-06-03 20:28:36 -04:00
phoenix a065388805 Added crates
Release Tagging / release (pull_request) Successful in 33s
swoosh Build / Check (pull_request) Successful in 37s
swoosh Build / Test Suite (pull_request) Successful in 24s
swoosh Build / Rustfmt (pull_request) Successful in 33s
swoosh Build / Clippy (pull_request) Successful in 31s
swoosh Build / build (pull_request) Successful in 27s
2026-06-03 20:08:01 -04:00
phoenix 3c45061026 Next step 2026-06-03 20:07:30 -04:00
phoenix 5752741972 cargo fmt
Release Tagging / release (pull_request) Successful in 27s
swoosh Build / Check (pull_request) Successful in 39s
swoosh Build / Test Suite (pull_request) Successful in 25s
swoosh Build / Rustfmt (pull_request) Successful in 30s
swoosh Build / Clippy (pull_request) Successful in 27s
swoosh Build / build (pull_request) Successful in 24s
2026-06-03 16:52:46 -04:00
phoenix 2971363d2f Saving changes
swoosh Build / Rustfmt (pull_request) Successful in 56s
Release Tagging / release (pull_request) Successful in 37s
swoosh Build / Check (pull_request) Successful in 37s
swoosh Build / Test Suite (pull_request) Successful in 30s
swoosh Build / build (pull_request) Successful in 24s
swoosh Build / Clippy (pull_request) Successful in 55s
2026-06-03 16:52:19 -04:00
phoenix 0cbf63c78d Adding networking dependencies
Release Tagging / release (pull_request) Successful in 30s
swoosh Build / Check (pull_request) Successful in 36s
swoosh Build / Test Suite (pull_request) Successful in 24s
swoosh Build / Clippy (pull_request) Successful in 28s
swoosh Build / build (pull_request) Successful in 24s
swoosh Build / Rustfmt (pull_request) Successful in 1m24s
2026-06-03 16:15:33 -04:00
phoenix e487a2c275 Adding type needed to send message
Release Tagging / release (pull_request) Successful in 30s
swoosh Build / Check (pull_request) Successful in 34s
swoosh Build / Rustfmt (pull_request) Successful in 27s
swoosh Build / Test Suite (pull_request) Successful in 40s
swoosh Build / Clippy (pull_request) Successful in 33s
swoosh Build / build (pull_request) Successful in 33s
2026-06-03 16:09:30 -04:00
phoenix 97dcb360ab Fixed type
Release Tagging / release (pull_request) Successful in 36s
swoosh Build / Check (pull_request) Successful in 36s
swoosh Build / Test Suite (pull_request) Successful in 31s
swoosh Build / Rustfmt (pull_request) Successful in 33s
swoosh Build / Clippy (pull_request) Successful in 32s
swoosh Build / build (pull_request) Successful in 37s
2026-06-03 15:55:09 -04:00
phoenix 7c3b51d6a0 Renaming struct 2026-06-03 15:52:43 -04:00
phoenix c6103ebba4 Adding type that maps to the response when a message is sent 2026-06-03 15:51:57 -04:00
7 changed files with 180 additions and 51 deletions
+2 -2
View File
@@ -3,10 +3,10 @@ name: Release Tagging
on: on:
push: push:
branches: branches:
- main - v0.3.x
pull_request: pull_request:
branches: branches:
- main - v0.3.x
jobs: jobs:
release: release:
+7 -7
View File
@@ -3,10 +3,10 @@ name: swoosh Build
on: on:
push: push:
branches: branches:
- main - v0.3.x
pull_request: pull_request:
branches: branches:
- main - v0.3.x
jobs: jobs:
check: check:
@@ -21,7 +21,7 @@ jobs:
mkdir -p ~/.ssh mkdir -p ~/.ssh
echo "${{ secrets.MYREPO_TOKEN }}" > ~/.ssh/textsender-models_deploy_key echo "${{ secrets.MYREPO_TOKEN }}" > ~/.ssh/textsender-models_deploy_key
chmod 600 ~/.ssh/textsender-models_deploy_key chmod 600 ~/.ssh/textsender-models_deploy_key
ssh-keyscan ${{ secrets.MY_HOST }} >> ~/.ssh/known_hosts ssh-keyscan ${{ secret.MY_HOST }} >> ~/.ssh/known_hosts
eval $(ssh-agent -s) eval $(ssh-agent -s)
ssh-add -v ~/.ssh/textsender-models_deploy_key ssh-add -v ~/.ssh/textsender-models_deploy_key
@@ -40,7 +40,7 @@ jobs:
mkdir -p ~/.ssh mkdir -p ~/.ssh
echo "${{ secrets.MYREPO_TOKEN }}" > ~/.ssh/textsender-models_deploy_key echo "${{ secrets.MYREPO_TOKEN }}" > ~/.ssh/textsender-models_deploy_key
chmod 600 ~/.ssh/textsender-models_deploy_key chmod 600 ~/.ssh/textsender-models_deploy_key
ssh-keyscan ${{ secrets.MY_HOST }} >> ~/.ssh/known_hosts ssh-keyscan ${{ secret.MY_HOST }} >> ~/.ssh/known_hosts
eval $(ssh-agent -s) eval $(ssh-agent -s)
ssh-add -v ~/.ssh/textsender-models_deploy_key ssh-add -v ~/.ssh/textsender-models_deploy_key
@@ -60,7 +60,7 @@ jobs:
mkdir -p ~/.ssh mkdir -p ~/.ssh
echo "${{ secrets.MYREPO_TOKEN }}" > ~/.ssh/textsender-models_deploy_key echo "${{ secrets.MYREPO_TOKEN }}" > ~/.ssh/textsender-models_deploy_key
chmod 600 ~/.ssh/textsender-models_deploy_key chmod 600 ~/.ssh/textsender-models_deploy_key
ssh-keyscan ${{ secrets.MY_HOST }} >> ~/.ssh/known_hosts ssh-keyscan ${{ secret.MY_HOST }} >> ~/.ssh/known_hosts
eval $(ssh-agent -s) eval $(ssh-agent -s)
ssh-add -v ~/.ssh/textsender-models_deploy_key ssh-add -v ~/.ssh/textsender-models_deploy_key
@@ -80,7 +80,7 @@ jobs:
mkdir -p ~/.ssh mkdir -p ~/.ssh
echo "${{ secrets.MYREPO_TOKEN }}" > ~/.ssh/textsender-models_deploy_key echo "${{ secrets.MYREPO_TOKEN }}" > ~/.ssh/textsender-models_deploy_key
chmod 600 ~/.ssh/textsender-models_deploy_key chmod 600 ~/.ssh/textsender-models_deploy_key
ssh-keyscan ${{ secrets.MY_HOST }} >> ~/.ssh/known_hosts ssh-keyscan ${{ secret.MY_HOST }} >> ~/.ssh/known_hosts
eval $(ssh-agent -s) eval $(ssh-agent -s)
ssh-add -v ~/.ssh/textsender-models_deploy_key ssh-add -v ~/.ssh/textsender-models_deploy_key
@@ -99,7 +99,7 @@ jobs:
mkdir -p ~/.ssh mkdir -p ~/.ssh
echo "${{ secrets.MYREPO_TOKEN }}" > ~/.ssh/textsender-models_deploy_key echo "${{ secrets.MYREPO_TOKEN }}" > ~/.ssh/textsender-models_deploy_key
chmod 600 ~/.ssh/textsender-models_deploy_key chmod 600 ~/.ssh/textsender-models_deploy_key
ssh-keyscan ${{ secrets.MY_HOST }} >> ~/.ssh/known_hosts ssh-keyscan ${{ secret.MY_HOST }} >> ~/.ssh/known_hosts
eval $(ssh-agent -s) eval $(ssh-agent -s)
ssh-add -v ~/.ssh/textsender-models_deploy_key ssh-add -v ~/.ssh/textsender-models_deploy_key
Generated
+3 -3
View File
@@ -1573,7 +1573,7 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]] [[package]]
name = "swoosh" name = "swoosh"
version = "0.3.0" version = "0.2.1"
dependencies = [ dependencies = [
"base64-ng", "base64-ng",
"const_format", "const_format",
@@ -1658,8 +1658,8 @@ dependencies = [
[[package]] [[package]]
name = "textsender_models" name = "textsender_models"
version = "0.2.5" version = "0.3.0"
source = "git+ssh://git@git.kundeng.us/phoenix/textsender-models.git?tag=v0.2.5-v0.3.0-migrate-38dbcb09f7-111#38dbcb09f7c2c572f429ec382c01e14b72b1d615" source = "git+ssh://git@git.kundeng.us/phoenix/textsender-models.git?tag=v0.3.0-20-15820d5505-111#15820d550526aea3016821c01f508d8d590292cc"
dependencies = [ dependencies = [
"const_format", "const_format",
"dotenvy", "dotenvy",
+2 -2
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "swoosh" name = "swoosh"
version = "0.3.0" version = "0.2.1"
edition = "2024" edition = "2024"
rust-version = "1.95" rust-version = "1.95"
description = "Library to send text messages" description = "Library to send text messages"
@@ -18,7 +18,7 @@ uuid = { version = "1.23.1", features = ["v4", "serde"] }
base64-ng = { version = "1.0.6" } base64-ng = { version = "1.0.6" }
const_format = { version = "0.2.36" } const_format = { version = "0.2.36" }
josekit = { version = "0.10.3" } 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" } textsender_models = { git = "ssh://git@git.kundeng.us/phoenix/textsender-models.git", tag = "v0.3.0-20-15820d5505-111" }
[dev-dependencies] [dev-dependencies]
tempfile = { version = "3.27.0" } tempfile = { version = "3.27.0" }
+129 -23
View File
@@ -1,3 +1,5 @@
// use josekit::jwk::alg::ec::EcCurve::P256;
pub mod types; pub mod types;
pub async fn send_mssage( pub async fn send_mssage(
@@ -5,8 +7,8 @@ pub async fn send_mssage(
contact: &textsender_models::contact::Contact, contact: &textsender_models::contact::Contact,
param: crate::twilio::types::Parameters, param: crate::twilio::types::Parameters,
config: &textsender_models::config::auxiliary::TwilioConfig, config: &textsender_models::config::auxiliary::TwilioConfig,
) -> Result<reqwest::Response, std::io::Error> { ) -> Result<crate::twilio::api::types::TwilioAPIMessage, std::io::Error> {
if config.account_sid.is_empty() { if config.account_sid.is_nil() {
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"))
@@ -21,6 +23,7 @@ pub async fn send_mssage(
); );
headers.insert("Accept", "application/json".parse().unwrap()); headers.insert("Accept", "application/json".parse().unwrap());
let auth = generate_auth(config).unwrap(); let auth = generate_auth(config).unwrap();
// headers.insert("Authorization", "Basic QUNlZmExZWY1MTYzMTRjOWQxYTY4Y2JkNjU3ZGU0OTI3NzpmNGExZjJiMGI3OWVhMzczNTA3OGMyZDhlZTk2ODRlMQ==".parse()?);
headers.insert("Authorization", auth.parse().unwrap()); headers.insert("Authorization", auth.parse().unwrap());
let mut params = std::collections::HashMap::new(); let mut params = std::collections::HashMap::new();
@@ -38,8 +41,13 @@ pub async fn send_mssage(
// params.insert("PersistentAction", "string"); // params.insert("PersistentAction", "string");
params.insert("ShortenUrls", "true"); params.insert("ShortenUrls", "true");
let date = match param.schedule_at { let date = match param.schedule_at {
Some(date_value) => date_value.to_string(), Some(date_value) => {
None => String::new(), let converted = date_value.to_string();
converted
}
None => {
String::new()
}
}; };
if param.schedule && is_scheduleable(&Some(now), &param.schedule_at) { if param.schedule && is_scheduleable(&Some(now), &param.schedule_at) {
params.insert("ScheduleType", "fixed"); params.insert("ScheduleType", "fixed");
@@ -52,18 +60,42 @@ pub async fn send_mssage(
params.insert("Body", msg.content.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", config.account_sid
config.account_sid
); );
// let client = reqwest::Client::new();
/*
let request = client.request(reqwest::Method::POST, url)
.headers(headers)
.form(&params);
*/
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) => {
reqwest::StatusCode::OK => Ok(response), match response.json::<crate::twilio::api::types::TwilioAPIMessage>().await {
status => Err(std::io::Error::other(format!( Ok(resp) => {
"Unaccounted status: {status:?}" Ok(resp)
))), }
}, Err(err) => {
Err(std::io::Error::other(err))
}
}
/*
match response.text().await {
Ok(body) => {
let mut obj = crate::twilio::api::types::TwilioAPIMessage {
..Default::default()
};
Ok(obj)
}
Err(err) => {
Err(std::io::Error::other(err))
}
}
*/
}
Err(err) => Err(std::io::Error::other(err.to_string())), Err(err) => Err(std::io::Error::other(err.to_string())),
} }
} }
@@ -72,17 +104,30 @@ pub async fn send_mssage(
} }
} }
fn generate_auth( fn generate_auth(config: &textsender_models::config::auxiliary::TwilioConfig) -> Result<String, base64_ng::EncodeError> {
config: &textsender_models::config::auxiliary::TwilioConfig,
) -> 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()) {
Ok(encoded) => Ok(format!("Basic {encoded}")), Ok(encoded) => {
Err(err) => Err(err), Ok(format!("Basic {encoded}"))
}
Err(err) => {
Err(err)
}
} }
} }
/* /*
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = reqwest::Client::builder()
.build()?;
let mut headers = reqwest::header::HeaderMap::new();
headers.insert("Content-Type", "application/x-www-form-urlencoded".parse()?);
headers.insert("Accept", "application/json".parse()?);
headers.insert("Authorization", "Basic QUNlZmExZWY1MTYzMTRjOWQxYTY4Y2JkNjU3ZGU0OTI3NzpmNGExZjJiMGI3OWVhMzczNTA3OGMyZDhlZTk2ODRlMQ==".parse()?);
let mut params = std::collections::HashMap::new(); let mut params = std::collections::HashMap::new();
params.insert("To", "16303831708");
params.insert("StatusCallback", "http://OjQozHznkhNTTR.vpnrM1zdXFuiQ"); params.insert("StatusCallback", "http://OjQozHznkhNTTR.vpnrM1zdXFuiQ");
params.insert("MaxPrice", "1"); params.insert("MaxPrice", "1");
params.insert("ProvideFeedback", "true"); params.insert("ProvideFeedback", "true");
@@ -95,29 +140,90 @@ fn generate_auth(
params.insert("PersistentAction", "string"); params.insert("PersistentAction", "string");
params.insert("PersistentAction", "string"); params.insert("PersistentAction", "string");
params.insert("ShortenUrls", "true"); params.insert("ShortenUrls", "true");
params.insert("ScheduleType", "fixed");
params.insert("SendAt", "2026-06-03T19:50:00.0Z");
params.insert("SendAsMms", "true"); params.insert("SendAsMms", "true");
params.insert("RiskCheck", "enable"); params.insert("RiskCheck", "enable");
params.insert("From", "12243026041");
params.insert("MessagingServiceSid", "MG803f3676706b92eb02e18dd820c447f2");
params.insert("Body", "This is a test");
let request = client.request(reqwest::Method::POST, "https://api.twilio.com/2010-04-01/Accounts/ACefa1ef516314c9d1a68cbd657de49277/Messages.json")
.headers(headers)
.form(&params);
let response = request.send().await?; let response = request.send().await?;
let body = response.text().await?; let body = response.text().await?;
println!("{}", body); println!("{}", body);
Ok(()) Ok(())
} }
*/ */
/**
func (m *MessageSender) Send(msg message.Message, number contact.Contact, sendTime *time.Time) (*types.TwilioResult, map[string]any, error) {
if m.Config == nil {
return nil, nil, fmt.Errorf("Config has not been initialized")
}
now := time.Now()
client := twilio.NewRestClientWithParams(twilio.ClientParams{
Username: m.Config.AccountSID,
Password: m.Config.AuthToken,
})
params := &twilioApi.CreateMessageParams{}
params.SetTo(number.PhoneNumber)
params.SetFrom(m.Config.Number)
params.SetMessagingServiceSid(m.Config.ServiceSID)
params.SetBody(msg.Content)
if sendTime != nil && isSchedulable(&now, sendTime) {
params.SetSendAt(*sendTime)
params.SetScheduleType(Schedule_Type)
}
if resp, err := client.Api.CreateMessage(params); err != nil {
return nil, nil, fmt.Errorf("Error sending message: %v", err)
} else {
if twilioRespMarshaled, err := json.Marshal(*resp); err != nil {
return nil, nil, fmt.Errorf("Error parsing result: %v", err)
} else {
var rawObject map[string]any
if err := json.Unmarshal(twilioRespMarshaled, &rawObject); err != nil {
return resp, nil, err
} else {
return resp, rawObject, nil
}
}
}
}
func isSchedulable(now *time.Time, scheduled *time.Time) bool {
early := now.Add(Schedulable_Limit_In_Seconds * time.Second)
if scheduled.After(early) {
return true
} else {
return false
}
}
*/
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().checked_add(time::Duration::seconds(300)).unwrap();
.unwrap() if *schedule_at > early {
.checked_add(time::Duration::seconds(300)) true
.unwrap(); } else {
*schedule_at > early false
}
}
None => {
false
} }
None => false,
} }
} }
+32 -13
View File
@@ -3,28 +3,20 @@ pub struct TwilioAPIMessage {
pub account_sid: String, pub account_sid: String,
pub api_version: String, pub api_version: String,
pub body: String, pub body: String,
#[serde(with = "time::serde::rfc3339::option")] // pub date_created: time::OffsetDateTime,
pub date_created: Option<time::OffsetDateTime>,
#[serde(with = "time::serde::rfc3339::option")]
pub date_sent: Option<time::OffsetDateTime>, pub date_sent: Option<time::OffsetDateTime>,
#[serde(with = "time::serde::rfc3339::option")]
pub date_updated: Option<time::OffsetDateTime>, pub date_updated: Option<time::OffsetDateTime>,
pub direction: String, pub direction: String,
// Not definite of what type this is // Not definite of what type this is
#[serde(skip_serializing_if = "String::is_empty")] pub error_code: Option<String>,
pub error_code: String,
// Not definite of what type this is // Not definite of what type this is
#[serde(skip_serializing_if = "String::is_empty")] pub error_message: Option<String>,
pub error_message: String,
pub from: String, pub from: String,
pub messaging_service_sid: String, pub messaging_service_sid: String,
pub num_media: String, pub num_media: String,
pub num_segments: String, pub num_segments: String,
#[serde(skip_serializing_if = "String::is_empty")] pub price: Option<String>,
pub price: String, pub price_unit: Option<String>,
#[serde(skip_serializing_if = "String::is_empty")]
pub price_unit: String,
#[serde(skip_serializing_if = "String::is_empty")]
pub sid: String, pub sid: String,
pub status: String, pub status: String,
pub subresource_uris: SubresourceURI, pub subresource_uris: SubresourceURI,
@@ -36,3 +28,30 @@ pub struct TwilioAPIMessage {
pub struct SubresourceURI { pub struct SubresourceURI {
pub media: String, pub media: String,
} }
/*
{
"account_sid": "ACefa1ef516314c9d1a68cbd657de49277",
"api_version": "2010-04-01",
"body": "This is a test",
"date_created": "Wed, 03 Jun 2026 19:40:55 +0000",
"date_sent": null,
"date_updated": "Wed, 03 Jun 2026 19:40:55 +0000",
"direction": "outbound-api",
"error_code": null,
"error_message": null,
"from": "+12243026041",
"messaging_service_sid": "MG803f3676706b92eb02e18dd820c447f2",
"num_media": "0",
"num_segments": "0",
"price": null,
"price_unit": null,
"sid": "SMf5c09c776c931bdb00c6e274da2ce34d",
"status": "scheduled",
"subresource_uris": {
"media": "/2010-04-01/Accounts/ACefa1ef516314c9d1a68cbd657de49277/Messages/SMf5c09c776c931bdb00c6e274da2ce34d/Media.json"
},
"to": "+16303831708",
"uri": "/2010-04-01/Accounts/ACefa1ef516314c9d1a68cbd657de49277/Messages/SMf5c09c776c931bdb00c6e274da2ce34d.json"
}
*/
+5 -1
View File
@@ -1,4 +1,8 @@
/// Parameters - Controls flow of sending message // Things needed to send a message
// Contact - Receipient to send to (textsender-models)
// Message - The content to sent (textsender-models)
// Parameters - Controls flow of sending message
// Config - Contains info needed to send message (textsender-models)
pub struct Parameters { pub struct Parameters {
pub schedule: bool, pub schedule: bool,
pub schedule_at: Option<time::OffsetDateTime>, pub schedule_at: Option<time::OffsetDateTime>,