Skip to content

Commit

Permalink
Fix several bugs
Browse files Browse the repository at this point in the history
Added a self-updating version number to the CLI.

Added a "background task" to receive updates from the "monitoring tasks"
through a `mpsc` channel using the new `Update` enum, handling them
without stopping the monitor.

Fixed bug where the program would repeatedly attempt to send webhooks to
invalid links. A vector called `broken_webhooks` is now used to keep
track of non-functioning URLs so that the monitor ignores them.

Changed logic to stop monitoring sites with no valid webhooks.

Changed program to quit when no stores are being monitored.

Fixed bug where the program would "spam" print "warning" messages to the
console when unable to reach a store. When it fails to connect to a
website, the monitor now gives a single warning, before alerting the
user that the site is back online once it can reconnect. If it fails to
connect to any store at any given time, the program will warn that it
likely isn't connected to the internet.

Fixed bug where the monitoring tasks would wait for all webhooks to be
sent before checking for new store updates. The webhook-sending tasks
are no longer `join`ed so that the program never stops "monitoring".

Added a `preview()` function to generate webhook previews through
https://discohook.org/. These links are currently never used but could
be stored in the `shopify-monitor.log` file in the future.

Removed several fields from the `struct`s defined in `products.rs`,
allowing for faster deserialization of `products.json`, which should
reduce latency.

Fixed bug where Discord API status codes were incorrectly detected.
  • Loading branch information
