From 055a7e0d9f91c8614631db5d5123ff8df22a2464 Mon Sep 17 00:00:00 2001 From: trivernis Date: Tue, 23 Aug 2022 16:34:28 +0200 Subject: [PATCH] Add tweet imports Signed-off-by: trivernis --- Cargo.lock | 282 +++++++++++++++--- Cargo.toml | 1 + src/args.rs | 8 +- src/config.rs | 8 +- src/error.rs | 18 ++ src/main.rs | 26 +- src/operations/find_and_send_twitter_posts.rs | 37 +++ src/operations/mod.rs | 1 + src/utils/mod.rs | 1 + src/utils/twitter.rs | 60 ++++ 10 files changed, 399 insertions(+), 43 deletions(-) create mode 100644 src/operations/find_and_send_twitter_posts.rs create mode 100644 src/utils/twitter.rs diff --git a/Cargo.lock b/Cargo.lock index f1a0d53..035db4d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -128,6 +128,15 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + [[package]] name = "block-buffer" version = "0.10.2" @@ -194,6 +203,7 @@ dependencies = [ "js-sys", "num-integer", "num-traits", + "serde", "time", "wasm-bindgen", "winapi 0.3.9", @@ -293,6 +303,12 @@ dependencies = [ "yaml-rust", ] +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + [[package]] name = "cookie" version = "0.12.0" @@ -413,13 +429,45 @@ dependencies = [ "typenum", ] +[[package]] +name = "crypto-mac" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" +dependencies = [ + "generic-array", + "subtle", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version 0.4.0", + "syn", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + [[package]] name = "digest" version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506" dependencies = [ - "block-buffer", + "block-buffer 0.10.2", "crypto-common", ] @@ -455,6 +503,33 @@ version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56899898ce76aaf4a0f24d914c97ea6ed976d42fec6ad33fcbb0a1103e07b2b0" +[[package]] +name = "egg-mode" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb96b3df2a1dd9fe043e58eed7ba1c835a23561e777e243e8cc08aa4178aec79" +dependencies = [ + "base64 0.13.0", + "chrono", + "derive_more", + "futures 0.3.23", + "hmac", + "hyper 0.14.20", + "hyper-tls 0.5.0", + "lazy_static", + "mime", + "native-tls", + "percent-encoding 2.1.0", + "rand 0.8.5", + "regex", + "serde", + "serde_json", + "sha-1 0.9.8", + "thiserror", + "tokio 1.20.1", + "url 2.2.2", +] + [[package]] name = "either" version = "1.7.0" @@ -580,20 +655,36 @@ version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678" +[[package]] +name = "futures" +version = "0.3.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab30e97ab6aacfe635fad58f22c2bb06c8b685f7421eb1e064a729e2a5f481fa" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + [[package]] name = "futures-channel" -version = "0.3.21" +version = "0.3.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010" +checksum = "2bfc52cbddcfd745bf1740338492bb0bd83d76c67b445f91c5fb29fae29ecaa1" dependencies = [ "futures-core", + "futures-sink", ] [[package]] name = "futures-core" -version = "0.3.21" +version = "0.3.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3" +checksum = "d2acedae88d38235936c3922476b10fced7b2b68136f5e3c03c2d5be348a1115" [[package]] name = "futures-cpupool" @@ -601,32 +692,66 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4" dependencies = [ - "futures", + "futures 0.1.31", "num_cpus", ] +[[package]] +name = "futures-executor" +version = "0.3.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d11aa21b5b587a64682c0094c2bdd4df0076c5324961a40cc3abd7f37930528" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93a66fc6d035a26a3ae255a6d2bca35eda63ae4c5512bef54449113f7a1228e5" + +[[package]] +name = "futures-macro" +version = "0.3.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0db9cce532b0eae2ccf2766ab246f114b56b9cf6d445e00c2549fbc100ca045d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "futures-sink" -version = "0.3.21" +version = "0.3.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868" +checksum = "ca0bae1fe9752cf7fd9b0064c674ae63f97b37bc714d745cbde0afb7ec4e6765" [[package]] name = "futures-task" -version = "0.3.21" +version = "0.3.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a" +checksum = "842fc63b931f4056a24d59de13fb1272134ce261816e063e634ad0c15cdc5306" [[package]] name = "futures-util" -version = "0.3.21" +version = "0.3.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a" +checksum = "f0828a5471e340229c11c77ca80017937ce3c58cb788a17e5f1c2d5c485a9577" dependencies = [ + "futures-channel", "futures-core", + "futures-io", + "futures-macro", + "futures-sink", "futures-task", + "memchr", "pin-project-lite", "pin-utils", + "slab", ] [[package]] @@ -665,7 +790,7 @@ dependencies = [ "byteorder", "bytes 0.4.12", "fnv", - "futures", + "futures 0.1.31", "http 0.1.21", "indexmap", "log", @@ -717,6 +842,16 @@ dependencies = [ "libc", ] +[[package]] +name = "hmac" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b" +dependencies = [ + "crypto-mac", + "digest 0.9.0", +] + [[package]] name = "http" version = "0.1.21" @@ -746,7 +881,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6741c859c1b2463a423a1dbce98d418e6c3c3fc720fb0d45528657320920292d" dependencies = [ "bytes 0.4.12", - "futures", + "futures 0.1.31", "http 0.1.21", "tokio-buf", ] @@ -799,6 +934,7 @@ dependencies = [ "color-eyre", "config", "directories", + "egg-mode", "hydrus-api", "pixiv-rs", "reqwest 0.11.11", @@ -819,7 +955,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c843caf6296fc1f93444735205af9ed4e109a539005abb2564ae1d6fad34c52" dependencies = [ "bytes 0.4.12", - "futures", + "futures 0.1.31", "futures-cpupool", "h2 0.1.26", "http 0.1.21", @@ -829,7 +965,7 @@ dependencies = [ "itoa 0.4.8", "log", "net2", - "rustc_version", + "rustc_version 0.2.3", "time", "tokio 0.1.22", "tokio-buf", @@ -873,7 +1009,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a800d6aa50af4b5850b2b0f659625ce9504df908e9733b635720483be26174f" dependencies = [ "bytes 0.4.12", - "futures", + "futures 0.1.31", "hyper 0.12.36", "native-tls", "tokio-io", @@ -1238,6 +1374,12 @@ version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1" +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + [[package]] name = "openssl" version = "0.10.41" @@ -1313,7 +1455,7 @@ checksum = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252" dependencies = [ "lock_api", "parking_lot_core", - "rustc_version", + "rustc_version 0.2.3", ] [[package]] @@ -1326,7 +1468,7 @@ dependencies = [ "cloudabi", "libc", "redox_syscall 0.1.57", - "rustc_version", + "rustc_version 0.2.3", "smallvec 0.6.14", "winapi 0.3.9", ] @@ -1390,7 +1532,7 @@ checksum = "1538eb784f07615c6d9a8ab061089c6c54a344c5b4301db51990ca1c241e8c04" dependencies = [ "once_cell", "pest", - "sha-1", + "sha-1 0.10.0", ] [[package]] @@ -1422,6 +1564,12 @@ version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" +[[package]] +name = "ppv-lite86" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" + [[package]] name = "proc-macro-error" version = "1.0.4" @@ -1495,7 +1643,7 @@ checksum = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" dependencies = [ "autocfg 0.1.8", "libc", - "rand_chacha", + "rand_chacha 0.1.1", "rand_core 0.4.2", "rand_hc", "rand_isaac", @@ -1506,6 +1654,17 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.3", +] + [[package]] name = "rand_chacha" version = "0.1.1" @@ -1516,6 +1675,16 @@ dependencies = [ "rand_core 0.3.1", ] +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.3", +] + [[package]] name = "rand_core" version = "0.3.1" @@ -1531,6 +1700,15 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" +[[package]] +name = "rand_core" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +dependencies = [ + "getrandom", +] + [[package]] name = "rand_hc" version = "0.1.0" @@ -1675,7 +1853,7 @@ dependencies = [ "cookie_store", "encoding_rs", "flate2", - "futures", + "futures 0.1.31", "http 0.1.21", "hyper 0.12.36", "hyper-tls 0.3.2", @@ -1767,7 +1945,16 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" dependencies = [ - "semver", + "semver 0.9.0", +] + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver 1.0.13", ] [[package]] @@ -1837,6 +2024,12 @@ dependencies = [ "semver-parser", ] +[[package]] +name = "semver" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93f6841e709003d68bb2deee8c343572bf446003ec20a583e76f7b15cebf3711" + [[package]] name = "semver-parser" version = "0.7.0" @@ -1898,6 +2091,19 @@ dependencies = [ "serde", ] +[[package]] +name = "sha-1" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if 1.0.0", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", +] + [[package]] name = "sha-1" version = "0.10.0" @@ -1906,7 +2112,7 @@ checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f" dependencies = [ "cfg-if 1.0.0", "cpufeatures", - "digest", + "digest 0.10.3", ] [[package]] @@ -1967,6 +2173,12 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "subtle" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" + [[package]] name = "syn" version = "1.0.99" @@ -2091,7 +2303,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a09c0b5bb588872ab2f09afa13ee6e9dac11e10a0ec9e8e3ba39a5a5d530af6" dependencies = [ "bytes 0.4.12", - "futures", + "futures 0.1.31", "mio 0.6.23", "num_cpus", "tokio-current-thread", @@ -2129,7 +2341,7 @@ checksum = "8fb220f46c53859a4b7ec083e41dec9778ff0b1851c0942b211edb89e0ccdc46" dependencies = [ "bytes 0.4.12", "either", - "futures", + "futures 0.1.31", ] [[package]] @@ -2138,7 +2350,7 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1de0e32a83f131e002238d7ccde18211c0a5397f60cbfffcb112868c2e0e20e" dependencies = [ - "futures", + "futures 0.1.31", "tokio-executor", ] @@ -2149,7 +2361,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb2d1b8f4548dbf5e1f7818512e9c406860678f29c300cdf0ebac72d1a3a1671" dependencies = [ "crossbeam-utils", - "futures", + "futures 0.1.31", ] [[package]] @@ -2159,7 +2371,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57fc868aae093479e3131e3d165c93b1c7474109d13c90ec0dda2a1bbfff0674" dependencies = [ "bytes 0.4.12", - "futures", + "futures 0.1.31", "log", ] @@ -2191,7 +2403,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09bc590ec4ba8ba87652da2068d150dcada2cfa2e07faae270a5e0409aa51351" dependencies = [ "crossbeam-utils", - "futures", + "futures 0.1.31", "lazy_static", "log", "mio 0.6.23", @@ -2210,7 +2422,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "edfe50152bc8164fcc456dab7891fa9bf8beaf01c5ee7e1dd43a397c3cf87dee" dependencies = [ "fnv", - "futures", + "futures 0.1.31", ] [[package]] @@ -2220,7 +2432,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98df18ed66e3b72e742f185882a9e201892407957e45fbff8da17ae7a7c51f72" dependencies = [ "bytes 0.4.12", - "futures", + "futures 0.1.31", "iovec", "mio 0.6.23", "tokio-io", @@ -2236,7 +2448,7 @@ dependencies = [ "crossbeam-deque", "crossbeam-queue", "crossbeam-utils", - "futures", + "futures 0.1.31", "lazy_static", "log", "num_cpus", @@ -2251,7 +2463,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93044f2d313c95ff1cb7809ce9a7a05735b012288a888b62d4434fd58c94f296" dependencies = [ "crossbeam-utils", - "futures", + "futures 0.1.31", "slab", "tokio-executor", ] @@ -2476,7 +2688,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6395efa4784b027708f7451087e647ec73cc74f5d9bc2e418404248d679a230" dependencies = [ - "futures", + "futures 0.1.31", "log", "try-lock", ] diff --git a/Cargo.toml b/Cargo.toml index 8fc3b40..63ede9f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,7 @@ serde_json = "1.0.83" config = "0.13.2" directories = "4.0.1" color-eyre = "0.6.2" +egg-mode = "0.16.0" [dependencies.tokio] version = "1.20.1" diff --git a/src/args.rs b/src/args.rs index 03a27cd..e9504e9 100644 --- a/src/args.rs +++ b/src/args.rs @@ -21,7 +21,11 @@ pub enum Command { /// Looks up and imports reddit posts #[clap(name = "import-reddit-posts")] - ImportRedditPosts(ImportRedditOptions), + ImportRedditPosts(ImportUrlsOptions), + + /// Looks up and imports tweets + #[clap(name = "import-tweets")] + ImportTweets(ImportUrlsOptions), } #[derive(Parser, Debug, Clone)] @@ -40,7 +44,7 @@ pub struct LookupOptions { } #[derive(Parser, Debug, Clone)] -pub struct ImportRedditOptions { +pub struct ImportUrlsOptions { /// A file containing all urls with each /// url in a separate line #[clap(short, long)] diff --git a/src/config.rs b/src/config.rs index 41e58c6..519239d 100644 --- a/src/config.rs +++ b/src/config.rs @@ -50,9 +50,15 @@ impl Config { Ok(settings.try_deserialize()?) } - /// Returns the saucenao configuratio or panics if nothing is configured + /// Returns the saucenao configuration or panics if nothing is configured pub fn into_saucenao(self) -> SauceNaoConfig { self.saucenao .expect("No saucenao key configured. Please add one to the config file.") } + + /// REturns the twitter api configuration or panics if nothing is configured + pub fn into_twitter_cfg(self) -> TwitterConfig { + self.twitter + .expect("No twitter api credentials configured. Please add them to the config file.") + } } diff --git a/src/error.rs b/src/error.rs index 68ddcaf..0186273 100644 --- a/src/error.rs +++ b/src/error.rs @@ -24,6 +24,12 @@ pub enum Error { #[error("Error in config {0}")] Config(#[from] config::ConfigError), + + #[error(transparent)] + Twitter(#[from] egg_mode::error::Error), + + #[error("{0}")] + String(String), } impl From for Error { @@ -31,3 +37,15 @@ impl From for Error { Self::RustNao(e.to_string()) } } + +impl From for Error { + fn from(s: String) -> Self { + Self::String(s) + } +} + +impl From<&str> for Error { + fn from(s: &str) -> Self { + Self::String(s.to_string()) + } +} diff --git a/src/main.rs b/src/main.rs index d9eae9b..827b4ca 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,6 +6,7 @@ pub mod utils; use crate::config::Config; use crate::config::SauceNaoConfig; +use crate::config::TwitterConfig; use crate::error::Result; use crate::operations::find_and_send_tags::find_and_send_tags; use crate::operations::find_and_send_urls::find_and_send_urls; @@ -15,6 +16,7 @@ use hydrus_api::wrapper::service::ServiceName; use hydrus_api::wrapper::tag::Tag; use hydrus_api::{Client, Hydrus}; use operations::find_and_send_reddit_posts::find_and_send_reddit_posts; +use operations::find_and_send_twitter_posts::find_and_send_twitter_posts; use pixiv_rs::PixivClient; use rustnao::{Handler, HandlerBuilder}; use std::str::FromStr; @@ -42,6 +44,7 @@ async fn main() { send_tags_or_urls(opt, config.into_saucenao(), hydrus, false).await } Command::ImportRedditPosts(opt) => import_reddit_posts(opt, hydrus).await, + Command::ImportTweets(opt) => import_tweets(opt, config.into_twitter_cfg(), hydrus).await, } .expect("Failed to send tags or urls"); } @@ -113,9 +116,23 @@ async fn send_tags_or_urls( } #[tracing::instrument(level = "debug", skip(hydrus))] -async fn import_reddit_posts(opt: ImportRedditOptions, hydrus: Hydrus) -> Result<()> { - let mut urls = Vec::new(); +async fn import_reddit_posts(opt: ImportUrlsOptions, hydrus: Hydrus) -> Result<()> { + let urls = get_urls_from_args(opt).await?; + find_and_send_reddit_posts(&hydrus, urls).await +} +#[tracing::instrument(level = "debug", skip(hydrus))] +async fn import_tweets( + opt: ImportUrlsOptions, + twitter_cfg: TwitterConfig, + hydrus: Hydrus, +) -> Result<()> { + let urls = get_urls_from_args(opt).await?; + find_and_send_twitter_posts(&hydrus, twitter_cfg, urls).await +} + +async fn get_urls_from_args(opt: ImportUrlsOptions) -> Result> { + let mut urls = Vec::new(); if let Some(input_file) = opt.input { let file = File::open(input_file).await?; let reader = BufReader::new(file); @@ -129,8 +146,7 @@ async fn import_reddit_posts(opt: ImportRedditOptions, hydrus: Hydrus) -> Result } else if let Some(args_urls) = opt.urls { urls = args_urls; } else { - panic!("No reddit post urls provided"); + panic!("No urls provided"); } - - find_and_send_reddit_posts(&hydrus, urls).await + Ok(urls) } diff --git a/src/operations/find_and_send_twitter_posts.rs b/src/operations/find_and_send_twitter_posts.rs new file mode 100644 index 0000000..f5a7070 --- /dev/null +++ b/src/operations/find_and_send_twitter_posts.rs @@ -0,0 +1,37 @@ +use egg_mode::Token; +use hydrus_api::Hydrus; + +use crate::config::TwitterConfig; +use crate::error::Result; +use crate::utils::twitter::{get_token, get_tweet_media}; + +#[tracing::instrument(level = "debug", skip(hydrus))] +pub async fn find_and_send_twitter_posts( + hydrus: &Hydrus, + twitter_cfg: TwitterConfig, + post_urls: Vec, +) -> Result<()> { + let token = get_token(twitter_cfg).await?; + let total_posts = post_urls.len(); + + for (index, post) in post_urls.into_iter().enumerate() { + tracing::info!("Importing post {} of {}", index + 1, total_posts); + if let Err(e) = import_post(&post, hydrus, &token).await { + tracing::error!("Failed to import {}: {}", post, e); + } + } + + Ok(()) +} + +#[tracing::instrument(level = "debug", skip(hydrus))] +async fn import_post(post_url: &str, hydrus: &Hydrus, token: &Token) -> Result<()> { + tracing::debug!("Tweet {}", post_url); + let images = get_tweet_media(post_url, token).await?; + tracing::info!("Found {} images for tweet {}", images.len(), post_url); + + for url in images { + hydrus.import().url(url).run().await?; + } + Ok(()) +} diff --git a/src/operations/mod.rs b/src/operations/mod.rs index 6b905c2..a64d8cf 100644 --- a/src/operations/mod.rs +++ b/src/operations/mod.rs @@ -1,3 +1,4 @@ pub mod find_and_send_reddit_posts; pub mod find_and_send_tags; +pub mod find_and_send_twitter_posts; pub mod find_and_send_urls; diff --git a/src/utils/mod.rs b/src/utils/mod.rs index d92311d..4b00887 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -1,5 +1,6 @@ pub mod pixiv; pub mod reddit; +pub mod twitter; use crate::error::Result; use directories::ProjectDirs; use std::{fs, path::PathBuf}; diff --git a/src/utils/twitter.rs b/src/utils/twitter.rs new file mode 100644 index 0000000..9598abf --- /dev/null +++ b/src/utils/twitter.rs @@ -0,0 +1,60 @@ +use std::borrow::Cow; + +use crate::{config::TwitterConfig, error::Result}; +use egg_mode::auth::Token; + +/// Returns the token that +/// can be used for twitter api requests +#[tracing::instrument(level = "debug")] +pub async fn get_token(config: TwitterConfig) -> Result { + let con_token = egg_mode::KeyPair::new( + Cow::from(config.consumer_key), + Cow::from(config.consumer_secret), + ); + let token = egg_mode::auth::bearer_token(&con_token).await?; + + Ok(token) +} + +/// Returns the media urls for a given tweet +#[tracing::instrument(level = "debug", skip(token))] +pub async fn get_tweet_media(url: &str, token: &Token) -> Result> { + let id = get_tweet_id(url)?; + let tweet = egg_mode::tweet::show(id, token).await?.response; + + if let Some(entities) = tweet.extended_entities { + let media = entities.media; + let urls: Vec = media + .into_iter() + .map(|m| { + if let Some(video_info) = m.video_info { + video_info.variants.into_iter().next().unwrap().url + } else { + m.media_url_https + } + }) + .collect(); + Ok(urls) + } else { + Ok(Vec::new()) + } +} + +/// Returns the tweet ID for a given twitter url +#[tracing::instrument(level = "debug")] +fn get_tweet_id(url: &str) -> Result { + let mut url = url; + if let Some((left, _right)) = url.split_once('?') { + url = left; + } + let id = url + .rsplit('/') + .filter(|s| !s.is_empty()) + .next() + .ok_or("No Tweet ID in twitter url")?; + let id = id + .parse::() + .map_err(|_| "Tweet ID cannot be parsed as u64")?; + + Ok(id) +}