pub struct Client { /* private fields */ }Expand description
Async client for the 4chan JSON API.
Cheap to clone, internal config is Arc-shared. Construct with
Client::new for sensible defaults, or Client::with_client to plug
in your own reqwest::Client (matching backgrounds/rchan ergonomics).
Implementations§
Source§impl Client
impl Client
Sourcepub fn new() -> Self
pub fn new() -> Self
New client with a fresh reqwest::Client and a chan-rs/<ver>
User-Agent.
Examples found in repository?
More examples
7async fn main() -> chan::Result<()> {
8 let board = env::args().nth(1).unwrap_or_else(|| "po".to_string());
9 let client = chan::Client::new();
10 let catalog = client.get_board_catalog(&board).await?;
11
12 let mut count = 0usize;
13 for thread in catalog.threads() {
14 if let Some(att) = &thread.op.attachment {
15 if !att.is_animated() {
16 println!("{}", att.url(&board));
17 count += 1;
18 }
19 }
20 for reply in &thread.last_replies {
21 if let Some(att) = &reply.attachment {
22 if !att.is_animated() {
23 println!("{}", att.url(&board));
24 count += 1;
25 }
26 }
27 }
28 }
29 eprintln!("/{}/: {} still-image URLs", board, count);
30 Ok(())
31}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}Sourcepub fn with_client(http: Client) -> Self
pub fn with_client(http: Client) -> Self
New client reusing an existing reqwest::Client. Lets callers share
connection pools and proxy/TLS config across multiple APIs.
Sourcepub fn with_api_host(self, host: impl Into<String>) -> Self
pub fn with_api_host(self, host: impl Into<String>) -> Self
Override the API host. Useful for tests or proxies.
Sourcepub async fn get_boards(&self) -> Result<Vec<Board>>
pub async fn get_boards(&self) -> Result<Vec<Board>>
GET /boards.json, return every board the API exposes.
Sourcepub async fn get_board_catalog(&self, board: &str) -> Result<Catalog>
pub async fn get_board_catalog(&self, board: &str) -> Result<Catalog>
GET /{board}/catalog.json, every thread on the board, with up to 5 reply previews
per thread.
Examples found in repository?
7async fn main() -> chan::Result<()> {
8 let board = env::args().nth(1).unwrap_or_else(|| "po".to_string());
9 let client = chan::Client::new();
10 let catalog = client.get_board_catalog(&board).await?;
11
12 let mut count = 0usize;
13 for thread in catalog.threads() {
14 if let Some(att) = &thread.op.attachment {
15 if !att.is_animated() {
16 println!("{}", att.url(&board));
17 count += 1;
18 }
19 }
20 for reply in &thread.last_replies {
21 if let Some(att) = &reply.attachment {
22 if !att.is_animated() {
23 println!("{}", att.url(&board));
24 count += 1;
25 }
26 }
27 }
28 }
29 eprintln!("/{}/: {} still-image URLs", board, count);
30 Ok(())
31}Sourcepub async fn get_threads(&self, board: &str) -> Result<ThreadList>
pub async fn get_threads(&self, board: &str) -> Result<ThreadList>
GET /{board}/threads.json, a lightweight per-thread last_modified summary
Sourcepub async fn get_archive(&self, board: &str) -> Result<Archive>
pub async fn get_archive(&self, board: &str) -> Result<Archive>
GET /{board}/archive.json, archived OP numbers, oldest first. Errors with
Error::NotFound on boards that have no archive.
Sourcepub async fn get_index_page(&self, board: &str, page: u8) -> Result<IndexPage>
pub async fn get_index_page(&self, board: &str, page: u8) -> Result<IndexPage>
GET /{board}/{page}.json, one index page (1.=15) with full thread previews.
Sourcepub async fn get_full_thread(
&self,
board: &str,
thread_no: u64,
) -> Result<Thread>
pub async fn get_full_thread( &self, board: &str, thread_no: u64, ) -> Result<Thread>
GET /{board}/thread/{no}.json, every post in a thread.
pub async fn get_threads_if_modified( &self, board: &str, since: Option<&str>, ) -> Result<Option<Conditional<ThreadList>>>
pub async fn get_catalog_if_modified( &self, board: &str, since: Option<&str>, ) -> Result<Option<Conditional<Catalog>>>
Sourcepub async fn get_full_thread_if_modified(
&self,
board: &str,
thread_no: u64,
since: Option<&str>,
) -> Result<Option<Conditional<Thread>>>
pub async fn get_full_thread_if_modified( &self, board: &str, thread_no: u64, since: Option<&str>, ) -> Result<Option<Conditional<Thread>>>
Examples found in repository?
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}