Compare commits

..
Author SHA1 Message Date
phoenix e4b6b191f0 Added scheduling check 2025-11-29 13:46:14 -05:00
16 changed files with 189 additions and 2939 deletions
@@ -1,36 +1,35 @@
name: Release Tagging
name: Go Release
on:
push:
branches:
- v0.3.x
- main
pull_request:
branches:
- v0.3.x
- main
jobs:
release:
build-and-release:
runs-on: ubuntu-24.04
steps:
- name: Checkout code
uses: actions/checkout@v5
with:
fetch-depth: 0 # Important for git describe --tags
- name: Install Rust
uses: actions-rs/toolchain@v1
- name: Set up Go
uses: actions/setup-go@v6
with:
toolchain: 1.95
components: cargo
go-version: '1.25.4'
- name: Extract Version from Cargo.toml
- name: Create version
id: version
run: |
VERSION=$(grep '^version = "' Cargo.toml | sed -E 's/version = "([^"]+)"/\1/')
echo "Creating version"
VERSION="0.0.4"
PROJECT_COMMIT_HASH=$(git rev-parse HEAD | cut -c 1-10)
BRANCH_REF="${{ gitea.ref }}"
BRANCH_NAME=$(echo "$BRANCH_REF" | cut -d '/' -f 3)
PROJECT_TAG_RELEASE="v$VERSION-$BRANCH_NAME-$PROJECT_COMMIT_HASH-871"
PROJECT_TAG_RELEASE="v$VERSION-$BRANCH_NAME-$PROJECT_COMMIT_HASH-556"
echo "::set-output name=project_tag_release::$PROJECT_TAG_RELEASE"
+36
View File
@@ -0,0 +1,36 @@
name: Go CI
on: [push, pull_request] # Triggers on push or pull request events
jobs:
test:
name: Test
runs-on: ubuntu-24.04
steps:
- name: Check out repository code
uses: actions/checkout@v5
- name: Set up Go
uses: actions/setup-go@v6
with:
go-version: '1.25.4'
- name: Configure Git for SSH
run: |
git config --global url."git@${{ secrets.GIT_HOST_ROOT }}:".insteadOf "https://${{ secrets.GIT_HOST_ROOT }}/"
go env -w GOPRIVATE=${{ secrets.GIT_HOST_ROOT }}/phoenix/*
- name: Download dependencies
run: |
echo "Initializing config"
mkdir -p ~/.ssh
echo "${{ secrets.MYREPO_TOKEN }}" > ~/.ssh/swoosh_deploy_key
chmod 600 ~/.ssh/swoosh_deploy_key
ssh-keyscan ${{ secrets.MY_HOST }} >> ~/.ssh/known_hosts
eval $(ssh-agent -s)
ssh-add -v ~/.ssh/swoosh_deploy_key
go mod download
- name: Run tests
run: go test -v ./... # Runs all tests in the project
-108
View File
@@ -1,108 +0,0 @@
name: swoosh Build
on:
push:
branches:
- v0.3.x
pull_request:
branches:
- v0.3.x
jobs:
check:
name: Check
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v5
- uses: actions-rust-lang/setup-rust-toolchain@v1
with:
toolchain: 1.95
- run: |
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
eval $(ssh-agent -s)
ssh-add -v ~/.ssh/textsender-models_deploy_key
cargo check
test:
name: Test Suite
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v5
- uses: actions-rust-lang/setup-rust-toolchain@v1
with:
toolchain: 1.95
- run: |
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
eval $(ssh-agent -s)
ssh-add -v ~/.ssh/textsender-models_deploy_key
cargo test
fmt:
name: Rustfmt
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v5
- uses: actions-rust-lang/setup-rust-toolchain@v1
with:
toolchain: 1.95
- 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 ${{ secret.MYHOST }} >> ~/.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@v5
- uses: actions-rust-lang/setup-rust-toolchain@v1
with:
toolchain: 1.95
- 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 ${{ secret.MYHOST }} >> ~/.ssh/known_hosts
eval $(ssh-agent -s)
ssh-add -v ~/.ssh/textsender-models_deploy_key
cargo clippy -- -D warnings
build:
name: build
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v5
- uses: actions-rust-lang/setup-rust-toolchain@v1
with:
toolchain: 1.95
- run: |
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
eval $(ssh-agent -s)
ssh-add -v ~/.ssh/textsender-models_deploy_key
cargo build
+1 -1
View File
@@ -1 +1 @@
target/
/vendor
Generated
-2530
View File
File diff suppressed because it is too large Load Diff
-23
View File
@@ -1,23 +0,0 @@
[package]
name = "swoosh"
version = "0.2.1"
edition = "2024"
rust-version = "1.95"
description = "Library to send text messages"
[dependencies]
serde = { version = "1.0.228", features = ["derive"] }
serde_json = { version = "1.0.149" }
tokio = { version = "1.52.2", features = ["full"] }
futures = { version = "0.3.32" }
http = { version = "1.4.0" }
reqwest = { version = "0.13.3", features = ["form", "json", "blocking", "multipart", "stream"] }
rand = { version = "0.10.1" }
time = { version = "0.3.47", features = ["formatting", "macros", "parsing", "serde"] }
uuid = { version = "1.23.1", features = ["v4", "serde"] }
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" }
[dev-dependencies]
tempfile = { version = "3.27.0" }
+2
View File
@@ -0,0 +1,2 @@
# Swoosh
`swoosh` is a library used to send text messages
+15
View File
@@ -0,0 +1,15 @@
module git.kundeng.us/phoenix/swoosh
go 1.25.4
require (
git.kundeng.us/phoenix/textsender-models v0.0.10
github.com/twilio/twilio-go v1.28.6
)
require (
github.com/golang-jwt/jwt/v5 v5.3.0 // indirect
github.com/golang/mock v1.6.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
)
+56
View File
@@ -0,0 +1,56 @@
git.kundeng.us/phoenix/textsender-models v0.0.10 h1:0AMe/zm4Xte2BcuqqhRP7+42m+4To+rUnHkR2a9eXlI=
git.kundeng.us/phoenix/textsender-models v0.0.10/go.mod h1:9iPDQJg1Tc6WMNoW5+f8YKmnosMwlWHJ++hmxNLDEe0=
github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo=
github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/localtunnel/go-localtunnel v0.0.0-20170326223115-8a804488f275 h1:IZycmTpoUtQK3PD60UYBwjaCUHUP7cML494ao9/O8+Q=
github.com/localtunnel/go-localtunnel v0.0.0-20170326223115-8a804488f275/go.mod h1:zt6UU74K6Z6oMOYJbJzYpYucqdcQwSMPBEdSvGiaUMw=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/twilio/twilio-go v1.28.6 h1:L/pikXkARmPOzF/gUMeZPP/Lu5mY/wA4XC7sRzS86W4=
github.com/twilio/twilio-go v1.28.6/go.mod h1:FpgNWMoD8CFnmukpKq9RNpUSGXC0BwnbeKZj2YHlIkw=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-1
View File
@@ -1 +0,0 @@
pub mod twilio;
-195
View File
@@ -1,195 +0,0 @@
// use josekit::jwk::alg::ec::EcCurve::P256;
pub mod types;
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() {
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"))
} else {
let now = time::OffsetDateTime::now_utc();
match reqwest::Client::builder().build() {
Ok(client) => {
let mut headers = reqwest::header::HeaderMap::new();
headers.insert(
"Content-Type",
"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 mut params = std::collections::HashMap::new();
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) => {
let converted = date_value.to_string();
converted
}
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!(
"https://api.twilio.com/2010-04-01/Accounts/ACefa1ef516314c9d1a68cbd657de49277/Messages.json"
);
// 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))
}
}
}
Err(err) => Err(std::io::Error::other(err.to_string())),
}
}
Err(err) => Err(std::io::Error::other(err.to_string())),
}
}
}
/*
#[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");
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("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
}
-57
View File
@@ -1,57 +0,0 @@
#[derive(Clone, Debug, Default, serde::Deserialize, serde::Serialize)]
pub struct TwilioAPIMessage {
pub account_sid: String,
pub api_version: String,
pub body: String,
// pub date_created: time::OffsetDateTime,
pub date_sent: Option<time::OffsetDateTime>,
pub date_updated: Option<time::OffsetDateTime>,
pub direction: String,
// Not definite of what type this is
pub error_code: Option<String>,
// Not definite of what type this is
pub error_message: Option<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>,
pub sid: String,
pub status: String,
pub subresource_uris: SubresourceURI,
pub to: String,
pub uri: String,
}
#[derive(Clone, Debug, Default, serde::Deserialize, serde::Serialize)]
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"
}
*/
-2
View File
@@ -1,2 +0,0 @@
pub mod api;
pub mod types;
-9
View File
@@ -1,9 +0,0 @@
// 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 schedule: bool,
pub schedule_at: Option<time::OffsetDateTime>,
}
+60
View File
@@ -0,0 +1,60 @@
package send
import (
"encoding/json"
"fmt"
"time"
"git.kundeng.us/phoenix/textsender-models/tx0/config"
"git.kundeng.us/phoenix/textsender-models/tx0/contact"
"git.kundeng.us/phoenix/textsender-models/tx0/message"
"github.com/twilio/twilio-go"
twilioApi "github.com/twilio/twilio-go/rest/api/v2010"
"git.kundeng.us/phoenix/swoosh/swoop/types"
)
type MessageSender struct {
Config *config.TwiloConfig
}
func (m *MessageSender) Send(msg message.Message, number contact.Contact, sendTime *time.Time) (*types.TwilioResult, error) {
if m.Config == nil {
return 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)
}
if resp, err := client.Api.CreateMessage(params); err != nil {
return nil, fmt.Errorf("Error sending message: %v", err)
} else {
if _, err := json.Marshal(*resp); err != nil {
return nil, fmt.Errorf("Error parsing result: %v", err)
} else {
return resp, nil
}
}
}
func isSchedulable(now *time.Time, scheduled *time.Time) (bool) {
early := now.Add(300 * time.Second)
if scheduled.Before(early) && scheduled.After(now) {
return true
} else {
return false
}
}
+7
View File
@@ -0,0 +1,7 @@
package types
import (
twilioApi "github.com/twilio/twilio-go/rest/api/v2010"
)
type TwilioResult = twilioApi.ApiV2010Message