Skip to main content

poll_thread/
poll_thread.rs

1// cargo run --example poll_thread -- po [thread]
2
3// Polls a single thread with `If-Modified-Since` and prints replies as they
4// arrive. Demonstrates the `Conditional<T>` flow: feed the previous
5// `Last-Modified` back in, get `Ok(None)` on `304 Not Modified`, and only
6// re-render when something actually changed.
7
8use std::collections::HashSet;
9use std::env;
10use std::time::Duration;
11
12use chan::{Client, Post};
13
14#[chan::tokio::main]
15async fn main() -> chan::Result<()> {
16    let mut args = env::args().skip(1);
17    let board = args.next().unwrap_or_else(|| "po".to_string());
18    let thread_no: u64 = args
19        .next()
20        .and_then(|s| s.parse().ok())
21        .expect("usage: poll_thread <board> <thread_no>");
22
23    let client = Client::new();
24    let mut seen: HashSet<u64> = HashSet::new();
25    let mut since: Option<String> = None;
26
27    loop {
28        match client
29            .get_full_thread_if_modified(&board, thread_no, since.as_deref())
30            .await
31        {
32            Ok(None) => {
33                eprintln!("304 Not Modified");
34            }
35            Ok(Some(cond)) => {
36                since = cond.last_modified;
37                for post in &cond.value.posts {
38                    if seen.insert(post.no) {
39                        print_post(&board, post);
40                    }
41                }
42                if cond.value.op().archived || cond.value.op().closed {
43                    eprintln!("Thread archived or closed. Exiting.");
44                    return Ok(());
45                }
46            }
47            Err(chan::Error::NotFound(_)) => {
48                eprintln!("Thread 404'd. Exiting.");
49                return Ok(());
50            }
51            Err(e) => return Err(e),
52        }
53        tokio::time::sleep(Duration::from_secs(30)).await;
54    }
55}
56
57fn print_post(board: &str, p: &Post) {
58    let who = match (&p.trip, &p.id) {
59        (Some(t), Some(id)) => format!("{} {} ({})", p.name, t, id),
60        (Some(t), None) => format!("{} {}", p.name, t),
61        (None, Some(id)) => format!("{} ({})", p.name, id),
62        (None, None) => p.name.clone(),
63    };
64    println!("--- >>{} {} [{}] ---", p.no, who, p.now);
65    if let Some(sub) = &p.sub {
66        println!("Subject: {}", sub);
67    }
68    if let Some(att) = &p.attachment {
69        println!("File: {} ({} bytes)", att.url(board), att.size);
70    }
71    if let Some(com) = &p.com {
72        println!("{}", com);
73    }
74    println!();
75}