use chrono::{DateTime, TimeZone, Utc}; use serde::Deserialize; use std::{thread, time}; use ureq; use std::{ fs::{File, OpenOptions}, io::{self, Read, Seek, Write}, }; const APPID: &str = "227300"; // ETS2: 227300, AssettoCorsa: 244210 const CLANIMGURLBASE: &str = "https://cdn.akamai.steamstatic.com/steamcommunity/public/images/clans/"; const LOGOURL: &str = "https://eurotrucksimulator2.com/images/logo.png"; #[derive(Deserialize)] struct SUResp { appnews: SUNews, } #[derive(Deserialize)] struct SUNews { newsitems: SUNewsItems, } #[derive(Deserialize)] struct SUNewsItems { post0: SUPost, } #[derive(Deserialize)] struct SUPost { //gid: String, title: String, date: i64, url: String, contents: String, } fn main() { let mut f = File::open("webhook.txt").expect( "\nCan't open webhook.txt\nHave you put your Discord Webhook URL in webhook.txt?\n", ); let mut webhook = String::new(); f.read_to_string(&mut webhook) .expect("Can't read webhook!\nPlease put your webhook URL in file webhook.txt"); f = OpenOptions::new() .read(true) .write(true) .create(true) .open("lastpost") .expect("Error opening file"); let mut fcontents = String::new(); f.read_to_string(&mut fcontents).expect("Can't read file"); let mut fdt = if fcontents.is_empty() { 0 } else { fcontents.trim().parse::().unwrap() }; loop { println!( "{}: Checking for ETS2 updates...", chrono::offset::Utc::now() ); let body: SUResp = ureq::get(&format!( "https://api.steampowered.com/ISteamNews/GetNewsForApp/v2/?appid={}&count=1&format=json", APPID )) .call() .expect("Error getting data from Steam API") .into_json() .expect("Error deserializing JSON"); if fdt < body.appnews.newsitems.post0.date { println!( "{}: Updating ets2 update timestamp file and posting...", chrono::offset::Utc::now() ); let ts = Utc .timestamp_opt(body.appnews.newsitems.post0.date.clone(), 0) .unwrap(); let dt = DateTime::::from(ts); let t_str = dt.format("%Y-%m-%dT%H:%M:%S.000Z"); let tmptext: Vec<&str> = body .appnews .newsitems .post0 .contents .split("[img]{STEAM_CLAN_IMAGE}") .collect(); let imgurl = if tmptext.len() > 1 { let splitend: Vec<&str> = tmptext[1].split("[/img]").collect(); format!("{}{}", CLANIMGURLBASE, splitend[0]) } else { LOGOURL.to_string() }; let whreq: String = format!( r#" {{ "username": "ETS2 Updates", "avatar_url": "{}", "content": "", "embeds": [{{ "title": "{}", "description": "{}", "url": "{}", "timestamp": "{}", "thumbnail": {{"url": "{}"}} }}] }} "#, LOGOURL, body.appnews.newsitems.post0.title, if body.appnews.newsitems.post0.contents.len() > 250 { format!( "{} ...", body.appnews.newsitems.post0.contents[..250] .to_string() .replace("\n", " ") ) } else { body.appnews .newsitems .post0 .contents .to_string() .replace("\n", " ") }, body.appnews.newsitems.post0.url, t_str, imgurl ); let resp = ureq::post(&webhook) .set("Content-Type", "application/json;charset=utf-8") .send_string(&whreq); match resp { Ok(_) => { println!("Successfully updated webhook"); f.set_len(0).expect("Cannot empty the file"); f.seek(io::SeekFrom::Start(0)) .expect("Cannot seek to file beginning"); write!(f, "{}", body.appnews.newsitems.post0.date).expect("Can't write file"); } Err(e) => { println!("Error updating webhook:\n{}\nRequest JSON:\n{}", e, &whreq); } } fdt = body.appnews.newsitems.post0.date; } println!("{}: Sleeping...", chrono::offset::Utc::now()); thread::sleep(time::Duration::from_secs(60 * 15)); } }