From 378d97369706226a4ce2f29ae9fae504bf376a9b Mon Sep 17 00:00:00 2001 From: VC Date: Fri, 5 Jan 2024 15:15:37 +0100 Subject: [PATCH] feat: add the ability to rewrite an edited toot --- Cargo.lock | 367 ++++++++++++++++++++++++++++++------------------ Cargo.toml | 4 +- src/lib.rs | 70 +++++++-- src/main.rs | 20 +++ src/mastodon.rs | 26 +++- src/state.rs | 277 +++++++++++++++++++++++++++++++++++- src/twitter.rs | 29 ++++ 7 files changed, 635 insertions(+), 158 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 69f6288..0cf768b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19,13 +19,14 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "ahash" -version = "0.7.7" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a824f2aa7e75a0c98c5a504fceb80649e9c35265d44525b5f94de4771a395cd" +checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01" dependencies = [ - "getrandom", + "cfg-if", "once_cell", "version_check", + "zerocopy", ] [[package]] @@ -37,6 +38,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "allocator-api2" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" + [[package]] name = "android-tzdata" version = "0.1.1" @@ -102,13 +109,13 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.75" +version = "0.1.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdf6721fb0140e4f897002dd086c06f6c27775df19cfe1fccb21181a48fd2c98" +checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.42", + "syn 2.0.48", ] [[package]] @@ -140,9 +147,9 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "base64" -version = "0.21.5" +version = "0.21.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" +checksum = "c79fed4cdb43e993fcdadc7e58a09fd0e3e649c4436fa11da71c9f1f3ee7feb9" [[package]] name = "bitflags" @@ -215,18 +222,18 @@ dependencies = [ [[package]] name = "clap" -version = "4.4.11" +version = "4.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfaff671f6b22ca62406885ece523383b9b64022e341e53e009a62ebc47a45f2" +checksum = "33e92c5c1a78c62968ec57dbc2440366a2d6e5a23faf829970ff1585dc6b18e2" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.4.11" +version = "4.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a216b506622bb1d316cd51328dce24e07bdff4a6128a47c7e7fad11878d5adbb" +checksum = "f4323769dc8a61e2c39ad7dc26f6f2800524691a44d74fe3d1071a5c24db6370" dependencies = [ "anstream", "anstyle", @@ -264,9 +271,9 @@ checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" [[package]] name = "cpufeatures" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" dependencies = [ "libc", ] @@ -338,9 +345,9 @@ dependencies = [ [[package]] name = "fallible-iterator" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" +checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" [[package]] name = "fallible-streaming-iterator" @@ -392,9 +399,9 @@ dependencies = [ [[package]] name = "futures" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0290714b38af9b4a7b094b8a37086d1b4e61f2df9122c3cad2577669145335" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" dependencies = [ "futures-channel", "futures-core", @@ -407,9 +414,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" dependencies = [ "futures-core", "futures-sink", @@ -417,15 +424,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" [[package]] name = "futures-executor" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f4fb8693db0cf099eadcca0efe2a5a22e4550f98ed16aba6c48700da29597bc" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" dependencies = [ "futures-core", "futures-task", @@ -434,38 +441,38 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" [[package]] name = "futures-macro" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.42", + "syn 2.0.48", ] [[package]] name = "futures-sink" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" [[package]] name = "futures-task" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" [[package]] name = "futures-util" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ "futures-channel", "futures-core", @@ -491,9 +498,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" +checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" dependencies = [ "cfg-if", "js-sys", @@ -519,7 +526,7 @@ dependencies = [ "futures-core", "futures-sink", "futures-util", - "http", + "http 0.2.11", "indexmap", "slab", "tokio", @@ -527,28 +534,23 @@ dependencies = [ "tracing", ] -[[package]] -name = "hashbrown" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" -dependencies = [ - "ahash", -] - [[package]] name = "hashbrown" version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +dependencies = [ + "ahash", + "allocator-api2", +] [[package]] name = "hashlink" -version = "0.7.0" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7249a3129cbc1ffccd74857f81464a323a152173cdb134e0fd81bc803b29facf" +checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7" dependencies = [ - "hashbrown 0.11.2", + "hashbrown", ] [[package]] @@ -592,6 +594,17 @@ dependencies = [ "itoa", ] +[[package]] +name = "http" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b32afd38673a8016f7c9ae69e5af41a58f81b1d31689040f2f1959594ce194ea" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + [[package]] name = "http-body" version = "0.4.6" @@ -599,7 +612,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" dependencies = [ "bytes", - "http", + "http 0.2.11", "pin-project-lite", ] @@ -632,7 +645,7 @@ dependencies = [ "futures-core", "futures-util", "h2", - "http", + "http 0.2.11", "http-body", "httparse", "httpdate", @@ -652,11 +665,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" dependencies = [ "futures-util", - "http", + "http 0.2.11", "hyper", - "rustls", + "rustls 0.21.10", "tokio", - "tokio-rustls", + "tokio-rustls 0.24.1", ] [[package]] @@ -674,9 +687,9 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.58" +version = "0.1.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20" +checksum = "b6a67363e2aa4443928ce15e57ebae94fd8949958fd1223c4cfc0cd473ad7539" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -712,7 +725,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" dependencies = [ "equivalent", - "hashbrown 0.14.3", + "hashbrown", ] [[package]] @@ -723,13 +736,13 @@ checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" [[package]] name = "is-terminal" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" +checksum = "0bad00257d07be169d870ab665980b06cdb366d792ad690bf2e76876dc503455" dependencies = [ "hermit-abi", "rustix", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -755,15 +768,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.151" +version = "0.2.152" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" +checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" [[package]] name = "libsqlite3-sys" -version = "0.24.2" +version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "898745e570c7d0453cc1fbc4a701eb6c662ed54e8fec8b7d14be137ebeeb9d14" +checksum = "cf4e226dcd58b4be396f7bd3c20da8fdee2911400705297ba7d2d7cc2c30f716" dependencies = [ "pkg-config", "vcpkg", @@ -793,15 +806,14 @@ checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "megalodon" -version = "0.11.7" +version = "0.11.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b9ee1d70f03dc0a197e6966320449e6ed55d14f74b0ea64ae1dffce9ce6d236" +checksum = "dc8942e3eaac5e7d0601819dbff08a751377ca5a9e6389436da7a16046dbbcb4" dependencies = [ "async-trait", "chrono", "futures-util", "hex", - "http", "log", "oauth2", "rand", @@ -821,9 +833,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.6.4" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" [[package]] name = "mime" @@ -944,7 +956,7 @@ dependencies = [ "base64 0.13.1", "chrono", "getrandom", - "http", + "http 0.2.11", "rand", "reqwest", "serde", @@ -957,9 +969,9 @@ dependencies = [ [[package]] name = "object" -version = "0.32.1" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" dependencies = [ "memchr", ] @@ -972,7 +984,7 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "oolatoocs" -version = "1.5.4" +version = "2.0.0" dependencies = [ "chrono", "clap", @@ -992,9 +1004,9 @@ dependencies = [ [[package]] name = "openssl" -version = "0.10.61" +version = "0.10.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b8419dc8cc6d866deb801274bba2e6f8f6108c1bb7fcc10ee5ab864931dbb45" +checksum = "8cde4d2d9200ad5909f8dac647e29482e07c3a35de8a13fce7c9c7747ad9f671" dependencies = [ "bitflags 2.4.1", "cfg-if", @@ -1013,7 +1025,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.42", + "syn 2.0.48", ] [[package]] @@ -1024,9 +1036,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.97" +version = "0.9.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3eaad34cdd97d81de97964fc7f29e2d104f483840d906ef56daa1912338460b" +checksum = "c1665caf8ab2dc9aef43d1c0023bd904633a6a05cb30b0ad59bec2ae986e57a7" dependencies = [ "cc", "libc", @@ -1123,18 +1135,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.71" +version = "1.0.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75cb1540fadbd5b8fbccc4dddad2734eba435053f725621c070711a14bb5f4b8" +checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.33" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", ] @@ -1213,13 +1225,13 @@ version = "0.11.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37b1ae8d9ac08420c66222fb9096fc5de435c3c48542bc5336c51892cffafb41" dependencies = [ - "base64 0.21.5", + "base64 0.21.6", "bytes", "encoding_rs", "futures-core", "futures-util", "h2", - "http", + "http 0.2.11", "http-body", "hyper", "hyper-rustls", @@ -1233,15 +1245,15 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rustls", - "rustls-pemfile", + "rustls 0.21.10", + "rustls-pemfile 1.0.4", "serde", "serde_json", "serde_urlencoded", "system-configuration", "tokio", "tokio-native-tls", - "tokio-rustls", + "tokio-rustls 0.24.1", "tokio-util", "tower-service", "url", @@ -1269,16 +1281,16 @@ dependencies = [ [[package]] name = "rusqlite" -version = "0.27.0" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85127183a999f7db96d1a976a309eebbfb6ea3b0b400ddd8340190129de6eb7a" +checksum = "a78046161564f5e7cd9008aff3b2990b3850dc8e0349119b98e8f251e099f24d" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.4.1", + "chrono", "fallible-iterator", "fallible-streaming-iterator", "hashlink", "libsqlite3-sys", - "memchr", "smallvec", ] @@ -1309,18 +1321,33 @@ checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba" dependencies = [ "log", "ring", - "rustls-webpki", + "rustls-webpki 0.101.7", "sct", ] [[package]] -name = "rustls-native-certs" -version = "0.6.3" +name = "rustls" +version = "0.22.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" +checksum = "e87c9956bd9807afa1f77e0f7594af32566e830e088a5576d27c5b6f30f49d41" +dependencies = [ + "log", + "ring", + "rustls-pki-types", + "rustls-webpki 0.102.1", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-native-certs" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1fb85efa936c42c6d5fc28d2629bb51e4b2f4b8a5211e297d599cc5a093792" dependencies = [ "openssl-probe", - "rustls-pemfile", + "rustls-pemfile 2.0.0", + "rustls-pki-types", "schannel", "security-framework", ] @@ -1331,9 +1358,25 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" dependencies = [ - "base64 0.21.5", + "base64 0.21.6", ] +[[package]] +name = "rustls-pemfile" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35e4980fa29e4c4b212ffb3db068a564cbf560e51d3944b7c88bd8bf5bec64f4" +dependencies = [ + "base64 0.21.6", + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e9d979b3ce68192e42760c7810125eb6cf2ea10efae545a156063e61f314e2a" + [[package]] name = "rustls-webpki" version = "0.101.7" @@ -1344,6 +1387,17 @@ dependencies = [ "untrusted", ] +[[package]] +name = "rustls-webpki" +version = "0.102.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef4ca26037c909dedb327b48c3327d0ba91d3dd3c4e05dad328f210ffb68e95b" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + [[package]] name = "ryu" version = "1.0.16" @@ -1352,11 +1406,11 @@ checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" [[package]] name = "schannel" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" +checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -1400,29 +1454,29 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.193" +version = "1.0.195" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" +checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.193" +version = "1.0.195" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" +checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.42", + "syn 2.0.48", ] [[package]] name = "serde_json" -version = "1.0.108" +version = "1.0.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" +checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4" dependencies = [ "itoa", "ryu", @@ -1431,9 +1485,9 @@ dependencies = [ [[package]] name = "serde_path_to_error" -version = "0.1.14" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4beec8bce849d58d06238cb50db2e1c417cfeafa4c63f692b15c82b7c80f8335" +checksum = "ebd154a240de39fdebcf5775d2675c204d7c13cf39a4c697be6493c8e734337c" dependencies = [ "itoa", "serde", @@ -1558,9 +1612,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.42" +version = "2.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b7d0a2c048d661a1a59fcd7355baa232f7ed34e0ee4df2eef3c1c1c0d3852d8" +checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" dependencies = [ "proc-macro2", "quote", @@ -1590,15 +1644,15 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.8.1" +version = "3.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5" +checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa" dependencies = [ "cfg-if", "fastrand", "redox_syscall", "rustix", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -1612,22 +1666,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.51" +version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f11c217e1416d6f036b870f14e0413d480dbf28edbee1f877abaf0206af43bb7" +checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.51" +version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01742297787513b79cf8e29d1056ede1313e2420b7b3b15d0a768b4921f549df" +checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" dependencies = [ "proc-macro2", "quote", - "syn 2.0.42", + "syn 2.0.48", ] [[package]] @@ -1672,7 +1726,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.42", + "syn 2.0.48", ] [[package]] @@ -1691,22 +1745,34 @@ version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" dependencies = [ - "rustls", + "rustls 0.21.10", + "tokio", +] + +[[package]] +name = "tokio-rustls" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f" +dependencies = [ + "rustls 0.22.2", + "rustls-pki-types", "tokio", ] [[package]] name = "tokio-tungstenite" -version = "0.20.1" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "212d5dcb2a1ce06d81107c3d0ffa3121fe974b73f068c8282cb1c32328113b6c" +checksum = "c83b561d025642014097b66e6c1bb422783339e0909e4429cde4749d1990bc38" dependencies = [ "futures-util", "log", - "rustls", + "rustls 0.22.2", "rustls-native-certs", + "rustls-pki-types", "tokio", - "tokio-rustls", + "tokio-rustls 0.25.0", "tungstenite", ] @@ -1802,18 +1868,19 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "tungstenite" -version = "0.20.1" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e3dac10fd62eaf6617d3a904ae222845979aec67c615d1c842b4002c7666fb9" +checksum = "9ef1a641ea34f399a848dea702823bbecfb4c486f911735368f1f137cb8257e1" dependencies = [ "byteorder", "bytes", "data-encoding", - "http", + "http 1.0.0", "httparse", "log", "rand", - "rustls", + "rustls 0.22.2", + "rustls-pki-types", "sha1", "thiserror", "url", @@ -1955,7 +2022,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.42", + "syn 2.0.48", "wasm-bindgen-shared", ] @@ -1989,7 +2056,7 @@ checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" dependencies = [ "proc-macro2", "quote", - "syn 2.0.42", + "syn 2.0.48", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2062,11 +2129,11 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-core" -version = "0.51.1" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.48.5", + "windows-targets 0.52.0", ] [[package]] @@ -2203,9 +2270,9 @@ checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" [[package]] name = "winnow" -version = "0.5.30" +version = "0.5.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b5c3db89721d50d0e2a673f5043fc4722f76dcc352d7b1ab8b8288bed4ed2c5" +checksum = "b7520bbdec7211caa7c4e682eb1fbe07abe20cee6756b6e00f537c82c11816aa" dependencies = [ "memchr", ] @@ -2219,3 +2286,29 @@ dependencies = [ "cfg-if", "windows-sys 0.48.0", ] + +[[package]] +name = "zerocopy" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "zeroize" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" diff --git a/Cargo.toml b/Cargo.toml index 726b667..2d9b15e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "oolatoocs" -version = "1.5.4" +version = "2.0.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -16,7 +16,7 @@ megalodon = "^0.11" oauth1-request = "^0.6" regex = "^1.10" reqwest = { version = "^0.11", features = ["json", "stream", "multipart"] } -rusqlite = "^0.27" +rusqlite = { version = "^0.30", features = ["chrono"] } serde = { version = "^1.0", features = ["derive"] } tokio = { version = "^1.33", features = ["rt-multi-thread", "macros", "time"] } toml = "^0.8" diff --git a/src/lib.rs b/src/lib.rs index ca73d0c..725c667 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,20 +5,20 @@ mod config; pub use config::{parse_toml, Config}; mod state; -pub use state::init_db; #[allow(unused_imports)] -use state::{read_state, write_state, TweetToToot}; +use state::{delete_state, read_all_tweet_state, read_state, write_state, TweetToToot}; +pub use state::{init_db, migrate_db}; mod mastodon; -use mastodon::get_mastodon_timeline_since; pub use mastodon::register; +use mastodon::{get_mastodon_instance, get_mastodon_timeline_since, get_status_edited_at}; mod utils; use utils::{generate_multi_tweets, strip_everything}; mod twitter; #[allow(unused_imports)] -use twitter::{generate_media_ids, post_tweet, transform_poll}; +use twitter::{delete_tweet, generate_media_ids, post_tweet, transform_poll}; use rusqlite::Connection; @@ -27,11 +27,51 @@ pub async fn run(config: &Config) { let conn = Connection::open(&config.oolatoocs.db_path) .unwrap_or_else(|e| panic!("Cannot open DB: {}", e)); - let last_toot_id = read_state(&conn, None) - .unwrap_or_else(|e| panic!("Cannot get last toot id: {}", e)) - .map(|r| r.toot_id); + let mastodon = get_mastodon_instance(&config.mastodon); - let timeline = get_mastodon_timeline_since(&config.mastodon, last_toot_id) + let last_entry = + read_state(&conn, None).unwrap_or_else(|e| panic!("Cannot get last toot id: {}", e)); + + let last_toot_id: Option = match last_entry { + None => None, // Does not exist, this is the same as previously + Some(t) => { + match get_status_edited_at(&mastodon, t.toot_id).await { + None => Some(t.toot_id), + Some(d) => { + // a date has been found + if d > t.datetime.unwrap() { + // said date is posterior to the previously + // written tweet, we need to delete/rewrite + for local_tweet_id in read_all_tweet_state(&conn, t.toot_id) + .unwrap_or_else(|e| { + panic!( + "Cannot fetch all tweets associated with Toot ID {}: {}", + t.toot_id, e + ) + }) + .into_iter() + { + delete_tweet(&config.twitter, local_tweet_id) + .await + .unwrap_or_else(|e| { + panic!("Cannot delete Tweet ID ({}): {}", t.tweet_id, e) + }); + } + delete_state(&conn, t.toot_id).unwrap_or_else(|e| { + panic!("Cannot delete Toot ID ({}): {}", t.toot_id, e) + }); + read_state(&conn, None) + .unwrap_or_else(|e| panic!("Cannot get last toot id: {}", e)) + .map(|a| a.toot_id) + } else { + Some(t.toot_id) + } + } + } + } + }; + + let timeline = get_mastodon_timeline_since(&mastodon, last_toot_id) .await .unwrap_or_else(|e| panic!("Cannot get instance: {}", e)); @@ -57,9 +97,22 @@ pub async fn run(config: &Config) { // if the toot is too long, we cut it in half here if let Some((first_half, second_half)) = generate_multi_tweets(&tweet_content) { tweet_content = second_half; + // post the first half let reply_id = post_tweet(&config.twitter, first_half, vec![], reply_to, None) .await .unwrap_or_else(|e| panic!("Cannot post the first half of {}: {}", &toot.id, e)); + // write it to db + write_state( + &conn, + TweetToToot { + tweet_id: reply_id, + toot_id: toot.id.parse::().unwrap(), + datetime: None, + }, + ) + .unwrap_or_else(|e| { + panic!("Cannot store Toot/Tweet ({}/{}): {}", &toot.id, reply_id, e) + }); reply_to = Some(reply_id); }; @@ -80,6 +133,7 @@ pub async fn run(config: &Config) { TweetToToot { tweet_id, toot_id: toot.id.parse::().unwrap(), + datetime: None, }, ) .unwrap_or_else(|e| panic!("Cannot store Toot/Tweet ({}/{}): {}", &toot.id, tweet_id, e)); diff --git a/src/main.rs b/src/main.rs index bb4223e..b3c77a2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -49,6 +49,21 @@ fn main() { .display_order(1), ), ) + .subcommand( + Command::new("migrate") + .version(env!("CARGO_PKG_VERSION")) + .about("Command to register to Mastodon Instance") + .arg( + Arg::new("config") + .short('c') + .long("config") + .value_name("CONFIG_FILE") + .help(format!("TOML config file for {}", env!("CARGO_PKG_NAME"))) + .num_args(1) + .default_value(DEFAULT_CONFIG_PATH) + .display_order(1), + ), + ) .get_matches(); env_logger::init(); @@ -63,6 +78,11 @@ fn main() { register(sub_m.get_one::("host").unwrap()); return; } + Some(("migrate", sub_m)) => { + let config = parse_toml(sub_m.get_one::("config").unwrap()); + migrate_db(&config.oolatoocs.db_path).unwrap(); + return; + } _ => (), } diff --git a/src/mastodon.rs b/src/mastodon.rs index bab701c..c6c94ed 100644 --- a/src/mastodon.rs +++ b/src/mastodon.rs @@ -1,4 +1,5 @@ use crate::config::MastodonConfig; +use chrono::{DateTime, Utc}; use megalodon::{ entities::{Status, StatusVisibility}, generator, @@ -10,16 +11,29 @@ use megalodon::{ use std::error::Error; use std::io::stdin; -pub async fn get_mastodon_timeline_since( - config: &MastodonConfig, - id: Option, -) -> Result, Box> { - let mastodon = Mastodon::new( +/// Get Mastodon Object instance +pub fn get_mastodon_instance(config: &MastodonConfig) -> Mastodon { + Mastodon::new( config.base.to_string(), Some(config.token.to_string()), None, - ); + ) +} +/// Get the edited_at field from the specified toot +pub async fn get_status_edited_at(mastodon: &Mastodon, t: u64) -> Option> { + mastodon + .get_status(t.to_string()) + .await + .ok() + .and_then(|t| t.json.edited_at) +} + +/// Get the home timeline since the last toot +pub async fn get_mastodon_timeline_since( + mastodon: &Mastodon, + id: Option, +) -> Result, Box> { let input_options = GetHomeTimelineInputOptions { only_media: Some(false), limit: None, diff --git a/src/state.rs b/src/state.rs index 43d1de1..9cafc3f 100644 --- a/src/state.rs +++ b/src/state.rs @@ -1,3 +1,4 @@ +use chrono::{DateTime, Utc}; use log::debug; use rusqlite::{params, Connection, OptionalExtension}; use std::error::Error; @@ -7,6 +8,34 @@ use std::error::Error; pub struct TweetToToot { pub tweet_id: u64, pub toot_id: u64, + pub datetime: Option>, +} + +/// Deletes a given state +pub fn delete_state(conn: &Connection, toot_id: u64) -> Result<(), Box> { + debug!("Deleting Toot ID {}", toot_id); + conn.execute( + &format!("DELETE FROM tweet_to_toot WHERE toot_id = {}", toot_id), + [], + )?; + Ok(()) +} + +/// Retrieves all tweets associated to a toot in the form of a vector +pub fn read_all_tweet_state(conn: &Connection, toot_id: u64) -> Result, Box> { + let query = format!( + "SELECT tweet_id FROM tweet_to_toot WHERE toot_id = {};", + toot_id + ); + let mut stmt = conn.prepare(&query)?; + let mut rows = stmt.query([])?; + + let mut v = Vec::new(); + while let Some(row) = rows.next()? { + v.push(row.get(0)?); + } + + Ok(v) } /// if None is passed, read the last tweet from DB @@ -17,8 +46,10 @@ pub fn read_state( ) -> Result, Box> { debug!("Reading toot_id {:?}", s); let query: String = match s { - Some(i) => format!("SELECT * FROM tweet_to_toot WHERE toot_id = {i}"), - None => "SELECT * FROM tweet_to_toot ORDER BY toot_id DESC LIMIT 1".to_string(), + Some(i) => format!( + "SELECT tweet_id, toot_id, UNIXEPOCH(datetime) AS datetime FROM tweet_to_toot WHERE toot_id = {i} ORDER BY tweet_id DESC LIMIT 1" + ), + None => "SELECT tweet_id, toot_id, UNIXEPOCH(datetime) AS datetime FROM tweet_to_toot ORDER BY toot_id DESC LIMIT 1".to_string(), }; let mut stmt = conn.prepare(&query)?; @@ -28,6 +59,7 @@ pub fn read_state( Ok(TweetToToot { tweet_id: row.get("tweet_id")?, toot_id: row.get("toot_id")?, + datetime: Some(DateTime::from_timestamp(row.get("datetime").unwrap(), 0).unwrap()), }) }) .optional()?; @@ -56,8 +88,9 @@ pub fn init_db(d: &str) -> Result<(), Box> { conn.execute( "CREATE TABLE IF NOT EXISTS tweet_to_toot ( - tweet_id INTEGER, - toot_id INTEGER PRIMARY KEY + tweet_id INTEGER PRIMARY KEY, + toot_id INTEGER, + datetime INTEGER DEFAULT CURRENT_TIMESTAMP )", [], )?; @@ -65,6 +98,56 @@ pub fn init_db(d: &str) -> Result<(), Box> { Ok(()) } +/// Migrate DB from 1.5.x to 1.6.x +pub fn migrate_db(d: &str) -> Result<(), Box> { + debug!("Migration DB for Oolatoocs"); + + let conn = Connection::open(d)?; + + let res = conn.execute("SELECT datetime from tweet_to_toot;", []); + + // If the column can be selected then, it’s OK + // if not, see if the error is a missing column and add it + match res { + Err(e) => match e.to_string().as_str() { + "no such column: datetime" => migrate_db_alter_table(&conn), //column does not exist + "Execute returned results - did you mean to call query?" => Ok(()), // return results, + // column does + // exist + _ => Err(e.into()), + }, + Ok(_) => Ok(()), + } +} + +/// Creates a new table, copy the data from the old table and rename it +fn migrate_db_alter_table(c: &Connection) -> Result<(), Box> { + // create the new table + c.execute( + "CREATE TABLE IF NOT EXISTS tweet_to_toot_new ( + tweet_id INTEGER PRIMARY KEY, + toot_id INTEGER, + datetime INTEGER DEFAULT CURRENT_TIMESTAMP + )", + [], + )?; + + // copy data from the old table + c.execute( + "INSERT INTO tweet_to_toot_new (tweet_id, toot_id) + SELECT tweet_id, toot_id FROM tweet_to_toot;", + [], + )?; + + // drop the old table + c.execute("DROP TABLE tweet_to_toot;", [])?; + + // rename the new table + c.execute("ALTER TABLE tweet_to_toot_new RENAME TO tweet_to_toot;", [])?; + + Ok(()) +} + #[cfg(test)] mod tests { use super::*; @@ -119,17 +202,25 @@ mod tests { let t_in = TweetToToot { tweet_id: 123456789, toot_id: 987654321, + datetime: None, }; write_state(&conn, t_in).unwrap(); - let mut stmt = conn.prepare("SELECT * FROM tweet_to_toot;").unwrap(); + let mut stmt = conn + .prepare( + "SELECT tweet_id, toot_id, UNIXEPOCH(datetime) AS datetime FROM tweet_to_toot;", + ) + .unwrap(); let t_out = stmt .query_row([], |row| { Ok(TweetToToot { tweet_id: row.get("tweet_id").unwrap(), toot_id: row.get("toot_id").unwrap(), + datetime: Some( + DateTime::from_timestamp(row.get("datetime").unwrap(), 0).unwrap(), + ), }) }) .unwrap(); @@ -226,4 +317,180 @@ mod tests { assert_eq!(t_out.tweet_id, 100); assert_eq!(t_out.toot_id, 1000); } + + #[test] + fn test_last_toot_id_read_state() { + let d = "/tmp/test_last_toot_id_read_state.sqlite"; + + init_db(d).unwrap(); + + let conn = Connection::open(d).unwrap(); + + conn.execute( + "INSERT INTO tweet_to_toot(tweet_id, toot_id) + VALUES (100, 1000), (101, 1000);", + [], + ) + .unwrap(); + + let t_out = read_state(&conn, Some(1000)).unwrap().unwrap(); + + remove_file(d).unwrap(); + + assert_eq!(t_out.tweet_id, 101); + assert_eq!(t_out.toot_id, 1000); + } + + #[test] + fn test_migrate_db_alter_table() { + let d = "/tmp/test_migrate_db_alter_table.sqlite"; + + let conn = Connection::open(d).unwrap(); + + init_db(d).unwrap(); + + write_state( + &conn, + TweetToToot { + tweet_id: 0, + toot_id: 0, + datetime: None, + }, + ) + .unwrap(); + write_state( + &conn, + TweetToToot { + tweet_id: 1, + toot_id: 1, + datetime: None, + }, + ) + .unwrap(); + + migrate_db_alter_table(&conn).unwrap(); + + let mut stmt = conn.prepare("PRAGMA table_info(tweet_to_toot);").unwrap(); + let mut t = stmt.query([]).unwrap(); + + while let Some(row) = t.next().unwrap() { + if row.get::(0).unwrap() == 2 { + assert_eq!(row.get::(1).unwrap(), "datetime".to_string()); + } + } + + remove_file(d).unwrap(); + } + + #[test] + fn test_migrate_db() { + // this should be idempotent + let d = "/tmp/test_migrate_db.sqlite"; + + let conn = Connection::open(d).unwrap(); + conn.execute( + "CREATE TABLE IF NOT EXISTS tweet_to_toot ( + tweet_id INTEGER, + toot_id INTEGER PRIMARY KEY + )", + [], + ) + .unwrap(); + + conn.execute("INSERT INTO tweet_to_toot VALUES (0, 0), (1, 1);", []) + .unwrap(); + + migrate_db(d).unwrap(); + + let last_state = read_state(&conn, None).unwrap().unwrap(); + + assert_eq!(last_state.tweet_id, 1); + assert_eq!(last_state.toot_id, 1); + + migrate_db(d).unwrap(); // shouldn’t do anything + + remove_file(d).unwrap(); + } + + #[test] + fn test_delete_state() { + let d = "/tmp/test_delete_state.sqlite"; + + init_db(d).unwrap(); + + let conn = Connection::open(d).unwrap(); + + conn.execute( + "INSERT INTO tweet_to_toot(tweet_id, toot_id) VALUES (0, 0);", + [], + ) + .unwrap(); + + delete_state(&conn, 0).unwrap(); + + let mut stmt = conn + .prepare( + "SELECT tweet_id, toot_id, UNIXEPOCH(datetime) AS datetime FROM tweet_to_toot;", + ) + .unwrap(); + + let t_out = stmt.query_row([], |row| { + Ok(TweetToToot { + tweet_id: row.get("tweet_id").unwrap(), + toot_id: row.get("toot_id").unwrap(), + datetime: Some(DateTime::from_timestamp(row.get("datetime").unwrap(), 0).unwrap()), + }) + }); + + assert!(t_out.is_err_and(|x| x == rusqlite::Error::QueryReturnedNoRows)); + + conn.execute( + "INSERT INTO tweet_to_toot(tweet_id, toot_id) VALUES(102,42), (103,42);", + [], + ) + .unwrap(); + + delete_state(&conn, 42).unwrap(); + + let mut stmt = conn + .prepare( + "SELECT tweet_id, toot_id, UNIXEPOCH(datetime) AS datetime FROM tweet_to_toot;", + ) + .unwrap(); + + let t_out = stmt.query_row([], |row| { + Ok(TweetToToot { + tweet_id: row.get("tweet_id").unwrap(), + toot_id: row.get("toot_id").unwrap(), + datetime: Some(DateTime::from_timestamp(row.get("datetime").unwrap(), 0).unwrap()), + }) + }); + + assert!(t_out.is_err_and(|x| x == rusqlite::Error::QueryReturnedNoRows)); + + remove_file(d).unwrap(); + } + + #[test] + fn test_read_all_tweet_state() { + let d = "/tmp/read_all_tweet_state.sqlite"; + + init_db(d).unwrap(); + + let conn = Connection::open(d).unwrap(); + + conn.execute( + "INSERT INTO tweet_to_toot(tweet_id, toot_id) VALUES (102, 42), (103, 42), (105, 43);", + [], + ) + .unwrap(); + + let v1 = read_all_tweet_state(&conn, 43).unwrap(); + let v2 = read_all_tweet_state(&conn, 42).unwrap(); + + assert_eq!(v1, vec![105]); + assert_eq!(v2, vec![102, 103]); + + remove_file(d).unwrap(); + } } diff --git a/src/twitter.rs b/src/twitter.rs index 2137034..d6b9a9a 100644 --- a/src/twitter.rs +++ b/src/twitter.rs @@ -113,6 +113,35 @@ fn get_token(config: &TwitterConfig) -> Token { ) } +/// This functions deletes a tweet, given its id +pub async fn delete_tweet(config: &TwitterConfig, id: u64) -> Result<(), Box> { + debug!("Deleting Tweet {}", id); + let empty_request = EmptyRequest {}; // Why? Because fuck you, that’s why! + let token = get_token(config); + + let client = Client::new(); + let res = client + .delete(format!("{}/{}", TWITTER_API_TWEET_URL, id)) + .header( + "Authorization", + oauth1_request::delete( + format!("{}/{}", TWITTER_API_TWEET_URL, id), + &empty_request, + &token, + oauth1_request::HMAC_SHA1, + ), + ) + .send() + .await?; + + if !res.status().is_success() { + return Err(OolatoocsError::new(&format!("Cannot delete Tweet {}", id)).into()); + } + + Ok(()) +} + +/// This function generates a media_ids vec to be used by Twitter pub async fn generate_media_ids(config: &TwitterConfig, media_attach: &[Attachment]) -> Vec { let mut medias: Vec = vec![];