Compare commits

..
Author SHA1 Message Date
phoenix c9d167da8d Changed name of function
swoosh Build / Check (pull_request) Successful in 31s
swoosh Build / Test Suite (pull_request) Successful in 28s
Release Tagging / release (pull_request) Successful in 1m10s
swoosh Build / Rustfmt (pull_request) Failing after 31s
swoosh Build / Clippy (pull_request) Successful in 33s
swoosh Build / build (pull_request) Successful in 33s
2026-06-04 21:06:08 -04:00
phoenix eeb46dc84d Added function to convert response to a json value 2026-06-04 21:05:11 -04:00
phoenix 8dd26cee44 Merge pull request 'v0.3.0' (#1) from v0.3.x into main
Release Tagging / release (push) Successful in 37s
swoosh Build / Check (push) Successful in 41s
swoosh Build / Test Suite (push) Successful in 32s
swoosh Build / Clippy (push) Successful in 51s
swoosh Build / build (push) Successful in 34s
swoosh Build / Rustfmt (push) Successful in 2m5s
Reviewed-on: #1
2026-06-04 20:40:22 -04:00
phoenix 04086e3182 Send message (#4)
swoosh Build / Test Suite (pull_request) Successful in 1m10s
Release Tagging / release (pull_request) Successful in 36s
swoosh Build / Check (pull_request) Successful in 40s
swoosh Build / Rustfmt (pull_request) Successful in 25s
swoosh Build / Clippy (pull_request) Successful in 1m2s
swoosh Build / build (pull_request) Successful in 27s
Reviewed-on: #4
2026-06-04 20:37:40 -04:00
7 changed files with 91 additions and 156 deletions
+2 -2
View File
@@ -3,10 +3,10 @@ name: Release Tagging
on:
push:
branches:
- v0.3.x
- main
pull_request:
branches:
- v0.3.x
- main
jobs:
release:
+7 -7
View File
@@ -3,10 +3,10 @@ name: swoosh Build
on:
push:
branches:
- v0.3.x
- main
pull_request:
branches:
- v0.3.x
- main
jobs:
check:
@@ -21,7 +21,7 @@ jobs:
mkdir -p ~/.ssh
echo "${{ secrets.MYREPO_TOKEN }}" > ~/.ssh/textsender-models_deploy_key
chmod 600 ~/.ssh/textsender-models_deploy_key
ssh-keyscan ${{ secret.MYHOST }} >> ~/.ssh/known_hosts
ssh-keyscan ${{ secrets.MY_HOST }} >> ~/.ssh/known_hosts
eval $(ssh-agent -s)
ssh-add -v ~/.ssh/textsender-models_deploy_key
@@ -40,7 +40,7 @@ jobs:
mkdir -p ~/.ssh
echo "${{ secrets.MYREPO_TOKEN }}" > ~/.ssh/textsender-models_deploy_key
chmod 600 ~/.ssh/textsender-models_deploy_key
ssh-keyscan ${{ secret.MYHOST }} >> ~/.ssh/known_hosts
ssh-keyscan ${{ secrets.MY_HOST }} >> ~/.ssh/known_hosts
eval $(ssh-agent -s)
ssh-add -v ~/.ssh/textsender-models_deploy_key
@@ -60,7 +60,7 @@ jobs:
mkdir -p ~/.ssh
echo "${{ secrets.MYREPO_TOKEN }}" > ~/.ssh/textsender-models_deploy_key
chmod 600 ~/.ssh/textsender-models_deploy_key
ssh-keyscan ${{ secret.MYHOST }} >> ~/.ssh/known_hosts
ssh-keyscan ${{ secrets.MY_HOST }} >> ~/.ssh/known_hosts
eval $(ssh-agent -s)
ssh-add -v ~/.ssh/textsender-models_deploy_key
@@ -80,7 +80,7 @@ jobs:
mkdir -p ~/.ssh
echo "${{ secrets.MYREPO_TOKEN }}" > ~/.ssh/textsender-models_deploy_key
chmod 600 ~/.ssh/textsender-models_deploy_key
ssh-keyscan ${{ secret.MYHOST }} >> ~/.ssh/known_hosts
ssh-keyscan ${{ secrets.MY_HOST }} >> ~/.ssh/known_hosts
eval $(ssh-agent -s)
ssh-add -v ~/.ssh/textsender-models_deploy_key
@@ -99,7 +99,7 @@ jobs:
mkdir -p ~/.ssh
echo "${{ secrets.MYREPO_TOKEN }}" > ~/.ssh/textsender-models_deploy_key
chmod 600 ~/.ssh/textsender-models_deploy_key
ssh-keyscan ${{ secret.MYHOST }} >> ~/.ssh/known_hosts
ssh-keyscan ${{ secrets.MY_HOST }} >> ~/.ssh/known_hosts
eval $(ssh-agent -s)
ssh-add -v ~/.ssh/textsender-models_deploy_key
Generated
+10 -3
View File
@@ -57,6 +57,12 @@ version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
name = "base64-ng"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8452b0c98385f31f08318109d6e06a859f89e0c5e38aad6103f095443b063052"
[[package]]
name = "bitflags"
version = "2.12.1"
@@ -1567,8 +1573,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]]
name = "swoosh"
version = "0.2.1"
version = "0.3.0"
dependencies = [
"base64-ng",
"const_format",
"futures",
"http",
@@ -1651,8 +1658,8 @@ dependencies = [
[[package]]
name = "textsender_models"
version = "0.3.0"
source = "git+ssh://git@git.kundeng.us/phoenix/textsender-models.git?tag=v0.3.0-20-15820d5505-111#15820d550526aea3016821c01f508d8d590292cc"
version = "0.2.5"
source = "git+ssh://git@git.kundeng.us/phoenix/textsender-models.git?tag=v0.2.5-v0.3.0-migrate-38dbcb09f7-111#38dbcb09f7c2c572f429ec382c01e14b72b1d615"
dependencies = [
"const_format",
"dotenvy",
+3 -2
View File
@@ -1,6 +1,6 @@
[package]
name = "swoosh"
version = "0.2.1"
version = "0.3.0"
edition = "2024"
rust-version = "1.95"
description = "Library to send text messages"
@@ -15,9 +15,10 @@ reqwest = { version = "0.13.3", features = ["form", "json", "blocking", "multipa
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.3.0-20-15820d5505-111" }
textsender_models = { git = "ssh://git@git.kundeng.us/phoenix/textsender-models.git", tag = "v0.2.5-v0.3.0-migrate-38dbcb09f7-111" }
[dev-dependencies]
tempfile = { version = "3.27.0" }
+55 -105
View File
@@ -1,14 +1,34 @@
// use josekit::jwk::alg::ec::EcCurve::P256;
pub mod types;
pub async fn response_to_json(response: reqwest::Response) -> serde_json::Value {
match response.json().await {
Ok(json_body) => {
json_body
/*
let res = jsonBody.as_str();
match serde_json::from_str(res) {
Ok(converted) => {
converted
}
Err(_err) => {
serde_json::Value::Null
}
}
*/
}
Err(_err) => {
serde_json::Value::Null
}
}
}
pub async fn send_mssage(
msg: &textsender_models::message::Message,
contact: &textsender_models::contact::Contact,
param: crate::twilio::types::Parameters,
config: &textsender_models::config::auxiliary::TwilioConfig,
) -> Result<crate::twilio::api::types::TwilioAPIMessage, std::io::Error> {
if config.account_sid.is_nil() {
) -> Result<reqwest::Response, std::io::Error> {
if config.account_sid.is_empty() {
Err(std::io::Error::other("Account SID is empty"))
} else if config.auth_token.is_empty() {
Err(std::io::Error::other(" Auth token is empty"))
@@ -22,8 +42,8 @@ pub async fn send_mssage(
"application/x-www-form-urlencoded".parse().unwrap(),
);
headers.insert("Accept", "application/json".parse().unwrap());
let auth = generate_auth(&config);
// headers.insert("Authorization", "Basic QUNlZmExZWY1MTYzMTRjOWQxYTY4Y2JkNjU3ZGU0OTI3NzpmNGExZjJiMGI3OWVhMzczNTA3OGMyZDhlZTk2ODRlMQ==".parse()?);
let auth = generate_auth(config).unwrap();
headers.insert("Authorization", auth.parse().unwrap());
let mut params = std::collections::HashMap::new();
params.insert("To", contact.phone_number.as_str());
@@ -40,13 +60,8 @@ pub async fn send_mssage(
// params.insert("PersistentAction", "string");
params.insert("ShortenUrls", "true");
let date = match param.schedule_at {
Some(date_value) => {
let converted = date_value.to_string();
converted
}
None => {
String::new()
}
Some(date_value) => date_value.to_string(),
None => String::new(),
};
if param.schedule && is_scheduleable(&Some(now), &param.schedule_at) {
params.insert("ScheduleType", "fixed");
@@ -59,32 +74,18 @@ pub async fn send_mssage(
params.insert("Body", msg.content.as_str());
let url = format!(
"https://api.twilio.com/2010-04-01/Accounts/ACefa1ef516314c9d1a68cbd657de49277/Messages.json"
"https://api.twilio.com/2010-04-01/Accounts/{}/Messages.json",
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);
match request.send().await {
Ok(response) => {
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))
}
}
}
Ok(response) => match response.status() {
reqwest::StatusCode::OK => Ok(response),
status => Err(std::io::Error::other(format!(
"Unaccounted status: {status:?}"
))),
},
Err(err) => Err(std::io::Error::other(err.to_string())),
}
}
@@ -93,19 +94,17 @@ pub async fn send_mssage(
}
}
fn generate_auth(
config: &textsender_models::config::auxiliary::TwilioConfig,
) -> Result<String, base64_ng::EncodeError> {
let input = format!("{}:{}", config.account_sid, config.auth_token);
match base64_ng::encode(input.as_bytes()) {
Ok(encoded) => 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();
params.insert("To", "16303831708");
params.insert("StatusCallback", "http://OjQozHznkhNTTR.vpnrM1zdXFuiQ");
params.insert("MaxPrice", "1");
params.insert("ProvideFeedback", "true");
@@ -118,78 +117,29 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
params.insert("PersistentAction", "string");
params.insert("PersistentAction", "string");
params.insert("ShortenUrls", "true");
params.insert("ScheduleType", "fixed");
params.insert("SendAt", "2026-06-03T19:50:00.0Z");
params.insert("SendAsMms", "true");
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 body = response.text().await?;
println!("{}", body);
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(
now: &Option<time::OffsetDateTime>,
scheduled: &Option<time::OffsetDateTime>,
) -> bool {
true
match scheduled {
Some(schedule_at) => {
let early = now
.unwrap()
.checked_add(time::Duration::seconds(300))
.unwrap();
*schedule_at > early
}
None => false,
}
}
+13 -32
View File
@@ -3,20 +3,28 @@ pub struct TwilioAPIMessage {
pub account_sid: String,
pub api_version: String,
pub body: String,
// pub date_created: time::OffsetDateTime,
#[serde(with = "time::serde::rfc3339::option")]
pub date_created: Option<time::OffsetDateTime>,
#[serde(with = "time::serde::rfc3339::option")]
pub date_sent: Option<time::OffsetDateTime>,
#[serde(with = "time::serde::rfc3339::option")]
pub date_updated: Option<time::OffsetDateTime>,
pub direction: String,
// Not definite of what type this is
pub error_code: Option<String>,
#[serde(skip_serializing_if = "String::is_empty")]
pub error_code: String,
// Not definite of what type this is
pub error_message: Option<String>,
#[serde(skip_serializing_if = "String::is_empty")]
pub error_message: String,
pub from: String,
pub messaging_service_sid: String,
pub num_media: String,
pub num_segments: String,
pub price: Option<String>,
pub price_unit: Option<String>,
#[serde(skip_serializing_if = "String::is_empty")]
pub price: String,
#[serde(skip_serializing_if = "String::is_empty")]
pub price_unit: String,
#[serde(skip_serializing_if = "String::is_empty")]
pub sid: String,
pub status: String,
pub subresource_uris: SubresourceURI,
@@ -28,30 +36,3 @@ pub struct TwilioAPIMessage {
pub struct SubresourceURI {
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"
}
*/
+1 -5
View File
@@ -1,8 +1,4 @@
// 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)
/// Parameters - Controls flow of sending message
pub struct Parameters {
pub schedule: bool,
pub schedule_at: Option<time::OffsetDateTime>,