commit db06926c3e4d650003df6329f9fcd9de9581931a
parent 19cdd92b7978b60864c5d353050560053cb74371
Author: Shimmy Xu <shimmy.xu@shimmy1996.com>
Date: Sun, 15 Sep 2019 00:29:40 -0400
Implement timeline
Diffstat:
4 files changed, 65 insertions(+), 2 deletions(-)
diff --git a/Cargo.toml b/Cargo.toml
@@ -8,5 +8,6 @@ edition = "2018"
chrono = "~0.4.8"
clap = "~2.33.0"
dirs = "~2.0.2"
+reqwest = "~0.9.20"
rust-ini = "~0.13.0"
-strfmt = "0.1.6"
+strfmt = "~0.1.6"
diff --git a/src/config.rs b/src/config.rs
@@ -8,6 +8,7 @@ pub struct Config {
pub twturl: String,
pub pre_tweet_hook: String,
pub post_tweet_hook: String,
+ pub limit_timeline: i32,
pub following: HashMap<String, String>,
}
@@ -37,6 +38,7 @@ impl Config {
twturl: twtxt_config["twturl"].to_owned(),
pre_tweet_hook: pre_tweet_hook,
post_tweet_hook: post_tweet_hook,
+ limit_timeline: twtxt_config["limit_timeline"].parse::<i32>().unwrap(),
following: following,
}
}
diff --git a/src/main.rs b/src/main.rs
@@ -1,8 +1,9 @@
-use clap::{crate_version, App, Arg, ArgMatches, SubCommand};
+use clap::{crate_version, App, Arg, SubCommand};
use std::path::Path;
mod config;
+mod timeline;
mod tweet;
use crate::config::Config;
@@ -67,6 +68,7 @@ fn main() {
// Parse subcommands.
match command.subcommand() {
("tweet", Some(subcommand)) => tweet::tweet(&config, &subcommand),
+ ("timeline", Some(subcommand)) => timeline::timeline(&config, &subcommand),
_ => {}
}
}
diff --git a/src/timeline.rs b/src/timeline.rs
@@ -0,0 +1,58 @@
+use chrono::{DateTime, FixedOffset, Local, SecondsFormat};
+use clap::ArgMatches;
+use reqwest::Client;
+
+use std::collections::BinaryHeap;
+
+use crate::config::Config;
+
+/// Print timeline from following.
+pub fn timeline(config: &Config, _subcommand: &ArgMatches) {
+ // Store (post_time, nick, content).
+ let mut all_tweets = BinaryHeap::<(DateTime<FixedOffset>, String, String)>::new();
+
+ for (nick, twturl) in config.following.iter() {
+ let tweets = parse_twtxt(twturl);
+ for (post_time, content) in tweets {
+ all_tweets.push((post_time, nick.to_owned(), content));
+ }
+ }
+
+ // Print the most recent tweets.
+ let now = Local::now();
+ for _ in 0..config.limit_timeline {
+ if let Some(tweet) = all_tweets.pop() {
+ println!("{}", format_tweet(&tweet, &now));
+ }
+ }
+}
+
+/// Parses given twtxt url, returns a Vec of (post_time, content).
+fn parse_twtxt(twturl: &str) -> Vec<(DateTime<FixedOffset>, String)> {
+ let client = Client::new();
+ let resp = client.get(twturl).send().unwrap().text().unwrap();
+ let mut tweets = Vec::new();
+
+ for line in resp.lines() {
+ if let Some(seperator_idx) = line.find('\t') {
+ if let Ok(tweet_time) = DateTime::parse_from_rfc3339(&line[..seperator_idx]) {
+ tweets.push((tweet_time, line[seperator_idx + 1..].to_owned()));
+ };
+ };
+ }
+
+ tweets
+}
+
+/// Formats a tweet for display in terminal.
+fn format_tweet(tweet: &(DateTime<FixedOffset>, String, String), now: &DateTime<Local>) -> String {
+ format!(
+ "\n@{} {}\n{}",
+ &tweet.1,
+ &tweet
+ .0
+ .with_timezone(&now.timezone())
+ .to_rfc3339_opts(SecondsFormat::Secs, true),
+ &tweet.2
+ )
+}