forum_notifier/src/main.rs
author unc0rr
Sun, 05 May 2019 00:06:52 +0200
changeset 1 52bbcfcbd850
parent 0 5696442fee1c
permissions -rw-r--r--
- Add check for skipped page ids - Silence spam with repeated author/title pair

use curl::easy::{Easy, WriteError};
use curl::Error;
use dbus::{BusType, Connection, Message};
use log::{debug, info, warn};
use scraper::{Html, Selector};
use std::fs;
use std::str;
use std::thread;
use std::time;

struct StateInfo {
    url: String,
    previous_title: String,
    previous_author: String,
}

fn inform(state: &mut StateInfo, title: &str, author: &str) {
    if state.previous_title == title && state.previous_author == author {
        return;
    }

    state.previous_title = title.to_string();
    state.previous_author = author.to_string();

    let c = Connection::get_private(BusType::Session).unwrap();
    let mut m = Message::new_method_call(
        "org.kde.konversation",
        "/irc",
        "org.kde.konversation",
        "say",
    )
    .unwrap();
    let msg_str = &format!("[{}] {}: {}", state.url, author, title)[..];

    info!("Notification: {}", msg_str);

    m.append_items(&["freenode".into(), "#hedgewars".into(), msg_str.into()]);
    let _r = c.send(m).unwrap();
}

fn handle_page(state: &mut StateInfo, data: &[u8]) -> Result<usize, WriteError> {
    let page = str::from_utf8(data).unwrap();
    let document = Html::parse_document(page);

    let title_selector = Selector::parse("title").unwrap();
    let mut titles = document.select(&title_selector);

    if let Some(title_str) = titles.next() {
        let t = &title_str.inner_html();
        let title = &t[..t.len() - 12];
        //info!("Topic is '{}'", title);

        let author_selector = Selector::parse("div.forum-post-wrapper div.forum-post-panel-sub div.author-pane div.author-pane-inner div.author-pane-name-status.author-pane-section div.author-pane-line.author-name").unwrap();
        let mut authors = document.select(&author_selector);

        if let Some(author_name) = authors.next() {
            let author = &author_name.inner_html();
            //info!("Author is '{}'", author);

            inform(state, title, &author[1..author.len() - 1]);
        } else {
            warn!("Author not found");
        }
    } else {
        warn!("No title found");
    }

    Ok(data.len())
}

fn query(state: &mut StateInfo) -> Result<(), Error> {
    let mut easy = Easy::new();

    easy.url(&state.url)?;
    easy.fail_on_error(true)?;

    let mut transfer = easy.transfer();
    transfer.write_function(|data| handle_page(state, data))?;
    transfer.perform()
}

fn main() {
    stderrlog::new()
        .verbosity(3)
        .timestamp(stderrlog::Timestamp::Second)
        .module(module_path!())
        .init()
        .unwrap();

    let short_delay = time::Duration::from_secs(3);
    let long_delay = time::Duration::from_secs(100);
    let file_name = "/usr/home/unC0Rr/.local/unC0Rr/forumchecker/node.txt";
    let mut node_id: u32 = fs::read_to_string(file_name).unwrap().parse().unwrap();
    let mut known_good_id: Option<u32> = None;
    let mut shift: u32 = 0;

    let mut state = StateInfo {
        url: String::new(),
        previous_title: String::new(),
        previous_author: String::new()
    };

    info!("Starting from node id {}", node_id);

    loop {
        let try_id = node_id + shift;

        debug!("Quering node id {}", try_id);

        thread::sleep(short_delay);

        state.url = format!("https://www.hedgewars.org/node/{}", try_id);

        let res = query(&mut state);

        if let Ok(_) = res {
            if shift > 0 && known_good_id == None {
                known_good_id = Some(try_id);
                shift = 0;
            } else {
                node_id = try_id + 1;
                if Some(node_id) == known_good_id {
                    node_id += 1;
                    known_good_id = None;
                }
                shift = 0;
                fs::write(file_name, node_id.to_string()).unwrap();
            }
        } else {
            if known_good_id == None {
                shift = (shift + 1) % 11;
                thread::sleep(long_delay);
            } else if Some(try_id + 1) == known_good_id {
                node_id = known_good_id.unwrap() + 1;
                known_good_id = None;
                shift = 0;
                fs::write(file_name, node_id.to_string()).unwrap();
            } else {
                shift += 1;
            }
        }
    }
}