mirror of
https://codeberg.org/icewind/rss-webhook-trigger.git
synced 2026-06-03 18:04:09 +02:00
initial version
This commit is contained in:
parent
0ee3aec432
commit
51cba405ec
7 changed files with 1414 additions and 5 deletions
28
src/config.rs
Normal file
28
src/config.rs
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
use color_eyre::{eyre::WrapErr, Result};
|
||||
use serde::{Deserialize, Deserializer};
|
||||
use std::fs::read_to_string;
|
||||
use tokio::time::Duration;
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct Config {
|
||||
interval: Option<u64>,
|
||||
pub feed: Vec<FeedConfig>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct FeedConfig {
|
||||
pub feed: String,
|
||||
pub hook: String,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn from_file(path: &str) -> Result<Self> {
|
||||
let file = read_to_string(path)
|
||||
.wrap_err_with(|| format!("Failed to open config file {}", path))?;
|
||||
toml::from_str(&file).wrap_err_with(|| format!("Failed to open config file {}", path))
|
||||
}
|
||||
|
||||
pub fn interval(&self) -> Duration {
|
||||
Duration::from_secs(self.interval.unwrap_or(30 * 60))
|
||||
}
|
||||
}
|
||||
104
src/main.rs
104
src/main.rs
|
|
@ -1,3 +1,103 @@
|
|||
fn main() {
|
||||
println!("Hello, world!");
|
||||
mod config;
|
||||
|
||||
use crate::config::Config;
|
||||
use color_eyre::{
|
||||
eyre::{eyre, WrapErr},
|
||||
Result,
|
||||
};
|
||||
use reqwest::Client;
|
||||
use rss::Channel;
|
||||
use std::collections::hash_map::DefaultHasher;
|
||||
use std::collections::HashMap;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use tokio::time::sleep;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
let mut args = std::env::args();
|
||||
let bin = args.next().unwrap();
|
||||
|
||||
let file = match args.next() {
|
||||
Some(file) => file,
|
||||
None => {
|
||||
eprintln!("Usage {} <config file>", bin);
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
|
||||
let config = Config::from_file(&file)?;
|
||||
let mut fetcher = FeedFetcher::default();
|
||||
|
||||
loop {
|
||||
for feed in config.feed.iter() {
|
||||
match fetcher.is_feed_updated(&feed.feed).await {
|
||||
Ok(true) => {
|
||||
println!("Trigering hook for {}", feed.feed);
|
||||
fetcher.client.post(&feed.hook).send().await?;
|
||||
}
|
||||
Err(e) => eprintln!("{:#}", e),
|
||||
Ok(false) => {}
|
||||
}
|
||||
}
|
||||
|
||||
sleep(config.interval()).await;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct FeedFetcher {
|
||||
client: Client,
|
||||
cache: HashMap<String, u64>,
|
||||
}
|
||||
|
||||
impl FeedFetcher {
|
||||
pub async fn is_feed_updated(&mut self, feed: &str) -> Result<bool> {
|
||||
let new_key = self.get_feed_key(feed).await?;
|
||||
|
||||
Ok(match self.cache.get_mut(feed) {
|
||||
Some(cached) => {
|
||||
if *cached != new_key {
|
||||
*cached = new_key;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
None => {
|
||||
self.cache.insert(feed.into(), new_key);
|
||||
|
||||
// dont trigger the actions on start
|
||||
|
||||
false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async fn get_feed_key(&self, feed: &str) -> Result<u64> {
|
||||
let content = self
|
||||
.client
|
||||
.get(feed)
|
||||
.send()
|
||||
.await
|
||||
.wrap_err_with(|| eyre!("Failed to load feed {}", feed))?
|
||||
.bytes()
|
||||
.await
|
||||
.wrap_err_with(|| eyre!("Failed to load feed {}", feed))?;
|
||||
let channel = Channel::read_from(content.as_ref())
|
||||
.wrap_err_with(|| eyre!("Failed to parse feed {}", feed))?;
|
||||
let item = channel.items.first().ok_or(eyre!("Empty feed"))?;
|
||||
|
||||
let mut hasher = DefaultHasher::new();
|
||||
if let Some(guid) = item.guid() {
|
||||
guid.value.hash(&mut hasher);
|
||||
} else if let Some(date) = item.pub_date() {
|
||||
date.hash(&mut hasher);
|
||||
} else if let Some(link) = item.link() {
|
||||
link.hash(&mut hasher);
|
||||
} else {
|
||||
return Err(eyre!("No guid, pubDate or link set on feed item"));
|
||||
}
|
||||
|
||||
Ok(hasher.finish())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue