mirror of
https://github.com/davegallant/rfd-notify.git
synced 2025-08-05 16:23:39 +00:00
2
.github/workflows/docker.yml
vendored
2
.github/workflows/docker.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
|||||||
- name: Publish version to Registry
|
- name: Publish version to Registry
|
||||||
uses: elgohr/Publish-Docker-Github-Action@master
|
uses: elgohr/Publish-Docker-Github-Action@master
|
||||||
with:
|
with:
|
||||||
name: davegallant/npm-groovy-lint-test
|
name: davegallant/rfd-notify
|
||||||
username: ${{ secrets.DOCKER_USERNAME }}
|
username: ${{ secrets.DOCKER_USERNAME }}
|
||||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
tag_semver: true
|
tag_semver: true
|
||||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1 +1,2 @@
|
|||||||
/target
|
/target
|
||||||
|
deals_db/
|
||||||
|
607
Cargo.lock
generated
607
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
13
Cargo.toml
13
Cargo.toml
@@ -1,16 +1,15 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "rfd-notify"
|
name = "rfd-notify"
|
||||||
version = "0.1.2"
|
version = "0.2.0"
|
||||||
authors = ["Dave Gallant <davegallant@gmail.com>"]
|
authors = ["Dave Gallant <davegallant@gmail.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
sendgrid = { version = "0.13.0", features = ["rustls"] }
|
sendgrid = { version = "0.13.0", default-features = false, features = ["rustls", "blocking"] }
|
||||||
toml = "0.5.7"
|
|
||||||
serde_derive = "1.0.112"
|
serde_derive = "1.0.112"
|
||||||
serde = "1.0.117"
|
serde = "1.0.117"
|
||||||
reqwest = { version = "0.10", default-features = false, features = ["json", "rustls-tls"] }
|
reqwest = { version = "0.10", default-features = false, features = ["json", "rustls-tls"] }
|
||||||
tokio = { version = "0.3", features = ["full"] }
|
tokio = { version = "0.2", features = ["full"] }
|
||||||
serde_json = "1.0.59"
|
serde_json = "1.0.59"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
pretty_env_logger = "0.4.0"
|
pretty_env_logger = "0.4.0"
|
||||||
@@ -18,3 +17,9 @@ regex = "1.4.1"
|
|||||||
rust-crypto = "^0.2"
|
rust-crypto = "^0.2"
|
||||||
sled = "0.34.4"
|
sled = "0.34.4"
|
||||||
clap = "2.33.3"
|
clap = "2.33.3"
|
||||||
|
envconfig = "0.9.1"
|
||||||
|
envconfig_derive = "0.9.1"
|
||||||
|
serde_yaml = "0.8"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
serial_test = "*"
|
||||||
|
69
README.md
69
README.md
@@ -8,11 +8,11 @@
|
|||||||
This tool looks for regular expressions from [RedFlagDeals.com forums](https://forums.redflagdeals.com/hot-deals-f9/) and will send emails based on matches.
|
This tool looks for regular expressions from [RedFlagDeals.com forums](https://forums.redflagdeals.com/hot-deals-f9/) and will send emails based on matches.
|
||||||
|
|
||||||
|
|
||||||
## requirements
|
## Prerequisites
|
||||||
|
|
||||||
- a free [SendGrid API key](https://sendgrid.com/pricing/)
|
- a free [SendGrid API key](https://sendgrid.com/pricing/)
|
||||||
|
|
||||||
## use
|
## Usage
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
USAGE:
|
USAGE:
|
||||||
@@ -27,28 +27,47 @@ OPTIONS:
|
|||||||
-d, --dbpath <dbpath> Specify path to where the embedded database is stored [default: ./deals_db]
|
-d, --dbpath <dbpath> Specify path to where the embedded database is stored [default: ./deals_db]
|
||||||
```
|
```
|
||||||
|
|
||||||
### docker
|
## Github action
|
||||||
|
|
||||||
```shell
|
An action can be setup to scan for deals, send a notification and store previously found deals in the repo.
|
||||||
# Run the docker image using an example config:
|
|
||||||
docker run -e RUST_LOG=INFO davegallant/rfd-notify -c /examples/config.toml
|
### Example
|
||||||
|
|
||||||
|
This action can be run on a cron and can store the embedded database by commiting the files to git.
|
||||||
|
|
||||||
|
It also requires the corresponding [encrypted secrets](https://docs.github.com/en/free-pro-team@latest/actions/reference/encrypted-secrets) setup.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# .github/workflows/main.yml
|
||||||
|
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: '*/5 * * * *'
|
||||||
|
jobs:
|
||||||
|
rfd_notify:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
name: rfd-notify
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
persist-credentials: false # otherwise, the token used is the GITHUB_TOKEN, instead of your personal token
|
||||||
|
fetch-depth: 0 # otherwise, you will failed to push refs to dest repo
|
||||||
|
- name: Run rfd-notify
|
||||||
|
uses: davegallant/rfd-notify@v0.2.0
|
||||||
|
env:
|
||||||
|
SENDGRID_API_KEY: ${{ secrets.SENDGRID_API_KEY }}
|
||||||
|
SENDGRID_MAIL_FROM: notify@rfd-notify.org
|
||||||
|
SENDGRID_MAIL_TO: ${{ secrets.SENDGRID_MAIL_TO }}
|
||||||
|
- name: Commit files
|
||||||
|
run: |
|
||||||
|
git config --local user.email "action@github.com"
|
||||||
|
git config --local user.name "GitHub Action"
|
||||||
|
git add deals_db/
|
||||||
|
git commit -m "Add changes" -a || true
|
||||||
|
- name: Push changes
|
||||||
|
uses: ad-m/github-push-action@master
|
||||||
|
with:
|
||||||
|
github_token: ${{ secrets.PERSONAL_GITHUB_TOKEN }}
|
||||||
|
branch: ${{ github.ref }}
|
||||||
```
|
```
|
||||||
|
|
||||||
Provide a custom configuration. An example can found in [config.toml](./examples/config.toml)
|
|
||||||
|
|
||||||
```shell
|
|
||||||
# Provide a custom-config.toml that is in the current directory
|
|
||||||
# ensuring the correct user is mapped to the working directory
|
|
||||||
docker run -u "$(id -u):$(id -g)" -w=/tmp -e RUST_LOG=INFO -v "$PWD":/tmp davegallant/rfd-notify -c /tmp/custom-config.toml
|
|
||||||
```
|
|
||||||
|
|
||||||
## cross compile
|
|
||||||
|
|
||||||
I had motivations to run this on a Raspberry Pi Zero (without needing docker on the pi):
|
|
||||||
|
|
||||||
```shell
|
|
||||||
alias rust-musl-builder='docker run --rm -it -v "$(pwd)":/home/rust/src messense/rust-musl-cross:arm-musleabihf'
|
|
||||||
rust-musl-builder cargo build --release
|
|
||||||
```
|
|
||||||
|
|
||||||
The above can be substituted for [other architectures](https://github.com/messense/rust-musl-cross#prebuilt-images).
|
|
||||||
|
7
action.yml
Normal file
7
action.yml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
name: 'rfd-notify'
|
||||||
|
description: 'get notified of deals based on regex'
|
||||||
|
icon: command
|
||||||
|
color: gray-dark
|
||||||
|
runs:
|
||||||
|
using: 'docker'
|
||||||
|
image: 'github-action/Dockerfile'
|
@@ -1,11 +0,0 @@
|
|||||||
expressions = [
|
|
||||||
"amazon",
|
|
||||||
"costco",
|
|
||||||
"rx.?5[6789]0",
|
|
||||||
"starbucks"
|
|
||||||
]
|
|
||||||
|
|
||||||
[sendgrid]
|
|
||||||
api_key = "<SENDGRID-API-KEY>"
|
|
||||||
mail_to = "<YOUR-EMAIL>@gmail.com"
|
|
||||||
mail_from = "Notify <notify@rfd-notify.org>"
|
|
5
examples/config.yaml
Normal file
5
examples/config.yaml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
expressions:
|
||||||
|
- amazon
|
||||||
|
- costco",
|
||||||
|
- "rx.?5[6789]0"
|
||||||
|
- starbucks
|
3
github-action/Dockerfile
Normal file
3
github-action/Dockerfile
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
FROM davegallant/rfd-notify:0.2.0
|
||||||
|
|
||||||
|
ENTRYPOINT ["/rfd-notify", "-c", "config.yaml"]
|
5
github-action/entrypoint.sh
Executable file
5
github-action/entrypoint.sh
Executable file
@@ -0,0 +1,5 @@
|
|||||||
|
#!/bin/sh -l
|
||||||
|
|
||||||
|
echo "Starting rfd-notify with config: $1"
|
||||||
|
|
||||||
|
rfd-notify -c "$1"
|
@@ -1,40 +1,76 @@
|
|||||||
extern crate toml;
|
extern crate envconfig;
|
||||||
use serde_derive::Deserialize;
|
extern crate envconfig_derive;
|
||||||
|
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
use envconfig::Envconfig;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
use std::vec::Vec;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
extern crate serial_test;
|
||||||
|
#[cfg(test)]
|
||||||
|
use serial_test::serial;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_config() {
|
#[should_panic]
|
||||||
let file = "./examples/config.toml";
|
#[serial]
|
||||||
parse(&file);
|
fn load_config_with_missing_sendgrid_api_key() {
|
||||||
|
std::env::remove_var("SENDGRID_API_KEY");
|
||||||
|
let file = "./examples/config.yaml";
|
||||||
|
load(&file);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[serial]
|
||||||
|
fn load_config() {
|
||||||
|
let file = "./examples/config.yaml";
|
||||||
|
std::env::set_var("SENDGRID_API_KEY", "FAKE");
|
||||||
|
std::env::set_var("SENDGRID_MAIL_FROM", "notify@rfd-notify.org");
|
||||||
|
std::env::set_var("SENDGRID_MAIL_TO", "test@email.com");
|
||||||
|
load(&file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub expressions: Vec<String>,
|
pub expressions: Vec<String>,
|
||||||
pub sendgrid: Sendgrid,
|
pub sendgrid: SendgridConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Debug, Deserialize, PartialEq)]
|
||||||
pub struct Sendgrid {
|
pub struct ConfigFile {
|
||||||
|
pub expressions: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Envconfig, Debug)]
|
||||||
|
pub struct SendgridConfig {
|
||||||
|
#[envconfig(from = "SENDGRID_MAIL_FROM")]
|
||||||
pub mail_from: String,
|
pub mail_from: String,
|
||||||
|
|
||||||
|
#[envconfig(from = "SENDGRID_MAIL_TO")]
|
||||||
pub mail_to: String,
|
pub mail_to: String,
|
||||||
|
|
||||||
|
#[envconfig(from = "SENDGRID_API_KEY")]
|
||||||
pub api_key: String,
|
pub api_key: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse(filename: &str) -> Config {
|
pub fn load(filename: &str) -> Config {
|
||||||
|
// Initialize expressions from file
|
||||||
let contents = fs::read_to_string(filename)
|
let contents = fs::read_to_string(filename)
|
||||||
.unwrap_or_else(|e| panic!("Unable to read configuration file '{}'. {}", filename, e));
|
.unwrap_or_else(|e| panic!("Unable to read configuration file '{}'. {}", filename, e));
|
||||||
let config: Config = toml::from_str(&contents).unwrap_or_else(|e| {
|
|
||||||
panic!(
|
|
||||||
"Unable to parse configuration with contents: {}. {}",
|
|
||||||
contents, e
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
config
|
let config_file: ConfigFile = serde_yaml::from_str(&contents).unwrap();
|
||||||
|
|
||||||
|
// Initialize config from environment variables or terminate the process.
|
||||||
|
let sendgrid_config = SendgridConfig::init_from_env().unwrap();
|
||||||
|
|
||||||
|
Config {
|
||||||
|
expressions: config_file.expressions,
|
||||||
|
sendgrid: sendgrid_config,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -39,7 +39,7 @@ fn main() {
|
|||||||
let matches = app.get_matches();
|
let matches = app.get_matches();
|
||||||
|
|
||||||
let config = matches.value_of("config").unwrap();
|
let config = matches.value_of("config").unwrap();
|
||||||
let parsed_config = config::parse(config);
|
let parsed_config = config::load(config);
|
||||||
|
|
||||||
info!("{:?}\n", parsed_config);
|
info!("{:?}\n", parsed_config);
|
||||||
let hot_deals = rfd::get_hot_deals().map_err(|err| error!("{:?}", err)).ok();
|
let hot_deals = rfd::get_hot_deals().map_err(|err| error!("{:?}", err)).ok();
|
||||||
|
Reference in New Issue
Block a user