11 Commits
Author SHA1 Message Date
phoenix ff79d38305 Adding license (#11)
sender Build / Check (push) Successful in 1m35s
sender Build / Rustfmt (push) Successful in 57s
sender Build / Test Suite (push) Successful in 2m39s
sender Build / Clippy (push) Successful in 3m2s
sender Build / build (push) Successful in 4m55s
Reviewed-on: #11
2026-07-14 11:33:30 -04:00
phoenix a42b5aa460 Dependency name change (#10)
sender Build / Check (push) Successful in 56s
sender Build / Test Suite (push) Successful in 1m0s
sender Build / Rustfmt (push) Successful in 1m4s
sender Build / Clippy (push) Successful in 1m37s
sender Build / build (push) Successful in 2m31s
Reviewed-on: #10
2026-07-13 15:44:07 -04:00
phoenix ea31581464 Update rust (#9)
sender pr / Rustfmt (pull_request) Successful in 45s
sender pr / Clippy (pull_request) Successful in 1m13s
sender pr / Check (pull_request) Successful in 2m34s
sender Build / Check (push) Successful in 1m24s
sender Build / build (push) Successful in 4m18s
sender Build / Rustfmt (push) Successful in 48s
sender Build / Test Suite (push) Successful in 1m20s
sender Build / Clippy (push) Successful in 1m53s
Reviewed-on: #9
2026-07-10 17:21:37 -04:00
phoenix c62fddf44e Update rust (#8)
sender pr / Check (pull_request) Failing after 13s
sender pr / Rustfmt (pull_request) Successful in 1m10s
sender pr / Clippy (pull_request) Successful in 3m42s
sender Build / Check (push) Successful in 1m39s
sender Build / Rustfmt (push) Successful in 1m19s
sender Build / Test Suite (push) Successful in 2m3s
sender Build / Clippy (push) Successful in 2m10s
sender Build / build (push) Successful in 2m53s
Reviewed-on: #8
2026-07-06 23:15:25 -04:00
phoenix b5a01cbdc2 Update (#7)
sender Build / build (push) Successful in 1m40s
sender Build / Check (push) Successful in 1m32s
sender Build / Rustfmt (push) Successful in 52s
sender Build / Clippy (push) Successful in 2m1s
sender Build / Test Suite (push) Successful in 2m11s
sender pr / Check (pull_request) Successful in 1m6s
sender pr / Rustfmt (pull_request) Successful in 30s
sender pr / Clippy (pull_request) Successful in 1m35s
Reviewed-on: #7
2026-06-14 18:30:25 -04:00
phoenix 416ff1ef89 Update dependency name (#6)
sender Build / Check (push) Successful in 1m4s
sender Build / Rustfmt (push) Successful in 47s
sender Build / Clippy (push) Successful in 1m19s
sender Build / build (push) Successful in 1m34s
sender Build / Test Suite (push) Successful in 2m42s
Reviewed-on: #6
2026-06-13 13:21:10 -04:00
phoenix 6396bc861b Update dependencies (#5)
sender Build / Check (push) Successful in 1m27s
sender Build / Rustfmt (push) Successful in 36s
sender Build / Test Suite (push) Successful in 1m15s
sender Build / Clippy (push) Successful in 42s
sender Build / build (push) Successful in 1m40s
sender Build / Check (pull_request) Successful in 1m3s
sender Build / Rustfmt (pull_request) Successful in 54s
sender Build / Test Suite (pull_request) Successful in 1m43s
sender Build / Clippy (pull_request) Successful in 1m37s
sender Build / build (pull_request) Successful in 3m11s
Reviewed-on: #5
2026-06-07 16:02:46 -04:00
phoenix 2ba8d1fd86 Merge pull request 'v0.1.0' (#1) from v0.1.x into main
Reviewed-on: #1
2026-06-06 17:52:42 -04:00
phoenix c32090881d Migrate (#4)
sender Build / Check (push) Successful in 27s
sender Build / Test Suite (push) Successful in 32s
sender Build / Rustfmt (push) Successful in 36s
sender Build / Clippy (push) Failing after 37s
sender Build / build (push) Successful in 35s
Reviewed-on: #4
2026-06-06 17:47:45 -04:00
phoenix b45cd58f6b Init (#3)
sender Build / Check (push) Successful in 40s
sender Build / Rustfmt (push) Successful in 27s
sender Build / Clippy (push) Successful in 32s
sender Build / build (push) Successful in 29s
sender Build / Test Suite (push) Successful in 1m55s
Reviewed-on: #3
2026-06-06 14:43:55 -04:00
phoenix c7484ea885 Clear out (#2)
Reviewed-on: #2
2026-06-06 14:26:17 -04:00
25 changed files with 3304 additions and 418 deletions
+4
View File
@@ -0,0 +1,4 @@
AUTH_SID=n9c43yr3f2c4743f7y4329dnq238f7
AUTH_TOKEN=9nmc4820qf4cor838r4fy3o84rfy348fyr
PHONE_NUMBER="+1234567890"
SERVICE_SID=n9f4dw4y327453947dhny23q7hy4379y5
-45
View File
@@ -1,45 +0,0 @@
name: Go
on:
push:
branches: [ main, master ]
pull_request:
branches: [ main, master ]
jobs:
build:
runs-on: ubuntu-24.04 # You can change this to macos-latest or windows-latest if needed
steps:
- uses: actions/checkout@v5
- name: Set up Go
uses: actions/setup-go@v6
with:
go-version: '1.26.2' # You can specify a specific version or 'stable'
- name: Build
run: |
echo "Initializing config"
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
go env -w GOPRIVATE='${{ secrets.GIT_HOST_ROOT }}'
echo "Creating local .gitconfig"
touch ~/.gitconfig
cat > ~/.gitconfig << "EOF"
[url "ssh://git@${{ secrets.GIT_HOST_ROOT }}"]
insteadOf = https://${{ secrets.GIT_HOST_ROOT }}
EOF
echo "Building binary"
make build
- name: Test
run: go test -v ./...
+65
View File
@@ -0,0 +1,65 @@
name: sender pr
on:
pull_request:
branches:
- main
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/deploy_key
chmod 600 ~/.ssh/deploy_key
ssh-keyscan ${{ secrets.MY_HOST }} >> ~/.ssh/known_hosts
eval $(ssh-agent -s)
ssh-add -v ~/.ssh/deploy_key
cargo check
fmt:
name: Rustfmt
runs-on: ubuntu-24.04
needs: setup_ssh
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/deploy_key
chmod 600 ~/.ssh/deploy_key
ssh-keyscan ${{ secrets.MY_HOST }} >> ~/.ssh/known_hosts
eval $(ssh-agent -s)
ssh-add -v ~/.ssh/deploy_key
cargo fmt --all -- --check
clippy:
name: Clippy
runs-on: ubuntu-24.04
needs: setup_ssh
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/deploy_key
chmod 600 ~/.ssh/deploy_key
ssh-keyscan ${{ secrets.MY_HOST }} >> ~/.ssh/known_hosts
eval $(ssh-agent -s)
ssh-add -v ~/.ssh/deploy_key
cargo clippy -- -D warnings
+110
View File
@@ -0,0 +1,110 @@
name: sender Build
on:
push:
branches:
- main
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/deploy_key
chmod 600 ~/.ssh/deploy_key
ssh-keyscan ${{ secrets.MY_HOST }} >> ~/.ssh/known_hosts
eval $(ssh-agent -s)
ssh-add -v ~/.ssh/deploy_key
cargo check
test:
name: Test Suite
runs-on: ubuntu-24.04
needs: setup_ssh
steps:
- uses: actions/checkout@v6
- uses: actions-rust-lang/setup-rust-toolchain@v1
with:
toolchain: 1.97
- uses: Swatinem/rust-cache@v2
env:
AUTH_SID: 'n9c43yr3f2c4743f7y4329dnq238f7'
AUTH_TOKEN: '9nmc4820qf4cor838r4fy3o84rfy348fyr'
PHONE_NUMBER: '+1234567890'
SERVICE_SID: 'n9f4dw4y327453947dhny23q7hy4379y5'
- run: |
mkdir -p ~/.ssh
echo "${{ secrets.MYREPO_TOKEN }}" > ~/.ssh/deploy_key
chmod 600 ~/.ssh/deploy_key
ssh-keyscan ${{ secrets.MY_HOST }} >> ~/.ssh/known_hosts
eval $(ssh-agent -s)
ssh-add -v ~/.ssh/deploy_key
cp .env.sample .env
cargo test
fmt:
name: Rustfmt
runs-on: ubuntu-24.04
needs: setup_ssh
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/deploy_key
chmod 600 ~/.ssh/deploy_key
ssh-keyscan ${{ secrets.MY_HOST }} >> ~/.ssh/known_hosts
eval $(ssh-agent -s)
ssh-add -v ~/.ssh/deploy_key
cargo fmt --all -- --check
clippy:
name: Clippy
runs-on: ubuntu-24.04
needs: setup_ssh
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/deploy_key
chmod 600 ~/.ssh/deploy_key
ssh-keyscan ${{ secrets.MY_HOST }} >> ~/.ssh/known_hosts
eval $(ssh-agent -s)
ssh-add -v ~/.ssh/deploy_key
cargo clippy -- -D warnings
build:
name: build
runs-on: ubuntu-24.04
needs: setup_ssh
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/deploy_key
chmod 600 ~/.ssh/deploy_key
ssh-keyscan ${{ secrets.MY_HOST }} >> ~/.ssh/known_hosts
eval $(ssh-agent -s)
ssh-add -v ~/.ssh/deploy_key
cargo build --release
+2 -5
View File
@@ -1,5 +1,2 @@
/sender
/messages
/numbers
/config.json
/vendor
.env
target/
Generated
+2836
View File
File diff suppressed because it is too large Load Diff
+15
View File
@@ -0,0 +1,15 @@
[package]
name = "sender"
version = "0.1.6"
edition = "2024"
license = "MIT"
description = "CLI tool to send text messages"
rust-version = "1.97"
[dependencies]
serde_json = { version = "1.0.150" }
tokio = { version = "1.52.3", features = ["full"] }
schedtxt_models = { git = "ssh://git@git.kundeng.us/phoenix/schedtxt_models.git", tag = "v0.5.3", features = ["config", "contact", "envy", "message"] }
swoosh = { git = "ssh://git@git.kundeng.us/phoenix/swoosh.git", tag = "v0.5.3" }
[dev-dependencies]
+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.
-21
View File
@@ -1,21 +0,0 @@
VERSION ?= $(shell git describe --tags 2>/dev/null || echo "dev")
COMMIT ?= $(shell git rev-parse --short HEAD)
BUILD_TIME ?= $(shell date -u +%Y-%m-%dT%H:%M:%SZ)
GO_VERSION ?= $(shell go version | awk '{print $$3}')
.PHONY: build
build:
go build -ldflags="\
-X 'git.kundeng.us/phoenix/sender/internal/version.Version=$(VERSION)' \
-X 'git.kundeng.us/phoenix/sender/internal/version.BuildTime=$(BUILD_TIME)' \
-X 'git.kundeng.us/phoenix/sender/internal/version.Commit=$(COMMIT)' \
-X 'git.kundeng.us/phoenix/sender/internal/version.GoVersion=$(GO_VERSION)'" \
-o sender cmd/sender/main.go
.PHONY: install
install:
go install -ldflags="\
-X 'git.kundeng.us/phoenix/sender/internal/version.Version=$(VERSION)' \
-X 'git.kundeng.us/phoenix/sender/internal/version.BuildTime=$(BUILD_TIME)' \
-X 'git.kundeng.us/phoenix/sender/internal/version.Commit=$(COMMIT)' \
-X 'git.kundeng.us/phoenix/sender/internal/version.GoVersion=$(GO_VERSION)'"
+1 -11
View File
@@ -1,12 +1,2 @@
# sender
sender is a cli program to send text messages.
## Install twilio
```
make build
```
## Using
```
./sender config_path.json numbers_path.json message_path.json
```
A CLI tool that can send text messages.
-62
View File
@@ -1,62 +0,0 @@
package main
import (
"fmt"
"log"
"os"
"git.kundeng.us/phoenix/sender/internal/config"
"git.kundeng.us/phoenix/sender/internal/messaging"
"git.kundeng.us/phoenix/sender/internal/util"
"git.kundeng.us/phoenix/sender/internal/version"
)
func main() {
myArgs := os.Args
argCount := len(myArgs)
if config.IsVersionFlagPresent() {
fmt.Println(config.App_Name)
fmt.Println(version.String())
return
} else if argCount < 4 {
log.Printf("Argument count: %v\n", argCount)
log.Fatal("Provide argument to config file and number file")
}
configPath, numberPath, messagePath := util.GetPaths(myArgs)
log.Printf("Config file path: %s\n", configPath)
log.Println("Numbers file path:", numberPath)
log.Println("Message path:", messagePath)
cfg, _ := config.ParseConfig(configPath)
cfg.PrintConfig()
numbers, err := util.ParseNumbers(numberPath)
if err != nil {
log.Println("Error:", err)
return
}
prepMessage, err := util.ParseMessage(messagePath)
if err != nil {
log.Println("Error:", err)
return
}
if len(numbers) == 0 || prepMessage.Content == "" {
if len(numbers) == 0 {
log.Println("No numbers to send")
os.Exit(-1)
} else {
log.Println("No message to send")
os.Exit(-1)
}
}
fmt.Println("")
log.Println("Message:", prepMessage.Content)
messaging.SendMessages(cfg, numbers, *prepMessage, 5)
}
-6
View File
@@ -1,6 +0,0 @@
{
"auth_sid": "somethingspecial243324",
"service_sid": "FEWJ93982N3FD9JV3F8",
"auth_token": "sdfdsmfoi3299823je98d29jd",
"phone_number": "+10123456789"
}
-16
View File
@@ -1,16 +0,0 @@
module git.kundeng.us/phoenix/sender
go 1.26.2
require (
git.kundeng.us/phoenix/swoosh v0.2.1
git.kundeng.us/phoenix/textsender-models v0.2.1
)
require (
github.com/golang-jwt/jwt/v5 v5.3.1 // 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
github.com/twilio/twilio-go v1.30.5 // indirect
)
-47
View File
@@ -1,47 +0,0 @@
git.kundeng.us/phoenix/swoosh v0.2.1 h1:mJ/zXBOqzi0YTrSm5kxvahzp2WH7lx4R3iIO0s9VFFI=
git.kundeng.us/phoenix/swoosh v0.2.1/go.mod h1:81XNzmTmDPuDPmXMUmKTLErNSKKBM+hsW5U60Lm78ww=
git.kundeng.us/phoenix/textsender-models v0.2.1 h1:21br4NF58aUFuCx8laKxC5RvZMl4GsSIaMX4bvf5plw=
git.kundeng.us/phoenix/textsender-models v0.2.1/go.mod h1:nu5QWy9o+spx/t9NFipaGmF5qiBJS/0QhxyCjoi3Z3E=
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.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY=
github.com/golang-jwt/jwt/v5 v5.3.1/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/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/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/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.30.5 h1:hi6+2kMte29zrFBsw7VSSNtbF30GPMGz/4LRIUgXng8=
github.com/twilio/twilio-go v1.30.5/go.mod h1:QbitvbvtkV77Jn4BABAKVmxabYSjMyQG4tHey9gfPqg=
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/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-46
View File
@@ -1,46 +0,0 @@
package config
import (
"encoding/json"
"flag"
"log"
"os"
axlry "git.kundeng.us/phoenix/textsender-models/tx0/config/auxiliary"
)
const (
App_Name = "sender"
)
func IsVersionFlagPresent() bool {
versionFlag := flag.Bool("version", false, "Print version information")
flag.Parse()
if *versionFlag {
return true
} else {
return false
}
}
func ParseConfig(filepath string) (config axlry.TwilioConfig, success bool) {
content, err := os.ReadFile(filepath)
log.Println("Reading config file...")
log.Println(filepath)
if err != nil {
log.Println("An error reading the file")
return
}
err = json.Unmarshal(content, &config)
if err != nil {
log.Println("An error occurred")
return config, false
}
return config, true
}
-60
View File
@@ -1,60 +0,0 @@
package messaging
import (
"encoding/json"
"fmt"
"log"
"time"
"git.kundeng.us/phoenix/swoosh/swoop/send"
cnfg "git.kundeng.us/phoenix/textsender-models/tx0/config/auxiliary"
"git.kundeng.us/phoenix/textsender-models/tx0/contact"
msg "git.kundeng.us/phoenix/textsender-models/tx0/message"
)
func SendMessages(config cnfg.TwilioConfig, numbers [](contact.Contact), message msg.Message, secondInterval int) {
log.Println("Sending message")
longSleepLimit := 5
count := 0
var snr send.MessageSender
snr.Config = &config
// 2026-04-24 12:00 ET
// 2026-04-25T16:00:00.000Z
// 2026-04-25T16:00:00.000Z
// inputSendTime := "2026-04-25T16:00:00.000Z"
// inputSendTime = "2026-04-25T15:12:00.000Z"
// inputSendTime := "2026-04-29T16:18:00.000Z"
inputSendTime := "2026-04-30T00:00:00.000Z"
sentTime, err := time.Parse(time.RFC3339, inputSendTime)
if err != nil {
fmt.Println("Error:", err)
return
}
for _, number := range numbers {
numberToSend := number
log.Println("Sending to: " + numberToSend.PhoneNumber)
if resp, _, err := snr.Send(message, number, &sentTime); err != nil {
log.Println("Error sending:", err)
} else {
if response, err := json.Marshal(*resp); err != nil {
log.Println("Error parsing:", err)
} else {
log.Println("Response:", string(response))
}
}
log.Println("Sleeping")
if count%longSleepLimit == 0 {
// sleep for X seconds
tme := 1 * time.Second
time.Sleep(tme)
} else {
time.Sleep(time.Second)
}
}
}
-57
View File
@@ -1,57 +0,0 @@
package util
import (
"encoding/json"
"log"
"os"
"git.kundeng.us/phoenix/textsender-models/tx0/contact"
"git.kundeng.us/phoenix/textsender-models/tx0/message"
)
func GetPaths(args []string) (configPath string, numberPath string, messagePath string) {
configPath = args[1]
numberPath = args[2]
messagePath = args[3]
return
}
func ParseNumbers(filepath string) (numbers [](contact.Contact), err error) {
var nmbrs []contact.Contact
content, err := os.ReadFile(filepath)
if err != nil {
log.Println("An error occurred")
return numbers, err
} else {
if err = json.Unmarshal(content, &nmbrs); err != nil {
log.Println("An error occurred:", err)
return numbers, err
} else {
return nmbrs, nil
}
}
}
func ParseMessage(filepath string) (*message.Message, error) {
var msg message.Message
content, err := os.ReadFile(filepath)
if err != nil {
log.Println("An error occurred")
return nil, err
}
err = json.Unmarshal(content, &msg)
if err != nil {
log.Println("An error occurred")
return nil, err
}
return &msg, nil
}
-17
View File
@@ -1,17 +0,0 @@
package version
import "fmt"
var (
Version = "dev"
BuildTime = "unknown"
Commit = "unknown"
GoVersion = "unknown"
)
func String() string {
return fmt.Sprintf(
"Version: %s\nBuild Date: %s\nCommit: %s\nGo Version: %s",
Version, BuildTime, Commit, GoVersion,
)
}
-24
View File
@@ -1,24 +0,0 @@
#!/bin/bash
root_dir=`pwd`
echo "Root directory: $root_dir"
if [[ ! -d "$root_dir" ]]; then
echo "Root directory does not exist. Exiting"
exit
fi
config_path="config.json"
numbers_part="1"
numbers_path="numbers/numbers-notify-$numbers_part.json"
type_message="json"
messages_path="messages/message-notify-$numbers_part.json"
send_message="True"
echo "Changing directory to $root_dir"
cd $root_dir
./sender "$config_path" "$numbers_path" "$messages_path"
echo "Done"
+68
View File
@@ -0,0 +1,68 @@
pub const APP_NAME: &str = "sender";
/// Returns numbers and message paths
pub fn get_paths(args: Vec<String>) -> Result<(String, String), std::io::Error> {
if args.len() < 2 {
Err(std::io::Error::other("Not enough arguments"))
} else {
let numbers_path = args[1].clone();
let message_path = args[2].clone();
if !path_exists(&numbers_path) && !path_exists(&message_path) {
Err(std::io::Error::other("Paths do not exists"))
} else {
Ok((numbers_path, message_path))
}
}
}
fn path_exists(path: &str) -> bool {
let file = std::path::Path::new(path);
file.exists()
}
pub fn parse_config() -> (schedtxt_models::config::auxiliary::TwilioConfig, bool) {
let mut config = schedtxt_models::config::auxiliary::TwilioConfig {
..Default::default()
};
let auth_sid_var = schedtxt_models::envy::environment::get_env("AUTH_SID");
let auth_token_var = schedtxt_models::envy::environment::get_env("AUTH_TOKEN");
let phone_number_var = schedtxt_models::envy::environment::get_env("PHONE_NUMBER");
let service_sid_var = schedtxt_models::envy::environment::get_env("SERVICE_SID");
config.account_sid = auth_sid_var.value;
config.auth_token = auth_token_var.value;
config.phone_number = phone_number_var.value;
config.service_sid = service_sid_var.value;
let success = {
!config.account_sid.is_empty()
&& !config.auth_token.is_empty()
&& !config.phone_number.is_empty()
&& !config.service_sid.is_empty()
};
(config, success)
}
#[cfg(test)]
mod tests {
#[test]
fn test_parse_config() {
let (config, success) = super::parse_config();
assert!(success, "Config was not parsed");
assert_eq!(
false,
config.account_sid.is_empty(),
"Account SID is missing"
);
assert_eq!(false, config.auth_token.is_empty(), "AUTH TOKEN is missing");
assert_eq!(
false,
config.phone_number.is_empty(),
"Phone Number is missing"
);
assert_eq!(
false,
config.service_sid.is_empty(),
"Service SID is missing"
);
}
}
+47
View File
@@ -0,0 +1,47 @@
pub mod config;
pub mod messaging;
pub mod version;
#[tokio::main]
async fn main() {
println!("sender");
let args: Vec<String> = std::env::args().collect();
if version::contains_version(&args) {
version::print_version();
std::process::exit(-1);
}
match config::get_paths(args) {
Ok((n_path, m_path)) => {
println!("Message path: {m_path:?}");
println!("Phone Number path: {n_path:?}");
let (config, success) = config::parse_config();
if !success {
eprintln!("Error parsing config");
} else {
let contacts = match messaging::parse_numbers(&n_path) {
Ok(contacts) => contacts,
Err(err) => {
eprintln!("Error: {err:?}");
std::process::exit(-1);
}
};
let message = match messaging::parse_message(&m_path) {
Ok(message) => message,
Err(err) => {
eprintln!("Error: {err:?}");
std::process::exit(-1);
}
};
println!("Message: {:?}", message.content);
messaging::send_messages(&config, &contacts, &message, 5).await;
}
}
Err(err) => {
eprintln!("Error: {err:?}");
}
};
}
+105
View File
@@ -0,0 +1,105 @@
use schedtxt_models::config::auxiliary::TwilioConfig;
use schedtxt_models::contact::Contact;
use schedtxt_models::message::Message;
pub async fn send_messages(
config: &TwilioConfig,
contacts: &[Contact],
message: &Message,
second_interval: u64,
) {
let long_sleep_limit: i64 = 5;
for (text_count, contact) in (0_i64..).zip(contacts.iter()) {
println!("Sending to: {:?}", contact.phone_number);
let param = swoosh::twilio::types::Parameters {
schedule: false,
schedule_at: None,
};
let mut sendmsg = swoosh::SendMsg::default();
sendmsg.load_config(
&config.auth_token,
&config.phone_number,
&config.service_sid,
&config.account_sid,
);
sendmsg.load_recipient(&contact.phone_number);
sendmsg.load_message(&message.content);
match sendmsg.send_message(&param).await {
Ok(response) => {
println!("Raw response: {response:?}");
let val = swoosh::twilio::api::response_to_json(response).await;
println!("Response: {val:?}");
}
Err(err) => {
eprintln!("Error: {err:?}");
}
}
if text_count % long_sleep_limit == 0 {
// Sleep for X seconds
let wait_time = second_interval;
std::thread::sleep(std::time::Duration::from_secs(wait_time));
} else {
// Sleep for a second
std::thread::sleep(std::time::Duration::from_secs(1));
}
}
}
pub fn parse_numbers(filepath: &str) -> Result<Vec<Contact>, std::io::Error> {
match std::fs::File::open(filepath) {
Ok(file) => {
let reader = std::io::BufReader::new(file);
let contacts: Vec<Contact> = match serde_json::from_reader(reader) {
Ok(va) => va,
Err(err) => {
return Err(std::io::Error::other(err.to_string()));
}
};
Ok(contacts)
}
Err(err) => Err(err),
}
}
pub fn parse_message(filepath: &str) -> Result<Message, std::io::Error> {
match std::fs::File::open(filepath) {
Ok(file) => {
let reader = std::io::BufReader::new(file);
let message: Message = serde_json::from_reader(reader)?;
Ok(message)
}
Err(err) => Err(err),
}
}
#[cfg(test)]
mod tests {
#[test]
fn test_parse_message() {
let message_path = String::from("tests/message-0.json");
match super::parse_message(&message_path) {
Ok(message) => {
assert_eq!(false, message.content.is_empty(), "Content is empty");
}
Err(err) => {
assert!(false, "Error: {err:?}");
}
};
}
#[test]
fn test_parse_numbers() {
let numbers_path = String::from("tests/numbers-0.json");
match super::parse_numbers(&numbers_path) {
Ok(contacts) => {
assert_eq!(false, contacts.is_empty(), "Should not be empty");
}
Err(err) => {
assert!(false, "Error: {err:?}");
}
};
}
}
+20
View File
@@ -0,0 +1,20 @@
pub fn print_version() {
let name = env!("CARGO_PKG_NAME");
let version = env!("CARGO_PKG_VERSION");
println!("{name:?} {version:?}");
}
pub fn contains_version(args: &Vec<String>) -> bool {
let valid_flags = vec!["-v", "--version"];
for arg in args {
for valid_flag in &valid_flags {
if valid_flag == arg {
return true;
}
}
}
false
}
+3
View File
@@ -0,0 +1,3 @@
{
"content": "What is life?"
}
+5
View File
@@ -0,0 +1,5 @@
[
{
"phone_number": "+10123456789"
}
]