subreme committed Dec 21, 2021
1 parent 1c8262c commit 3cff865
Show file tree
Hide file tree
Showing 9 changed files with 842 additions and 491 deletions.
20 changes: 20 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `minimum` field in `Settings`, which controls the minimum number of
available variants a product should have for a `restock` webhook to be
sent out.
- The ability to detect invalid webhook URLs and stop using them.
- The process of automatically stopping the monitoring of a site if none
of its webhooks are working.
- The logic to terminate the program if no stores are being monitored.
- A self-updating [version number](src/main.rs#L43-L50) in the program's
start screen.

### Changed

- The monitor's logic to have a "background process" handle tasks that
would interrupt the monitor.
- The number of fields in `products.json` that are deserialized,
reducing latency.

### Fixed

- Bug where the Status Codes to the Discord API Responses were
interpreted incorrectly.
- Process that "spammed" the terminal, sending repeated warnings about
websites being unreachable.

## [0.1.1] - 2021-07-31

Expand Down
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ version = "1"
features = ["full"]

[dependencies]
base64 = "0.13.0"
chrono = "0.4"
colored = "2"
futures = "0.3.16"
Expand Down
76 changes: 42 additions & 34 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,39 +9,45 @@ use std::{collections::HashMap, fs, io, process, vec::IntoIter};
// This function is used to get the deserialized values saved in
// `config.json` in order for the program to know what to do.
pub fn read() -> Config {
// At first, the program assumes it's being run by a regular user
// and doesn't mention the possible existence of
// `config.private.json`.

// This file is intended to be used by developers, as the repository's
// `.gitignore` doesn't include `config.json`. By configuring their
// own settings in the private file, leaving the "public" one with
// its example settings, they can ensure that they don't
// accidentally publish their webhook URLs and the integrity of the
// example configuration isn't compromised.

hidden!("Loading `config.private.json`...");
default!("Loading config file...");

if let Ok(config) = fs::read_to_string("config.private.json") {
hidden!("Reading `private.config.json`...");

// The program only refers to the private config file as such if
//the directory it's in contains it.
default!("Reading private config file...");

let json = serde_json::from_str(config.as_str());

if let Ok(value) = json {
success!("Successfully parsed settings!");
return value;
} else if let Err(error) = json {
hidden!("Failed to parse `config.private.json`: {}", error);
}
// This code block will only run when the program is in debug mode,
// and won't be included in the monitor's release build, as regular
// users have no use for the alternative `config.private.json`.
#[cfg(debug_assertions)]
{
// At first, the program assumes it's being run by a regular user
// and doesn't mention the possible existence of
// `config.private.json`.

// This file is intended to be used by developers, as the repository's
// `.gitignore` doesn't include `config.json`. By configuring their
// own settings in the private file, leaving the "public" one with
// its example settings, they can ensure that they don't
// accidentally publish their webhook URLs and the integrity of the
// example configuration isn't compromised.

hidden!("Loading `config.private.json`...");
default!("Loading config file...");

if let Ok(config) = fs::read_to_string("config.private.json") {
hidden!("Reading `private.config.json`...");

// The program only refers to the private config file as such if
//the directory it's in contains it.
default!("Reading private config file...");

let json = serde_json::from_str(config.as_str());

if let Ok(value) = json {
success!("Successfully parsed settings!");
return value;
} else if let Err(error) = json {
hidden!("Failed to parse `config.private.json`: {}", error);
}

warning!("Invalid private config file!");
default!("Trying again with `config.json`...");
};
warning!("Invalid private config file!");
default!("Trying again with `config.json`...");
};
}

hidden!("Loading `config.json`...");

Expand Down Expand Up @@ -134,7 +140,9 @@ fn suggest_instructions() {
// there's enough time to read the error messages, before discarding
// the new input and terminating the program.
let mut input = String::new();
io::stdin().read_line(&mut input).unwrap();
io::stdin()
.read_line(&mut input)
.expect("Failed to read input.");

// The function could also call `process::exit()`, as it is repeated
// after both of its instances, however the compiler wouldn't
Expand Down Expand Up @@ -476,7 +484,7 @@ pub struct Keyword {
// they can set common "shared" keywords without having to repeat
// them in every event's settings.

// As a result, these two example are equivalent:
// As a result, these three example are equivalent:

// // Example 1:
// "keywords": null,
Expand Down
27 changes: 24 additions & 3 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ mod tests;
mod webhook;

use colored::*;
use std::io::stdin;

#[tokio::main]
async fn main() {
Expand All @@ -18,15 +19,24 @@ async fn main() {
// of "Shopify Monitor", with "Shopify" printed green, using the
// `colored` crate, to somewhat resemble the company logo.
println!(
" {} __ __ _ _\n {} | \\/ | (_) |\n{} | \\ / | ___ _ __ _| |_ ___ _ __\n {} | |\\/| |/ _ \\| '_ \\| | __/ _ \\| '__|\n {} | | | | (_) | | | | | || (_) | |\n{} |_| |_|\\___/|_| |_|_|\\__\\___/|_|\n {}\n {}\n",
" {} __ __ _ _\n {} | \\/ | (_) |\n{} | \\ / | ___ _ __ _| |_ ___ _ __\n {} | |\\/| |/ _ \\| '_ \\| | __/ _ \\| '__|\n {} | | | | (_) | | | | | || (_) | |\n{} |_| |_|\\___/|_| |_|_|\\__\\___/|_|\n {}\n {}{}\n",
"_____ _ _ __".green(),
"/ ____| | (_)/ _|".green(),
"| (___ | |__ ___ _ __ _| |_ _ _".green(),
"\\___ \\| '_ \\ / _ \\| '_ \\| | _| | | |".green(),
"____) | | | | (_) | |_) | | | | |_| |".green(),
"|_____/|_| |_|\\___/| .__/|_|_| \\__, |".green(),
"| | __/ |".green(),
"|_| |___/".green()
"|_| |___/".green(),

// This code block allows for the version number of the program
// to be always up-to-date, as it will check the value indicated
// in `Cargo.toml` and dynamically adjust the number of spaces
// used so that the text is always aligned properly.
{
let version = env!("CARGO_PKG_VERSION");
format!("{}VERSION {}", " ".repeat(27 - version.len()), version.green())
}
);

// The output will look like this:
Expand All @@ -37,7 +47,7 @@ async fn main() {
// ____) | | | | (_) | |_) | | | | |_| | | | | | (_) | | | | | || (_) | |
// |_____/|_| |_|\___/| .__/|_|_| \__, | |_| |_|\___/|_| |_|_|\__\___/|_|
// | | __/ |
// |_| |___/
// |_| |___/ VERSION X.X.X

important!("LOADING SETTINGS");

Expand All @@ -50,4 +60,15 @@ async fn main() {

// Once the `settings` are returned, the monitor can start running.
monitor::run(settings).await;

// If there aren't any issues, the program should run indefinitely.
// If the monitor is stopped, however, the function will return and
// the following code will run. At the moment, the only cause for
// `run()` to end is if all provided webhook links are invalid,
// however additional logic may be implemented in the future.
important!("STOPPED MONITOR");
default!("The monitor has stopped running. Press `Enter` to quit.");
stdin()
.read_line(&mut String::new())
.expect("Failed to read input.");
}
Loading

0 comments on commit 3cff865

Please sign in to comment.