mod argument; mod config; use argument::Argument; use chrono::Local; use clap::Parser; use config::Config; use log::*; use rustypipe::client::RustyPipe; use std::{env::var, process::Command, time::Duration}; #[tokio::main] async fn main() { if var("RUST_LOG").is_ok() { use tracing_subscriber::{EnvFilter, fmt::*}; struct T; impl time::FormatTime for T { fn format_time(&self, w: &mut format::Writer<'_>) -> std::fmt::Result { write!(w, "{}", Local::now()) } } fmt() .with_timer(T) .with_env_filter(EnvFilter::from_default_env()) .init() } let argument = Argument::parse(); let config: Config = toml::from_str(&std::fs::read_to_string(&argument.config).unwrap()).unwrap(); let update = config.update.map(Duration::from_secs); let rp = RustyPipe::new(); loop { for (c, channel) in &config.channel { match rp.query().channel_videos(&channel.id).await { Ok(result) => { for item in result.content.items.iter().filter(|i| { i.is_live == channel.is_live && i.is_short == channel.is_short && i.is_upcoming == channel.is_upcoming }) { for command in &channel.command { let cmd = command.exec.replace("{ID}", &item.id); match Command::new("sh").arg("-c").arg(&cmd).output() { Ok(response) => { if response.status.success() { if command.stdout_contains.as_ref().is_none_or(|s| { String::from_utf8_lossy(&response.stdout).contains(s) }) { debug!( "command `{cmd}` for channel `{c}` successful: `{:?}`", response ) } else { warn!( "unexpected command `{cmd}` for channel `{c}`: `{:?}`", response ) } } else { warn!( "command `{cmd}` for channel `{c}` failed: `{:?}`", response ) } } Err(e) => { warn!("can't execute command `{cmd}` for channel `{c}`: `{e}`") } } } } } Err(e) => warn!("can't scrape channel `{c}`: `{e}`"), } } match update { Some(duration) => { debug!( "queue completed; await {} seconds to continue...", duration.as_secs() ); std::thread::sleep(duration) } None => { debug!("all tasks completed; exit."); break; } } } }