4 Commits
Author SHA1 Message Date
phoenix f9c098b92f Adding license (#18)
catapult PR / Rustfmt (push) Successful in 41s
catapult PR / Clippy (push) Successful in 59s
catapult PR / Check (push) Successful in 1m44s
Reviewed-on: #18
2026-07-14 11:47:05 -04:00
phoenix 6801b1e635 Dependency name change (#17)
Reviewed-on: #17
2026-07-13 16:33:51 -04:00
phoenix 50aa9089f0 Update rust (#16)
catapult PR / Rustfmt (pull_request) Successful in 48s
catapult PR / Clippy (pull_request) Successful in 1m55s
catapult PR / Check (pull_request) Successful in 2m21s
Reviewed-on: #16
2026-07-10 18:33:46 -04:00
phoenix 8dbfd88aae Refactoring (#15)
catapult PR / Check (pull_request) Successful in 59s
catapult PR / Rustfmt (pull_request) Successful in 1m25s
catapult PR / Clippy (pull_request) Successful in 1m18s
Reviewed-on: #15
2026-07-07 15:51:06 -04:00
17 changed files with 206 additions and 158 deletions
+6 -3
View File
@@ -4,6 +4,9 @@ on:
pull_request:
branches:
- main
push:
branches:
- main
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
@@ -17,7 +20,7 @@ jobs:
- uses: actions/checkout@v6
- uses: actions-rust-lang/setup-rust-toolchain@v1
with:
toolchain: 1.96.1
toolchain: 1.97
- uses: Swatinem/rust-cache@v2
- run: |
mkdir -p ~/.ssh
@@ -36,7 +39,7 @@ jobs:
- uses: actions/checkout@v6
- uses: actions-rust-lang/setup-rust-toolchain@v1
with:
toolchain: 1.96.1
toolchain: 1.97
- run: rustup component add rustfmt
- uses: Swatinem/rust-cache@v2
- run: |
@@ -56,7 +59,7 @@ jobs:
- uses: actions/checkout@v6
- uses: actions-rust-lang/setup-rust-toolchain@v1
with:
toolchain: 1.96.1
toolchain: 1.97
- run: rustup component add clippy
- uses: Swatinem/rust-cache@v2
- run: |
Generated
+51 -51
View File
@@ -81,9 +81,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
name = "base64-ng"
version = "1.3.5"
version = "1.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b87dbd7e45c4885ad3432a2d125ddb155c5ee87046ab23bad043dfe84fedfa2"
checksum = "78dbdaa3c8250efc13282f483f2ae92733e407c9cf6ffc2fe7918ce1ee420724"
[[package]]
name = "bitflags"
@@ -126,18 +126,18 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "bytes"
version = "1.12.0"
version = "1.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ae3f5d315924270530207e2a68396c3cc547f6dca3fbdca317cfb1a51edb593"
checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04"
[[package]]
name = "catapult"
version = "0.3.2"
version = "0.3.5"
dependencies = [
"reqwest",
"schedtxt_models",
"serde_json",
"swoosh",
"textsender_models",
"time",
"tokio",
"uuid",
@@ -145,9 +145,9 @@ dependencies = [
[[package]]
name = "cc"
version = "1.2.66"
version = "1.2.67"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f5d6cac793997bd970000024b2934968efe83b382de4fdcf4fcb46b6ee4ad996"
checksum = "e17dd265a7d0f31ef544e1b20e03add05d3b45b491b633b10d67145d2acc1a38"
dependencies = [
"find-msvc-tools",
"jobserver",
@@ -734,9 +734,9 @@ dependencies = [
[[package]]
name = "http-body"
version = "1.0.1"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184"
checksum = "ca2a8f2913ee65f60facd6a5905613afaa448497a0230cc41ce022d93290bc2c"
dependencies = [
"bytes",
"http",
@@ -744,9 +744,9 @@ dependencies = [
[[package]]
name = "http-body-util"
version = "0.1.3"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a"
checksum = "e9f41fd6a08e4d4ec69df65976da761afd5ad5e58a9d4acb46bd1c953a9e3ff2"
dependencies = [
"bytes",
"futures-core",
@@ -1121,9 +1121,9 @@ dependencies = [
[[package]]
name = "memchr"
version = "2.8.2"
version = "2.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4"
checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
[[package]]
name = "mime"
@@ -1153,9 +1153,9 @@ dependencies = [
[[package]]
name = "mio"
version = "1.2.1"
version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda"
checksum = "30d65c71f1ce40ab09135ce117d742b9f8a19ff91a41a8b57ed50bc2de59c427"
dependencies = [
"libc",
"wasi",
@@ -1423,9 +1423,9 @@ dependencies = [
[[package]]
name = "regex"
version = "1.12.4"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1292b7759ae1cb9ec195452d1390a074f0cd8541ab7a5a8c31cd6db45d4a6ba"
checksum = "2a0e75113e14dc5acb068cd0786884f214f1312650a3d36d269f5c4f3cdee8a2"
dependencies = [
"aho-corasick",
"memchr",
@@ -1435,9 +1435,9 @@ dependencies = [
[[package]]
name = "regex-automata"
version = "0.4.14"
version = "0.4.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f"
checksum = "1f388202e4b80542a0921078cc23b6333bcf1409c1e3f86404cae4766a6131db"
dependencies = [
"aho-corasick",
"memchr",
@@ -1540,9 +1540,9 @@ dependencies = [
[[package]]
name = "rustls"
version = "0.23.41"
version = "0.23.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b92b125634d9b795e7beca796cc790df15a7fb38323bf3196fda83292d06b1f"
checksum = "3c54fcab019b409d04215d3a17cb438fd7fbf192ee61461f20f4fe18704bc138"
dependencies = [
"aws-lc-rs",
"once_cell",
@@ -1643,6 +1643,22 @@ dependencies = [
"windows-sys 0.61.2",
]
[[package]]
name = "schedtxt_models"
version = "0.5.3"
source = "git+ssh://git@git.kundeng.us/phoenix/schedtxt_models.git?tag=v0.5.3#597af4e84c082bb437cc94817bb19592614681a5"
dependencies = [
"const_format",
"dotenvy",
"josekit",
"serde",
"serde_json",
"sqlx",
"time",
"utoipa",
"uuid",
]
[[package]]
name = "scopeguard"
version = "1.2.0"
@@ -1822,9 +1838,9 @@ dependencies = [
[[package]]
name = "socket2"
version = "0.6.4"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51"
checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4"
dependencies = [
"libc",
"windows-sys 0.61.2",
@@ -1832,9 +1848,9 @@ dependencies = [
[[package]]
name = "spin"
version = "0.9.8"
version = "0.9.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
checksum = "3763264f6b73151db08c50ff20d7d8a0b8796e021cdea7ceedad07b80155fa0e"
dependencies = [
"lock_api",
]
@@ -2044,14 +2060,14 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]]
name = "swoosh"
version = "0.5.2"
source = "git+ssh://git@git.kundeng.us/phoenix/swoosh.git?tag=v0.5.2#50e4b209f521d8985d73a33b4a8f1db69334100a"
version = "0.5.4"
source = "git+ssh://git@git.kundeng.us/phoenix/swoosh.git?tag=v0.5.4#84ba4c21ec721662d76bb21ef67062d51f31f719"
dependencies = [
"base64-ng",
"reqwest",
"schedtxt_models",
"serde",
"serde_json",
"textsender_models",
"time",
]
@@ -2120,22 +2136,6 @@ dependencies = [
"windows-sys 0.61.2",
]
[[package]]
name = "textsender_models"
version = "0.5.1"
source = "git+ssh://git@git.kundeng.us/phoenix/textsender_models.git?tag=v0.5.1#151eeaacf64e255bde56a48df03f4c46ec77c235"
dependencies = [
"const_format",
"dotenvy",
"josekit",
"serde",
"serde_json",
"sqlx",
"time",
"utoipa",
"uuid",
]
[[package]]
name = "thiserror"
version = "2.0.18"
@@ -2198,9 +2198,9 @@ dependencies = [
[[package]]
name = "tinyvec"
version = "1.11.0"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3"
checksum = "bb4ebadaa0af04fab11ae01eb5f9fdb5f9c5b875506e210e71c07873528baa7f"
dependencies = [
"tinyvec_macros",
]
@@ -2451,9 +2451,9 @@ dependencies = [
[[package]]
name = "uuid"
version = "1.23.4"
version = "1.23.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf80a72845275afea99e7f2b434723d3bc7e38470fcd1c7ed39a599c73319a53"
checksum = "ea5fab0d6c3c01ae70085a09cb03d4c7a1d6314e2b3e075392783396d724ca0a"
dependencies = [
"getrandom 0.4.3",
"js-sys",
@@ -2818,6 +2818,6 @@ dependencies = [
[[package]]
name = "zmij"
version = "1.0.21"
version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b"
+7 -5
View File
@@ -1,7 +1,9 @@
[package]
name = "catapult"
version = "0.3.2"
rust-version = "1.96.1"
version = "0.3.5"
rust-version = "1.97"
license = "MIT"
description = "Service to carry out text message scheduling"
edition = "2024"
[dependencies]
@@ -9,8 +11,8 @@ tokio = { version = "1.52.3", features = ["full"] }
reqwest = { version = "0.13.4", features = ["json", "stream", "multipart"] }
serde_json = { version = "1.0.150" }
time = { version = "0.3.53", features = ["formatting", "macros", "parsing", "serde"] }
uuid = { version = "1.23.3", features = ["v4", "serde"] }
textsender_models = { git = "ssh://git@git.kundeng.us/phoenix/textsender_models.git", tag = "v0.5.1" }
swoosh = { git = "ssh://git@git.kundeng.us/phoenix/swoosh.git", tag = "v0.5.2" }
uuid = { version = "1.23.5", features = ["v4", "serde"] }
schedtxt_models = { git = "ssh://git@git.kundeng.us/phoenix/schedtxt_models.git", tag = "v0.5.3" }
swoosh = { git = "ssh://git@git.kundeng.us/phoenix/swoosh.git", tag = "v0.5.4" }
[dev-dependencies]
+1 -18
View File
@@ -1,5 +1,4 @@
# Stage 1: Build the application
FROM rust:1.96.1 as builder
FROM rust:1.97 as builder
# Set the working directory inside the container
WORKDIR /usr/src/app
@@ -11,35 +10,23 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
openssh-client git \
&& rm -rf /var/lib/apt/lists/*
# << --- ADD HOST KEY HERE --- >>
# Replace 'yourgithost.com' with the actual hostname (e.g., github.com)
RUN mkdir -p -m 0700 ~/.ssh && \
ssh-keyscan git.kundeng.us >> ~/.ssh/known_hosts
# Copy Cargo manifests
COPY Cargo.toml Cargo.lock ./
# Build *only* dependencies to leverage Docker cache
# This dummy build caches dependencies as a separate layer
RUN --mount=type=ssh mkdir src && \
echo "fn main() {println!(\"if you see this, the build broke\")}" > src/main.rs && \
cargo build --release --quiet && \
rm -rf src target/release/deps/catapult*
# Copy the actual source code
COPY src ./src
# If you have other directories like `templates` or `static`, copy them too
COPY .env ./.env
# << --- SSH MOUNT ADDED HERE --- >>
# Build *only* dependencies to leverage Docker cache
# This dummy build caches dependencies as a separate layer
# Mount the SSH agent socket for this command
RUN --mount=type=ssh \
cargo build --release --quiet
# Stage 2: Create the final, smaller runtime image
# Use a minimal base image like debian-slim or even distroless for security/size
FROM debian:trixie-slim
# Install runtime dependencies if needed (e.g., SSL certificates)
@@ -48,12 +35,8 @@ RUN apt-get update && apt-get install -y ca-certificates libssl-dev libssl3 && r
# Set the working directory
WORKDIR /usr/local/bin
# Copy the compiled binary from the builder stage
# Replace 'catapult' with the actual name of your binary (usually the crate name)
COPY --from=builder /usr/src/app/target/release/catapult .
# Copy other necessary files like .env (if used for runtime config) or static assets
# It's generally better to configure via environment variables in Docker though
COPY --from=builder /usr/src/app/.env .
# Set the command to run your application
+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.
+29
View File
@@ -0,0 +1,29 @@
# catapult
A service that manages scheduling text messages.
## Getting started
Docker isn't required, but it is the quickest way to get started.
Copy over the `.env.docker.sample` to `.env`. From that point, make edits to the
file to get it working.
### Messaging configuration
The service vendor used to send the messages is `twilio`. After signing up and being
provided credentials, update the `TWILIO_*` variables.
### Client-Server interaction
In order for `catapult` to function properly, credentials need to be provided via
`SERVICE_USERNAME` and `SERVICE_PASSPHRASE`. The credentials are created from making
an API call to register a service user in `textsender_auth`. So, you need to have credentials
set in the `.env` file and use those same credentials to create the account in the
API call.
### API sources
The `AUTH_URL` and `API_URL` variables need to be set for the base URL for the
`textsender_auth` and `textsender_api` respectively.
Since this is meant to run along with `textsender_auth` and `textsender_api`, it isn't
meant to be run by itself. So no need to build or run the container in isolation, but
that capability is present.
+2 -2
View File
@@ -4,8 +4,8 @@ services:
songparser:
build:
context: .
ssh: ["default"] # Uses host's SSH agent
ssh: ["default"]
container_name: catapult
env_file:
- .env
restart: unless-stopped # Optional: Restart policy
restart: unless-stopped
+20 -14
View File
@@ -9,24 +9,24 @@ pub struct App {
pub auth_url: String,
pub service_username: String,
pub service_passphrase: String,
pub twilio_config: textsender_models::config::auxiliary::TwilioConfig,
pub twilio_config: schedtxt_models::config::auxiliary::TwilioConfig,
}
pub fn load_app() -> Result<App, std::io::Error> {
use textsender_models::envy;
use schedtxt_models::envy;
let auth_url_env = envy::environment::get_env("AUTH_URL");
let api_url_env = envy::environment::get_env("API_URL");
let service_username_env = envy::environment::get_env("SERVICE_USERNAME");
let service_passphrase_env = envy::environment::get_env("SERVICE_PASSPHRASE");
let envs = vec![
api_url_env,
auth_url_env,
service_username_env,
service_passphrase_env,
&api_url_env,
&auth_url_env,
&service_username_env,
&service_passphrase_env,
];
let check_envs = |vs: &Vec<envy::EnvVar>| -> (bool, Option<String>) {
let check_envs = |vs: &Vec<&envy::EnvVar>| -> (bool, Option<String>) {
for env in vs {
if env.value.is_empty() {
return (false, Some(format!("Key {} is not provided", env.key)));
@@ -36,19 +36,25 @@ pub fn load_app() -> Result<App, std::io::Error> {
};
let (envs_valid, reason) = check_envs(&envs);
if envs_valid {
match textsender_models::config::auxiliary::load_config() {
let auth_url: String = auth_url_env.value;
let api_url: String = api_url_env.value;
let service_username: String = service_username_env.value;
let service_passphrase: String = service_passphrase_env.value;
match schedtxt_models::config::auxiliary::load_config() {
Ok(twilio_config) => Ok(App {
api_url: envs[0].value.clone(),
auth_url: envs[1].value.clone(),
service_username: envs[2].value.clone(),
service_passphrase: envs[3].value.clone(),
api_url,
auth_url,
service_username,
service_passphrase,
twilio_config,
}),
Err(err) => Err(err),
}
} else {
Err(std::io::Error::other(reason.unwrap()))
match reason {
Some(reason) => Err(std::io::Error::other(reason)),
None => Err(std::io::Error::other("No reason found")),
}
}
}
+9 -6
View File
@@ -7,7 +7,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
}
let app = match catapult::app::load_app() {
Ok(app) => app,
Ok(app) => std::sync::Arc::new(app),
Err(err) => {
eprintln!("Error: {err:?}");
std::process::exit(-1);
@@ -16,9 +16,11 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("App: {app:?}");
let auth = catapult::service::auth::Auth { app: app.clone() };
let auth = catapult::service::auth::Auth {
app: std::sync::Arc::clone(&app),
};
let token = match auth.get_token().await {
Ok(token) => token,
Ok(token) => std::sync::Arc::new(token),
Err(err) => {
eprintln!("Error getting token");
eprintln!("Error: {err:?}");
@@ -27,8 +29,8 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
};
let mut svc = catapult::service::core::Service {
app: app.clone(),
token,
app: std::sync::Arc::clone(&app),
token: std::sync::Arc::clone(&token),
};
loop {
@@ -36,7 +38,8 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
match auth.get_refresh_token(&svc.token).await {
Ok(refresh) => {
println!("Refresh token: {refresh:?}");
svc.token = refresh;
let refreshed = std::sync::Arc::new(refresh);
svc.token = refreshed;
}
Err(err) => {
eprintln!("Error: {err:?}");
+7 -7
View File
@@ -1,9 +1,9 @@
pub struct Auth {
pub app: crate::app::App,
pub app: std::sync::Arc<crate::app::App>,
}
impl Auth {
pub async fn get_token(&self) -> Result<textsender_models::token::Login, std::io::Error> {
pub async fn get_token(&self) -> Result<schedtxt_models::token::Login, std::io::Error> {
let client = reqwest::Client::new();
let endpoint = String::from("api/v1/service/login");
let api_url = format!("{}/{endpoint}", self.app.auth_url);
@@ -37,8 +37,8 @@ impl Auth {
pub async fn get_refresh_token(
&self,
login_result: &textsender_models::token::Login,
) -> Result<textsender_models::token::Login, std::io::Error> {
login_result: &schedtxt_models::token::Login,
) -> Result<schedtxt_models::token::Login, std::io::Error> {
let client = reqwest::Client::new();
let endpoint = String::from("api/v1/token/refresh");
let api_url = format!("{}/{endpoint}", self.app.auth_url);
@@ -70,7 +70,7 @@ impl Auth {
async fn parse_token_response(
response: &str,
) -> Result<textsender_models::token::LoginResult, std::io::Error> {
) -> Result<schedtxt_models::token::LoginResult, std::io::Error> {
match serde_json::from_str::<serde_json::Value>(response) {
Ok(j) => {
println!("Good");
@@ -85,7 +85,7 @@ async fn parse_token_response(
Err(std::io::Error::other("Error response is empty"))
} else {
let lr = lrs[0].clone();
let ll: textsender_models::token::LoginResult =
let ll: schedtxt_models::token::LoginResult =
match serde_json::from_value(lr) {
Ok(lr) => lr,
Err(err) => {
@@ -102,7 +102,7 @@ async fn parse_token_response(
}
}
pub async fn has_token_expired(token: &textsender_models::token::LoginResult) -> bool {
pub async fn has_token_expired(token: &schedtxt_models::token::LoginResult) -> bool {
let now = time::OffsetDateTime::now_utc();
let expire = match time::OffsetDateTime::from_unix_timestamp(token.expires_in) {
Ok(res) => res,
+6 -6
View File
@@ -1,13 +1,13 @@
pub struct Contact {
pub app: crate::app::App,
pub token: textsender_models::token::LoginResult,
pub app: std::sync::Arc<crate::app::App>,
pub token: std::sync::Arc<schedtxt_models::token::LoginResult>,
}
impl Contact {
pub async fn get(
&self,
contact_id: &uuid::Uuid,
) -> Result<Vec<textsender_models::contact::Contact>, std::io::Error> {
) -> Result<Vec<schedtxt_models::contact::Contact>, std::io::Error> {
let client = reqwest::Client::new();
let endpoint = format!("api/v1/contact?id={contact_id}");
let api_url = format!("{}/{endpoint}", self.app.api_url);
@@ -34,17 +34,17 @@ impl Contact {
async fn parse_response(
response: &str,
) -> Result<Vec<textsender_models::contact::Contact>, std::io::Error> {
) -> Result<Vec<schedtxt_models::contact::Contact>, std::io::Error> {
match serde_json::from_str::<serde_json::Value>(response) {
Ok(j) => match j.get("data") {
Some(serde_json::Value::Array(lrs)) => {
if lrs.is_empty() {
Err(std::io::Error::other("Error response is empty"))
} else {
let mut events: Vec<textsender_models::contact::Contact> = Vec::new();
let mut events: Vec<schedtxt_models::contact::Contact> = Vec::new();
for event in lrs.iter() {
let ll: textsender_models::contact::Contact =
let ll: schedtxt_models::contact::Contact =
match serde_json::from_value(event.clone()) {
Ok(lr) => lr,
Err(err) => {
+17 -17
View File
@@ -1,17 +1,17 @@
pub struct Service {
pub app: crate::app::App,
pub token: textsender_models::token::Login,
pub app: std::sync::Arc<crate::app::App>,
pub token: std::sync::Arc<schedtxt_models::token::Login>,
}
impl Service {
pub async fn do_the_work(&mut self) {
println!("Checking queue");
use textsender_models::message::scheduling::ScheduledMessage;
use schedtxt_models::message::scheduling::ScheduledMessage;
let queue = super::queue::Queue {
app: self.app.clone(),
token: self.token.clone(),
app: std::sync::Arc::clone(&self.app),
token: std::sync::Arc::clone(&self.token),
};
let queue_item: Option<ScheduledMessage> = match queue.get_queue().await {
Ok(item) => Some(item),
@@ -37,8 +37,8 @@ impl Service {
println!("Can schedule");
let events_req = super::event::Event {
app: self.app.clone(),
token: self.token.clone(),
app: std::sync::Arc::clone(&self.app),
token: std::sync::Arc::clone(&self.token),
};
let scheduled_message_id = queue_item.id;
@@ -51,20 +51,20 @@ impl Service {
};
let contacts = super::contact::Contact {
app: self.app.clone(),
token: self.token.clone(),
app: std::sync::Arc::clone(&self.app),
token: std::sync::Arc::clone(&self.token),
};
let messages = super::message::Message {
app: self.app.clone(),
token: self.token.clone(),
app: std::sync::Arc::clone(&self.app),
token: std::sync::Arc::clone(&self.token),
};
let sch_msg = super::scheduler::ScheduledMessage {
app: self.app.clone(),
token: self.token.clone(),
app: std::sync::Arc::clone(&self.app),
token: std::sync::Arc::clone(&self.token),
};
let mer_req = super::mer::MessageEventResponse {
app: self.app.clone(),
token: self.token.clone(),
app: std::sync::Arc::clone(&self.app),
token: std::sync::Arc::clone(&self.token),
};
let events = events.unwrap_or_default();
@@ -120,7 +120,7 @@ impl Service {
println!("Message scheduled");
// MER response
let result = swoosh::twilio::api::response_to_json(resp).await;
let mer = textsender_models::message::event::MessageEventResponse {
let mer = schedtxt_models::message::event::MessageEventResponse {
contact_id: contact.id.unwrap(),
scheduled_message_event_id: Some(event.id),
response: result,
@@ -151,7 +151,7 @@ impl Service {
fn is_schedulable(
&self,
queue_item: &textsender_models::message::scheduling::ScheduledMessage,
queue_item: &schedtxt_models::message::scheduling::ScheduledMessage,
) -> bool {
match queue_item.scheduled {
Some(date) => date > time::OffsetDateTime::now_utc(),
+6 -6
View File
@@ -1,13 +1,13 @@
pub struct Event {
pub app: crate::app::App,
pub token: textsender_models::token::LoginResult,
pub app: std::sync::Arc<crate::app::App>,
pub token: std::sync::Arc<schedtxt_models::token::LoginResult>,
}
impl Event {
pub async fn get(
&self,
scheduled_message_id: &uuid::Uuid,
) -> Result<Vec<textsender_models::message::scheduling::ScheduledMessageEvent>, std::io::Error>
) -> Result<Vec<schedtxt_models::message::scheduling::ScheduledMessageEvent>, std::io::Error>
{
let client = reqwest::Client::new();
let endpoint =
@@ -38,7 +38,7 @@ impl Event {
async fn parse_response(
response: &str,
) -> Result<Vec<textsender_models::message::scheduling::ScheduledMessageEvent>, std::io::Error> {
) -> Result<Vec<schedtxt_models::message::scheduling::ScheduledMessageEvent>, std::io::Error> {
match serde_json::from_str::<serde_json::Value>(response) {
Ok(j) => match j.get("data") {
Some(serde_json::Value::Array(lrs)) => {
@@ -46,11 +46,11 @@ async fn parse_response(
Err(std::io::Error::other("Error response is empty"))
} else {
let mut events: Vec<
textsender_models::message::scheduling::ScheduledMessageEvent,
schedtxt_models::message::scheduling::ScheduledMessageEvent,
> = Vec::new();
for event in lrs.iter() {
let ll: textsender_models::message::scheduling::ScheduledMessageEvent =
let ll: schedtxt_models::message::scheduling::ScheduledMessageEvent =
match serde_json::from_value(event.clone()) {
Ok(lr) => lr,
Err(err) => {
+7 -7
View File
@@ -1,13 +1,13 @@
pub struct MessageEventResponse {
pub app: crate::app::App,
pub token: textsender_models::token::LoginResult,
pub app: std::sync::Arc<crate::app::App>,
pub token: std::sync::Arc<schedtxt_models::token::LoginResult>,
}
impl MessageEventResponse {
pub async fn record_message_event_response(
&self,
mer: &textsender_models::message::event::MessageEventResponse,
) -> Result<textsender_models::message::event::MessageEventResponse, std::io::Error> {
mer: &schedtxt_models::message::event::MessageEventResponse,
) -> Result<schedtxt_models::message::event::MessageEventResponse, std::io::Error> {
let client = reqwest::Client::new();
let endpoint = String::from("api/v1/schedule/message/event/response/record");
let api_url = format!("{}/{endpoint}", self.app.api_url);
@@ -27,7 +27,7 @@ impl MessageEventResponse {
"response": mer.response,
"user_id": mer.user_id,
"sent": sent,
"status": textsender_models::message::event::MESSAGE_EVENT_RESPONSE_STATUS_SCHEDULED,
"status": schedtxt_models::message::event::MESSAGE_EVENT_RESPONSE_STATUS_SCHEDULED,
"contact_id": mer.contact_id,
"message_id": mer.message_id
});
@@ -79,7 +79,7 @@ impl MessageEventResponse {
async fn parse_response(
response: &str,
) -> Result<textsender_models::message::event::MessageEventResponse, std::io::Error> {
) -> Result<schedtxt_models::message::event::MessageEventResponse, std::io::Error> {
match serde_json::from_str::<serde_json::Value>(response) {
Ok(j) => {
println!("Good");
@@ -94,7 +94,7 @@ async fn parse_response(
Err(std::io::Error::other("Error response is empty"))
} else {
let lr = lrs[0].clone();
let ll: textsender_models::message::event::MessageEventResponse =
let ll: schedtxt_models::message::event::MessageEventResponse =
match serde_json::from_value(lr) {
Ok(lr) => lr,
Err(err) => {
+6 -6
View File
@@ -1,13 +1,13 @@
pub struct Message {
pub app: crate::app::App,
pub token: textsender_models::token::LoginResult,
pub app: std::sync::Arc<crate::app::App>,
pub token: std::sync::Arc<schedtxt_models::token::LoginResult>,
}
impl Message {
pub async fn get(
&self,
message_id: &uuid::Uuid,
) -> Result<Vec<textsender_models::message::Message>, std::io::Error> {
) -> Result<Vec<schedtxt_models::message::Message>, std::io::Error> {
let client = reqwest::Client::new();
let endpoint = format!("api/v1/message?id={message_id}");
let api_url = format!("{}/{endpoint}", self.app.api_url);
@@ -34,17 +34,17 @@ impl Message {
async fn parse_response(
response: &str,
) -> Result<Vec<textsender_models::message::Message>, std::io::Error> {
) -> Result<Vec<schedtxt_models::message::Message>, std::io::Error> {
match serde_json::from_str::<serde_json::Value>(response) {
Ok(j) => match j.get("data") {
Some(serde_json::Value::Array(lrs)) => {
if lrs.is_empty() {
Err(std::io::Error::other("Error response is empty"))
} else {
let mut events: Vec<textsender_models::message::Message> = Vec::new();
let mut events: Vec<schedtxt_models::message::Message> = Vec::new();
for event in lrs.iter() {
let ll: textsender_models::message::Message =
let ll: schedtxt_models::message::Message =
match serde_json::from_value(event.clone()) {
Ok(lr) => lr,
Err(err) => {
+5 -5
View File
@@ -1,12 +1,12 @@
pub struct Queue {
pub app: crate::app::App,
pub token: textsender_models::token::LoginResult,
pub app: std::sync::Arc<crate::app::App>,
pub token: std::sync::Arc<schedtxt_models::token::LoginResult>,
}
impl Queue {
pub async fn get_queue(
&self,
) -> Result<textsender_models::message::scheduling::ScheduledMessage, std::io::Error> {
) -> Result<schedtxt_models::message::scheduling::ScheduledMessage, std::io::Error> {
let client = reqwest::Client::new();
let endpoint = String::from("api/v1/schedule/message/fetch");
let api_url = format!("{}/{endpoint}", self.app.api_url);
@@ -36,7 +36,7 @@ impl Queue {
async fn parse_queue_response(
response: &str,
) -> Result<textsender_models::message::scheduling::ScheduledMessage, std::io::Error> {
) -> Result<schedtxt_models::message::scheduling::ScheduledMessage, std::io::Error> {
match serde_json::from_str::<serde_json::Value>(response) {
Ok(j) => {
let message = &j["message"];
@@ -50,7 +50,7 @@ async fn parse_queue_response(
Err(std::io::Error::other("Error response is empty"))
} else {
let lr = lrs[0].clone();
let ll: textsender_models::message::scheduling::ScheduledMessage =
let ll: schedtxt_models::message::scheduling::ScheduledMessage =
match serde_json::from_value(lr) {
Ok(lr) => lr,
Err(err) => {
+5 -5
View File
@@ -1,13 +1,13 @@
pub struct ScheduledMessage {
pub app: crate::app::App,
pub token: textsender_models::token::LoginResult,
pub app: std::sync::Arc<crate::app::App>,
pub token: std::sync::Arc<schedtxt_models::token::LoginResult>,
}
impl ScheduledMessage {
pub async fn get(
&self,
scheduled_message_id: &uuid::Uuid,
) -> Result<textsender_models::message::scheduling::ScheduledMessage, std::io::Error> {
) -> Result<schedtxt_models::message::scheduling::ScheduledMessage, std::io::Error> {
let client = reqwest::Client::new();
let endpoint = format!("api/v1/schedule/message?id={scheduled_message_id}");
let api_url = format!("{}/{endpoint}", self.app.api_url);
@@ -34,14 +34,14 @@ impl ScheduledMessage {
async fn parse_response(
response: &str,
) -> Result<textsender_models::message::scheduling::ScheduledMessage, std::io::Error> {
) -> Result<schedtxt_models::message::scheduling::ScheduledMessage, std::io::Error> {
match serde_json::from_str::<serde_json::Value>(response) {
Ok(j) => match j.get("data") {
Some(serde_json::Value::Array(lrs)) => {
if lrs.is_empty() {
Err(std::io::Error::other("Error response is empty"))
} else {
let ll: textsender_models::message::scheduling::ScheduledMessage =
let ll: schedtxt_models::message::scheduling::ScheduledMessage =
match serde_json::from_value(lrs[0].clone()) {
Ok(lr) => lr,
Err(err) => {