1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
mod common;
use egg_mode::{error::Result, tweet};
use std::collections::{HashSet, VecDeque};
#[tokio::main]
async fn main() -> Result<()> {
let c = common::Config::load().await;
//Thread Reconstruction
//
//This is fairly imperfect, but still effective enough for use in a regular client or with
//recent-enough tweets. The idea is that we have an arbitrary tweet and we want to find tweets
//that it replied to, and tweets the user posted that reply to it.
//
//The first part is fairly easy: it's essentially a linked list, where each node traversal is a
//call to tweet::show. Since we're receiving these tweets in reverse-chronological order,
//calling push_front makes sure the order of the eventual thread is properly chronological.
//
//The latter part calls for some creative use of tweet::user_timeline. First we set up the
//timeline itself, indicating that replies should be included in the response. Then, rather
//than calling start() to get the most recent, we use call() directly, saying "give me tweets
//posted since this ID". Since this could include tweets that aren't on the same thread, we
//need to make keep track of the tweet IDs in the thread, so we can make sure the tweet itself
//is a reply to something within the thread. Since the timeline returned by this call is in
//reverse-chronologocal order, we rev() the output page to make sure we're pushing tweets in
//chronological order. If we wanted to fill our thread buffer completely, we could keep calling
//newer() for a certain number of pages or until we've hit our cap, but using the default 20 is
//sufficient for this demo.
//The example post used in this demo is the fourth post in a seven-post thread I
//(@QuietMisdreavus) posted shortly before writing this. You can easily extrapolate this into a
//function that takes u64 as needed.
let start_id: u64 = 773236818921873409;
println!("Let's reconstruct a tweet thread!");
let mut thread = VecDeque::with_capacity(21);
let mut thread_ids = HashSet::new();
let start_tweet = tweet::show(start_id, &c.token).await?;
let thread_user = start_tweet.user.as_ref().unwrap().id;
thread_ids.insert(start_tweet.id);
thread.push_front(start_tweet.response);
for _ in 0usize..10 {
if let Some(id) = thread.front().and_then(|t| t.in_reply_to_status_id) {
let parent = tweet::show(id, &c.token).await?;
thread_ids.insert(parent.id);
thread.push_front(parent.response);
} else {
break;
}
}
let replies = tweet::user_timeline(thread_user, true, false, &c.token);
for tweet in replies
.call(Some(start_id), None)
.await?
.response
.into_iter()
.rev()
{
if let Some(reply_id) = tweet.in_reply_to_status_id {
if thread_ids.contains(&reply_id) {
thread_ids.insert(tweet.id);
thread.push_back(tweet);
}
}
if thread.len() == thread.capacity() {
break;
}
}
for tweet in &thread {
println!("");
if tweet.id == start_id {
println!("-- this is our starting tweet");
}
common::print_tweet(&tweet);
}
Ok(())
}