mirror of
https://framagit.org/veretcle/scootaloo.git
synced 2025-07-20 17:11:19 +02:00
Still a WIP: need to use async reqwest to respect the global context of usage (reqwest::blocking is using async inside so it does not really sync whatever)
This commit is contained in:
@@ -6,5 +6,7 @@ rust-latest:
|
|||||||
image: rust:latest
|
image: rust:latest
|
||||||
script:
|
script:
|
||||||
- cargo build --verbose
|
- cargo build --verbose
|
||||||
- cargo test --verbose
|
- cargo build --release --verbose
|
||||||
|
- strip target/release/${CI_PROJECT_NAME}
|
||||||
|
- du -h target/release/${CI_PROJECT_NAME}
|
||||||
|
|
||||||
|
339
Cargo.lock
generated
339
Cargo.lock
generated
@@ -94,12 +94,6 @@ version = "0.11.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7"
|
checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "base64"
|
|
||||||
version = "0.12.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "base64"
|
name = "base64"
|
||||||
version = "0.13.0"
|
version = "0.13.0"
|
||||||
@@ -265,6 +259,17 @@ dependencies = [
|
|||||||
"bitflags",
|
"bitflags",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "colored"
|
||||||
|
version = "1.9.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f4ffc801dacf156c5854b9df4f425a626539c3a6ef7893cc0c5084a23f0b6c59"
|
||||||
|
dependencies = [
|
||||||
|
"atty",
|
||||||
|
"lazy_static",
|
||||||
|
"winapi 0.3.8",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cookie"
|
name = "cookie"
|
||||||
version = "0.12.0"
|
version = "0.12.0"
|
||||||
@@ -373,9 +378,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crypto-mac"
|
name = "crypto-mac"
|
||||||
version = "0.8.0"
|
version = "0.10.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab"
|
checksum = "4857fd85a0c34b3c3297875b747c1e02e06b6a0ea32dd892d8192b9ce0813ea6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"generic-array 0.14.4",
|
"generic-array 0.14.4",
|
||||||
"subtle",
|
"subtle",
|
||||||
@@ -431,27 +436,26 @@ checksum = "4358a9e11b9a09cf52383b451b49a169e8d797b68aa02301ff586d70d9661ea3"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "egg-mode"
|
name = "egg-mode"
|
||||||
version = "0.15.0"
|
version = "0.15.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "git+https://github.com/egg-mode-rs/egg-mode?rev=6b81073eba9c3b123ca0e80bdb5ef61d1758f131#6b81073eba9c3b123ca0e80bdb5ef61d1758f131"
|
||||||
checksum = "5f877bc908325f50163ff1670a4733eabf87942511ccfd907fbef4e239c3f8aa"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.12.3",
|
"base64 0.13.0",
|
||||||
"chrono",
|
"chrono",
|
||||||
"derive_more",
|
"derive_more",
|
||||||
"futures 0.3.5",
|
"futures 0.3.5",
|
||||||
"hmac",
|
"hmac",
|
||||||
"hyper 0.13.2",
|
"hyper 0.14.4",
|
||||||
"hyper-tls 0.4.1",
|
"hyper-tls 0.5.0",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"mime",
|
"mime",
|
||||||
"native-tls",
|
"native-tls",
|
||||||
"percent-encoding 2.1.0",
|
"percent-encoding 2.1.0",
|
||||||
"rand 0.7.3",
|
"rand 0.8.3",
|
||||||
"regex",
|
"regex",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sha-1 0.9.4",
|
"sha-1 0.9.4",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"tokio 0.2.25",
|
"tokio 1.3.0",
|
||||||
"url 2.2.1",
|
"url 2.2.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -735,7 +739,18 @@ checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if 0.1.9",
|
"cfg-if 0.1.9",
|
||||||
"libc",
|
"libc",
|
||||||
"wasi",
|
"wasi 0.9.0+wasi-snapshot-preview1",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "getrandom"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if 1.0.0",
|
||||||
|
"libc",
|
||||||
|
"wasi 0.10.2+wasi-snapshot-preview1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -762,25 +777,6 @@ dependencies = [
|
|||||||
"tokio-io",
|
"tokio-io",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "h2"
|
|
||||||
version = "0.2.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b9433d71e471c1736fd5a61b671fc0b148d7a2992f666c958d03cd8feb3b88d1"
|
|
||||||
dependencies = [
|
|
||||||
"bytes 0.5.6",
|
|
||||||
"fnv",
|
|
||||||
"futures-core",
|
|
||||||
"futures-sink",
|
|
||||||
"futures-util",
|
|
||||||
"http 0.2.0",
|
|
||||||
"indexmap",
|
|
||||||
"log",
|
|
||||||
"slab",
|
|
||||||
"tokio 0.2.25",
|
|
||||||
"tokio-util 0.2.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "h2"
|
name = "h2"
|
||||||
version = "0.3.1"
|
version = "0.3.1"
|
||||||
@@ -796,7 +792,7 @@ dependencies = [
|
|||||||
"indexmap",
|
"indexmap",
|
||||||
"slab",
|
"slab",
|
||||||
"tokio 1.3.0",
|
"tokio 1.3.0",
|
||||||
"tokio-util 0.6.4",
|
"tokio-util",
|
||||||
"tracing",
|
"tracing",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -817,9 +813,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hmac"
|
name = "hmac"
|
||||||
version = "0.8.1"
|
version = "0.10.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "126888268dcc288495a26bf004b38c5fdbb31682f992c84ceb046a1f0fe38840"
|
checksum = "c1441c6b1e930e2817404b5046f1f989899143a12bf92de603b69f4e0aee1e15"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crypto-mac",
|
"crypto-mac",
|
||||||
"digest 0.9.0",
|
"digest 0.9.0",
|
||||||
@@ -865,16 +861,6 @@ dependencies = [
|
|||||||
"tokio-buf",
|
"tokio-buf",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "http-body"
|
|
||||||
version = "0.3.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "13d5ff830006f7646652e057693569bfe0d51760c0085a071769d142a205111b"
|
|
||||||
dependencies = [
|
|
||||||
"bytes 0.5.6",
|
|
||||||
"http 0.2.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "http-body"
|
name = "http-body"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
@@ -927,30 +913,6 @@ dependencies = [
|
|||||||
"want 0.2.0",
|
"want 0.2.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "hyper"
|
|
||||||
version = "0.13.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "fa1c527bbc634be72aa7ba31e4e4def9bbb020f5416916279b7c705cd838893e"
|
|
||||||
dependencies = [
|
|
||||||
"bytes 0.5.6",
|
|
||||||
"futures-channel",
|
|
||||||
"futures-core",
|
|
||||||
"futures-util",
|
|
||||||
"h2 0.2.1",
|
|
||||||
"http 0.2.0",
|
|
||||||
"http-body 0.3.1",
|
|
||||||
"httparse",
|
|
||||||
"itoa",
|
|
||||||
"log",
|
|
||||||
"net2",
|
|
||||||
"pin-project 0.4.8",
|
|
||||||
"time",
|
|
||||||
"tokio 0.2.25",
|
|
||||||
"tower-service",
|
|
||||||
"want 0.3.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hyper"
|
name = "hyper"
|
||||||
version = "0.14.4"
|
version = "0.14.4"
|
||||||
@@ -1005,19 +967,6 @@ dependencies = [
|
|||||||
"tokio-io",
|
"tokio-io",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "hyper-tls"
|
|
||||||
version = "0.4.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3adcd308402b9553630734e9c36b77a7e48b3821251ca2493e8cd596763aafaa"
|
|
||||||
dependencies = [
|
|
||||||
"bytes 0.5.6",
|
|
||||||
"hyper 0.13.2",
|
|
||||||
"native-tls",
|
|
||||||
"tokio 0.2.25",
|
|
||||||
"tokio-tls",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hyper-tls"
|
name = "hyper-tls"
|
||||||
version = "0.5.0"
|
version = "0.5.0"
|
||||||
@@ -1072,6 +1021,15 @@ dependencies = [
|
|||||||
"bytes 0.5.6",
|
"bytes 0.5.6",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "instant"
|
||||||
|
version = "0.1.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if 1.0.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "iovec"
|
name = "iovec"
|
||||||
version = "0.1.4"
|
version = "0.1.4"
|
||||||
@@ -1150,6 +1108,15 @@ dependencies = [
|
|||||||
"scopeguard",
|
"scopeguard",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lock_api"
|
||||||
|
version = "0.4.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5a3c91c24eae6777794bb1997ad98bbb87daf92890acab859f7eaa4320333176"
|
||||||
|
dependencies = [
|
||||||
|
"scopeguard",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "log"
|
name = "log"
|
||||||
version = "0.4.8"
|
version = "0.4.8"
|
||||||
@@ -1389,11 +1356,22 @@ version = "0.9.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252"
|
checksum = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"lock_api",
|
"lock_api 0.3.3",
|
||||||
"parking_lot_core",
|
"parking_lot_core 0.6.2",
|
||||||
"rustc_version",
|
"rustc_version",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "parking_lot"
|
||||||
|
version = "0.11.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb"
|
||||||
|
dependencies = [
|
||||||
|
"instant",
|
||||||
|
"lock_api 0.4.3",
|
||||||
|
"parking_lot_core 0.8.2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parking_lot_core"
|
name = "parking_lot_core"
|
||||||
version = "0.6.2"
|
version = "0.6.2"
|
||||||
@@ -1409,6 +1387,20 @@ dependencies = [
|
|||||||
"winapi 0.3.8",
|
"winapi 0.3.8",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "parking_lot_core"
|
||||||
|
version = "0.8.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9ccb628cad4f84851442432c60ad8e1f607e29752d0bf072cbd0baf28aa34272"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if 1.0.0",
|
||||||
|
"instant",
|
||||||
|
"libc",
|
||||||
|
"redox_syscall",
|
||||||
|
"smallvec 1.2.0",
|
||||||
|
"winapi 0.3.8",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "percent-encoding"
|
name = "percent-encoding"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
@@ -1499,12 +1491,6 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "pin-project-lite"
|
|
||||||
version = "0.1.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "237844750cfbb86f67afe27eee600dfbbcb6188d734139b534cbfbf4f96792ae"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pin-project-lite"
|
name = "pin-project-lite"
|
||||||
version = "0.2.6"
|
version = "0.2.6"
|
||||||
@@ -1525,9 +1511,9 @@ checksum = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ppv-lite86"
|
name = "ppv-lite86"
|
||||||
version = "0.2.6"
|
version = "0.2.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b"
|
checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro-hack"
|
name = "proc-macro-hack"
|
||||||
@@ -1619,13 +1605,25 @@ version = "0.7.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
|
checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom",
|
"getrandom 0.1.14",
|
||||||
"libc",
|
"libc",
|
||||||
"rand_chacha 0.2.1",
|
"rand_chacha 0.2.1",
|
||||||
"rand_core 0.5.1",
|
"rand_core 0.5.1",
|
||||||
"rand_hc 0.2.0",
|
"rand_hc 0.2.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand"
|
||||||
|
version = "0.8.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"rand_chacha 0.3.0",
|
||||||
|
"rand_core 0.6.2",
|
||||||
|
"rand_hc 0.3.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rand_chacha"
|
name = "rand_chacha"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
@@ -1646,6 +1644,16 @@ dependencies = [
|
|||||||
"rand_core 0.5.1",
|
"rand_core 0.5.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_chacha"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d"
|
||||||
|
dependencies = [
|
||||||
|
"ppv-lite86",
|
||||||
|
"rand_core 0.6.2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rand_core"
|
name = "rand_core"
|
||||||
version = "0.3.1"
|
version = "0.3.1"
|
||||||
@@ -1667,7 +1675,16 @@ version = "0.5.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
|
checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom",
|
"getrandom 0.1.14",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.6.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom 0.2.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1688,6 +1705,15 @@ dependencies = [
|
|||||||
"rand_core 0.5.1",
|
"rand_core 0.5.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_hc"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73"
|
||||||
|
dependencies = [
|
||||||
|
"rand_core 0.6.2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rand_isaac"
|
name = "rand_isaac"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
@@ -1839,7 +1865,7 @@ dependencies = [
|
|||||||
"mime",
|
"mime",
|
||||||
"native-tls",
|
"native-tls",
|
||||||
"percent-encoding 2.1.0",
|
"percent-encoding 2.1.0",
|
||||||
"pin-project-lite 0.2.6",
|
"pin-project-lite",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_urlencoded 0.7.0",
|
"serde_urlencoded 0.7.0",
|
||||||
"tokio 1.3.0",
|
"tokio 1.3.0",
|
||||||
@@ -1899,16 +1925,17 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "scootaloo"
|
name = "scootaloo"
|
||||||
version = "0.2.1"
|
version = "0.3.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
"egg-mode",
|
"egg-mode",
|
||||||
"elefren",
|
"elefren",
|
||||||
"htmlescape",
|
"htmlescape",
|
||||||
|
"log",
|
||||||
"reqwest 0.11.2",
|
"reqwest 0.11.2",
|
||||||
"serde",
|
"serde",
|
||||||
|
"simple_logger",
|
||||||
"tokio 1.3.0",
|
"tokio 1.3.0",
|
||||||
"tokio-compat-02",
|
|
||||||
"toml",
|
"toml",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -2061,6 +2088,28 @@ dependencies = [
|
|||||||
"opaque-debug 0.3.0",
|
"opaque-debug 0.3.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "signal-hook-registry"
|
||||||
|
version = "1.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "16f1d0fef1604ba8f7a073c7e701f213e056707210e9020af4528e0101ce11a6"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "simple_logger"
|
||||||
|
version = "1.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cd57f17c093ead1d4a1499dc9acaafdd71240908d64775465543b8d9a9f1d198"
|
||||||
|
dependencies = [
|
||||||
|
"atty",
|
||||||
|
"chrono",
|
||||||
|
"colored",
|
||||||
|
"log",
|
||||||
|
"winapi 0.3.8",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "siphasher"
|
name = "siphasher"
|
||||||
version = "0.2.3"
|
version = "0.2.3"
|
||||||
@@ -2257,24 +2306,6 @@ dependencies = [
|
|||||||
"tokio-timer",
|
"tokio-timer",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tokio"
|
|
||||||
version = "0.2.25"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6703a273949a90131b290be1fe7b039d0fc884aa1935860dfcbe056f28cd8092"
|
|
||||||
dependencies = [
|
|
||||||
"bytes 0.5.6",
|
|
||||||
"fnv",
|
|
||||||
"iovec",
|
|
||||||
"lazy_static",
|
|
||||||
"memchr",
|
|
||||||
"mio 0.6.23",
|
|
||||||
"num_cpus",
|
|
||||||
"pin-project-lite 0.1.4",
|
|
||||||
"slab",
|
|
||||||
"tokio-macros",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio"
|
name = "tokio"
|
||||||
version = "1.3.0"
|
version = "1.3.0"
|
||||||
@@ -2287,7 +2318,12 @@ dependencies = [
|
|||||||
"memchr",
|
"memchr",
|
||||||
"mio 0.7.9",
|
"mio 0.7.9",
|
||||||
"num_cpus",
|
"num_cpus",
|
||||||
"pin-project-lite 0.2.6",
|
"once_cell",
|
||||||
|
"parking_lot 0.11.1",
|
||||||
|
"pin-project-lite",
|
||||||
|
"signal-hook-registry",
|
||||||
|
"tokio-macros",
|
||||||
|
"winapi 0.3.8",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2301,20 +2337,6 @@ dependencies = [
|
|||||||
"futures 0.1.29",
|
"futures 0.1.29",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tokio-compat-02"
|
|
||||||
version = "0.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e7d4237822b7be8fff0a7a27927462fad435dcb6650f95cea9e946bf6bdc7e07"
|
|
||||||
dependencies = [
|
|
||||||
"bytes 0.5.6",
|
|
||||||
"once_cell",
|
|
||||||
"pin-project-lite 0.2.6",
|
|
||||||
"tokio 0.2.25",
|
|
||||||
"tokio 1.3.0",
|
|
||||||
"tokio-stream",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-current-thread"
|
name = "tokio-current-thread"
|
||||||
version = "0.1.7"
|
version = "0.1.7"
|
||||||
@@ -2348,9 +2370,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-macros"
|
name = "tokio-macros"
|
||||||
version = "0.2.6"
|
version = "1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e44da00bfc73a25f814cd8d7e57a68a5c31b74b3152a0a1d1f590c97ed06265a"
|
checksum = "caf7b11a536f46a809a8a9f0bb4237020f70ecbf115b842360afb127ea2fda57"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -2379,24 +2401,13 @@ dependencies = [
|
|||||||
"log",
|
"log",
|
||||||
"mio 0.6.23",
|
"mio 0.6.23",
|
||||||
"num_cpus",
|
"num_cpus",
|
||||||
"parking_lot",
|
"parking_lot 0.9.0",
|
||||||
"slab",
|
"slab",
|
||||||
"tokio-executor",
|
"tokio-executor",
|
||||||
"tokio-io",
|
"tokio-io",
|
||||||
"tokio-sync",
|
"tokio-sync",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tokio-stream"
|
|
||||||
version = "0.1.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c535f53c0cfa1acace62995a8994fc9cc1f12d202420da96ff306ee24d576469"
|
|
||||||
dependencies = [
|
|
||||||
"futures-core",
|
|
||||||
"pin-project-lite 0.2.6",
|
|
||||||
"tokio 1.3.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-sync"
|
name = "tokio-sync"
|
||||||
version = "0.1.8"
|
version = "0.1.8"
|
||||||
@@ -2450,30 +2461,6 @@ dependencies = [
|
|||||||
"tokio-executor",
|
"tokio-executor",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tokio-tls"
|
|
||||||
version = "0.3.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7bde02a3a5291395f59b06ec6945a3077602fac2b07eeeaf0dee2122f3619828"
|
|
||||||
dependencies = [
|
|
||||||
"native-tls",
|
|
||||||
"tokio 0.2.25",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tokio-util"
|
|
||||||
version = "0.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "571da51182ec208780505a32528fc5512a8fe1443ab960b3f2f3ef093cd16930"
|
|
||||||
dependencies = [
|
|
||||||
"bytes 0.5.6",
|
|
||||||
"futures-core",
|
|
||||||
"futures-sink",
|
|
||||||
"log",
|
|
||||||
"pin-project-lite 0.1.4",
|
|
||||||
"tokio 0.2.25",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-util"
|
name = "tokio-util"
|
||||||
version = "0.6.4"
|
version = "0.6.4"
|
||||||
@@ -2484,7 +2471,7 @@ dependencies = [
|
|||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-sink",
|
"futures-sink",
|
||||||
"log",
|
"log",
|
||||||
"pin-project-lite 0.2.6",
|
"pin-project-lite",
|
||||||
"tokio 1.3.0",
|
"tokio 1.3.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -2510,7 +2497,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "01ebdc2bb4498ab1ab5f5b73c5803825e60199229ccba0698170e3be0e7f959f"
|
checksum = "01ebdc2bb4498ab1ab5f5b73c5803825e60199229ccba0698170e3be0e7f959f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if 1.0.0",
|
"cfg-if 1.0.0",
|
||||||
"pin-project-lite 0.2.6",
|
"pin-project-lite",
|
||||||
"tracing-core",
|
"tracing-core",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -2703,6 +2690,12 @@ version = "0.9.0+wasi-snapshot-preview1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
|
checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasi"
|
||||||
|
version = "0.10.2+wasi-snapshot-preview1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen"
|
name = "wasm-bindgen"
|
||||||
version = "0.2.71"
|
version = "0.2.71"
|
||||||
|
18
Cargo.toml
18
Cargo.toml
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "scootaloo"
|
name = "scootaloo"
|
||||||
version = "0.2.1"
|
version = "0.3.2"
|
||||||
authors = ["VC <veretcle+framagit@mateu.be>"]
|
authors = ["VC <veretcle+framagit@mateu.be>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
@@ -8,18 +8,12 @@ edition = "2018"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
|
||||||
toml = "^0.5"
|
toml = "^0.5"
|
||||||
|
|
||||||
clap = "^2.33"
|
clap = "^2.33"
|
||||||
|
egg-mode = { git = "https://github.com/egg-mode-rs/egg-mode", rev = "6b81073eba9c3b123ca0e80bdb5ef61d1758f131" }
|
||||||
tokio = { version = "1", features = ["rt-multi-thread"]}
|
|
||||||
tokio-compat-02 = "0.2"
|
|
||||||
|
|
||||||
egg-mode = "^0.15"
|
|
||||||
|
|
||||||
elefren = "^0.22"
|
elefren = "^0.22"
|
||||||
|
tokio = { version = "1", features = ["full"]}
|
||||||
reqwest = { version="^0.11", features = ["blocking"] }
|
reqwest = "^0.11"
|
||||||
|
|
||||||
htmlescape = "^0.3"
|
htmlescape = "^0.3"
|
||||||
|
log = "^0.4"
|
||||||
|
simple_logger = "^1.11"
|
||||||
|
@@ -3,7 +3,7 @@ A Twitter to Mastodon copy bot written in Rust
|
|||||||
It:
|
It:
|
||||||
* copies the content (text) of the original Tweet
|
* copies the content (text) of the original Tweet
|
||||||
* dereferences the links
|
* dereferences the links
|
||||||
* gets every attach media (photo, video or gif)
|
* gets every attached media (photo, video or gif)
|
||||||
|
|
||||||
If any of the last steps failed, the Toot gets published with the exact same text as the Tweet.
|
If any of the last steps failed, the Toot gets published with the exact same text as the Tweet.
|
||||||
|
|
||||||
@@ -74,4 +74,3 @@ echo -n '8189881949849' > last_tweet
|
|||||||
|
|
||||||
**This file should only contain the last tweet ID without any other char (no EOL or new line).**
|
**This file should only contain the last tweet ID without any other char (no EOL or new line).**
|
||||||
|
|
||||||
Oh and everything is sync (and not async) so this does not run at a blazing speed…
|
|
||||||
|
174
src/lib.rs
174
src/lib.rs
@@ -1,18 +1,14 @@
|
|||||||
// std
|
// std
|
||||||
use std::{
|
use std::{
|
||||||
path::Path,
|
|
||||||
borrow::Cow,
|
borrow::Cow,
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
io::{stdin, copy},
|
io::stdin,
|
||||||
fmt,
|
fmt,
|
||||||
fs::{read_to_string, write, create_dir_all, File, remove_file},
|
fs::{read_to_string, write},
|
||||||
error::Error,
|
error::Error,
|
||||||
|
sync::{Arc, Mutex},
|
||||||
};
|
};
|
||||||
|
|
||||||
//tokio
|
|
||||||
use tokio::runtime::Runtime;
|
|
||||||
use tokio_compat_02::FutureExt;
|
|
||||||
|
|
||||||
// toml
|
// toml
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
@@ -37,34 +33,45 @@ use elefren::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
// reqwest
|
// reqwest
|
||||||
use reqwest::blocking::Client;
|
use reqwest::Url;
|
||||||
|
|
||||||
|
// tokio
|
||||||
|
use tokio::{
|
||||||
|
io::copy,
|
||||||
|
fs::{File, create_dir_all, remove_file},
|
||||||
|
sync::mpsc,
|
||||||
|
};
|
||||||
|
|
||||||
// htmlescape
|
// htmlescape
|
||||||
use htmlescape::decode_html;
|
use htmlescape::decode_html;
|
||||||
|
|
||||||
|
// log
|
||||||
|
use log::{info, warn, error, debug};
|
||||||
|
|
||||||
/**********
|
/**********
|
||||||
* Generic usage functions
|
* Generic usage functions
|
||||||
***********/
|
***********/
|
||||||
/*
|
/*
|
||||||
* Those functions are related to the Twitter side of things
|
* Those functions are related to the Twitter side of things
|
||||||
*/
|
*/
|
||||||
/// Read last tweet id from a file
|
/// Reads last tweet id from a file
|
||||||
fn read_state(s: &str) -> Option<u64> {
|
fn read_state(s: &str) -> Option<u64> {
|
||||||
let state = read_to_string(s);
|
let state = read_to_string(s);
|
||||||
|
|
||||||
if let Ok(s) = state {
|
if let Ok(s) = state {
|
||||||
|
debug!("Last Tweet ID (from file): {}", &s);
|
||||||
return s.parse::<u64>().ok();
|
return s.parse::<u64>().ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Write last treated tweet id to a file
|
/// Writes last treated tweet id to a file
|
||||||
fn write_state(f: &str, s: u64) -> Result<(), std::io::Error> {
|
fn write_state(f: &str, s: u64) -> Result<(), std::io::Error> {
|
||||||
write(f, format!("{}", s))
|
write(f, format!("{}", s))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get twitter oauth2 token
|
/// Gets Twitter oauth2 token
|
||||||
fn get_oauth2_token(config: &Config) -> Token {
|
fn get_oauth2_token(config: &Config) -> Token {
|
||||||
let con_token = KeyPair::new(String::from(&config.twitter.consumer_key), String::from(&config.twitter.consumer_secret));
|
let con_token = KeyPair::new(String::from(&config.twitter.consumer_key), String::from(&config.twitter.consumer_secret));
|
||||||
let access_token = KeyPair::new(String::from(&config.twitter.access_key), String::from(&config.twitter.access_secret));
|
let access_token = KeyPair::new(String::from(&config.twitter.access_key), String::from(&config.twitter.access_secret));
|
||||||
@@ -75,19 +82,18 @@ fn get_oauth2_token(config: &Config) -> Token {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get twitter user timeline
|
/// Gets Twitter user timeline
|
||||||
fn get_user_timeline(config: &Config, token: Token, lid: Option<u64>) -> Result<Vec<Tweet>, Box<dyn Error>> {
|
async fn get_user_timeline(config: &Config, token: Token, lid: Option<u64>) -> Result<Vec<Tweet>, Box<dyn Error>> {
|
||||||
// fix the page size to 200 as it is the maximum Twitter authorizes
|
// fix the page size to 200 as it is the maximum Twitter authorizes
|
||||||
let rt = Runtime::new()?;
|
let (_, feed) = user_timeline(UserID::from(String::from(&config.twitter.username)), true, false, &token)
|
||||||
let (_timeline, feed) = rt.block_on(user_timeline(UserID::from(String::from(&config.twitter.username)), true, false, &token)
|
|
||||||
.with_page_size(200)
|
.with_page_size(200)
|
||||||
.older(lid)
|
.older(lid)
|
||||||
.compat())?;
|
.await?;
|
||||||
|
|
||||||
Ok(feed.to_vec())
|
Ok(feed.to_vec())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// decode urls from UrlEntities
|
/// Decodes urls from UrlEntities
|
||||||
fn decode_urls(urls: &Vec<UrlEntity>) -> HashMap<String, String> {
|
fn decode_urls(urls: &Vec<UrlEntity>) -> HashMap<String, String> {
|
||||||
let mut decoded_urls = HashMap::new();
|
let mut decoded_urls = HashMap::new();
|
||||||
|
|
||||||
@@ -101,6 +107,8 @@ fn decode_urls(urls: &Vec<UrlEntity>) -> HashMap<String, String> {
|
|||||||
decoded_urls
|
decoded_urls
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Decodes the Twitter mention to something that will make sense once Twitter has joined the
|
||||||
|
/// Fediverse
|
||||||
fn twitter_mentions(ums: &Vec<MentionEntity>) -> HashMap<String, String> {
|
fn twitter_mentions(ums: &Vec<MentionEntity>) -> HashMap<String, String> {
|
||||||
let mut decoded_mentions = HashMap::new();
|
let mut decoded_mentions = HashMap::new();
|
||||||
|
|
||||||
@@ -111,18 +119,18 @@ fn twitter_mentions(ums: &Vec<MentionEntity>) -> HashMap<String, String> {
|
|||||||
decoded_mentions
|
decoded_mentions
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieve a single media from a tweet and store it in a temporary file
|
/// Retrieves a single media from a tweet and store it in a temporary file
|
||||||
fn get_tweet_media(m: &MediaEntity, t: &str) -> Result<String, Box<dyn Error>> {
|
async fn get_tweet_media(m: &MediaEntity, t: &str) -> Result<String, Box<dyn Error>> {
|
||||||
match m.media_type {
|
match m.media_type {
|
||||||
MediaType::Photo => {
|
MediaType::Photo => {
|
||||||
return cache_media(&m.media_url_https, t);
|
return cache_media(&m.media_url_https, t).await;
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
match &m.video_info {
|
match &m.video_info {
|
||||||
Some(v) => {
|
Some(v) => {
|
||||||
for variant in &v.variants {
|
for variant in &v.variants {
|
||||||
if variant.content_type == "video/mp4" {
|
if variant.content_type == "video/mp4" {
|
||||||
return cache_media(&variant.url, t);
|
return cache_media(&variant.url, t).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Err(Box::new(ScootalooError::new(format!("Media Type for {} is video but no mp4 file URL is available", &m.url).as_str())));
|
return Err(Box::new(ScootalooError::new(format!("Media Type for {} is video but no mp4 file URL is available", &m.url).as_str())));
|
||||||
@@ -138,7 +146,7 @@ fn get_tweet_media(m: &MediaEntity, t: &str) -> Result<String, Box<dyn Error>> {
|
|||||||
/*
|
/*
|
||||||
* Those functions are related to the Mastodon side of things
|
* Those functions are related to the Mastodon side of things
|
||||||
*/
|
*/
|
||||||
/// Get Mastodon Data
|
/// Gets Mastodon Data
|
||||||
fn get_mastodon_token(masto: &MastodonConfig) -> Mastodon {
|
fn get_mastodon_token(masto: &MastodonConfig) -> Mastodon {
|
||||||
let data = Data {
|
let data = Data {
|
||||||
base: Cow::from(String::from(&masto.base)),
|
base: Cow::from(String::from(&masto.base)),
|
||||||
@@ -151,7 +159,7 @@ fn get_mastodon_token(masto: &MastodonConfig) -> Mastodon {
|
|||||||
Mastodon::from(data)
|
Mastodon::from(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// build toot text from tweet
|
/// Builds toot text from tweet
|
||||||
fn build_basic_status(tweet: &Tweet) -> Result<String, Box<dyn Error>> {
|
fn build_basic_status(tweet: &Tweet) -> Result<String, Box<dyn Error>> {
|
||||||
let mut toot = String::from(&tweet.text);
|
let mut toot = String::from(&tweet.text);
|
||||||
|
|
||||||
@@ -177,35 +185,40 @@ fn build_basic_status(tweet: &Tweet) -> Result<String, Box<dyn Error>> {
|
|||||||
/*
|
/*
|
||||||
* Generic private functions
|
* Generic private functions
|
||||||
*/
|
*/
|
||||||
fn cache_media(u: &str, t: &str) -> Result<String, Box<dyn Error>> {
|
|
||||||
|
/// Gets and caches Twitter Media inside the determined temp dir
|
||||||
|
async fn cache_media(u: &str, t: &str) -> Result<String, Box<dyn Error>> {
|
||||||
// create dir
|
// create dir
|
||||||
if !Path::new(t).is_dir() {
|
create_dir_all(t).await?;
|
||||||
create_dir_all(t)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// get file
|
// get file
|
||||||
let client = Client::new();
|
let mut response = reqwest::get(u).await?;
|
||||||
let mut response = client.get(u).send()?;
|
|
||||||
|
|
||||||
// create local file
|
// create local file
|
||||||
let dest_filename = match response.url()
|
let url = Url::parse(u)?;
|
||||||
.path_segments()
|
let dest_filename = url.path_segments().ok_or_else(|| Box::new(ScootalooError::new(format!("Cannot determine the destination filename for {}", u).as_str())))?
|
||||||
.and_then(|segments| segments.last()) {
|
.last().ok_or_else(|| Box::new(ScootalooError::new(format!("Cannot determine the destination filename for {}", u).as_str())))?;
|
||||||
Some(r) => r,
|
|
||||||
None => {
|
|
||||||
return Err(Box::new(ScootalooError::new(format!("Cannot determine the destination filename for {}", u).as_str())));
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
let dest_filepath = format!("{}/{}", t, dest_filename);
|
let dest_filepath = format!("{}/{}", t, dest_filename);
|
||||||
|
|
||||||
let mut dest_file = File::create(&dest_filepath)?;
|
let mut dest_file = File::create(&dest_filepath).await?;
|
||||||
|
|
||||||
copy(&mut response, &mut dest_file)?;
|
while let Some(chunk) = response.chunk().await? {
|
||||||
|
copy(&mut &*chunk, &mut dest_file).await?;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(dest_filepath)
|
Ok(dest_filepath)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**********
|
||||||
|
* This is the struct that holds the Mastodon Media ID and the Twitter Media URL at the same Time
|
||||||
|
**********/
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct ScootalooSpawnResponse {
|
||||||
|
mastodon_media_id: String,
|
||||||
|
twitter_media_url: String,
|
||||||
|
}
|
||||||
|
|
||||||
/**********
|
/**********
|
||||||
* local error handler
|
* local error handler
|
||||||
**********/
|
**********/
|
||||||
@@ -287,7 +300,7 @@ pub fn parse_toml(toml_file: &str) -> Config {
|
|||||||
|
|
||||||
/// Generic register function
|
/// Generic register function
|
||||||
/// As this function is supposed to be run only once, it will panic for every error it encounters
|
/// As this function is supposed to be run only once, it will panic for every error it encounters
|
||||||
/// Most of this function is a direct copy/paste of the official `mammut` crate
|
/// Most of this function is a direct copy/paste of the official `elefren` crate
|
||||||
pub fn register(host: &str) {
|
pub fn register(host: &str) {
|
||||||
let mut builder = App::builder();
|
let mut builder = App::builder();
|
||||||
builder.client_name(Cow::from(String::from(env!("CARGO_PKG_NAME"))))
|
builder.client_name(Cow::from(String::from(env!("CARGO_PKG_NAME"))))
|
||||||
@@ -315,7 +328,8 @@ pub fn register(host: &str) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// This is where the magic happens
|
/// This is where the magic happens
|
||||||
pub fn run(config: Config) {
|
#[tokio::main]
|
||||||
|
pub async fn run(config: Config) {
|
||||||
// retrieve the last tweet ID for the username
|
// retrieve the last tweet ID for the username
|
||||||
let last_tweet_id = read_state(&config.scootaloo.last_tweet_path);
|
let last_tweet_id = read_state(&config.scootaloo.last_tweet_path);
|
||||||
|
|
||||||
@@ -323,16 +337,18 @@ pub fn run(config: Config) {
|
|||||||
let token = get_oauth2_token(&config);
|
let token = get_oauth2_token(&config);
|
||||||
|
|
||||||
// get Mastodon instance
|
// get Mastodon instance
|
||||||
let mastodon = get_mastodon_token(&config.mastodon);
|
let mastodon = Arc::new(Mutex::new(get_mastodon_token(&config.mastodon)));
|
||||||
|
|
||||||
// get user timeline feed (Vec<tweet>)
|
// get user timeline feed (Vec<tweet>)
|
||||||
let mut feed = get_user_timeline(&config, token, last_tweet_id).unwrap_or_else(|e|
|
let mut feed = get_user_timeline(&config, token, last_tweet_id)
|
||||||
|
.await
|
||||||
|
.unwrap_or_else(|e|
|
||||||
panic!("Something went wrong when trying to retrieve {}’s timeline: {}", &config.twitter.username, e)
|
panic!("Something went wrong when trying to retrieve {}’s timeline: {}", &config.twitter.username, e)
|
||||||
);
|
);
|
||||||
|
|
||||||
// empty feed -> exiting
|
// empty feed -> exiting
|
||||||
if feed.is_empty() {
|
if feed.is_empty() {
|
||||||
println!("Nothing to retrieve since last time, exiting…");
|
info!("Nothing to retrieve since last time, exiting…");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -340,10 +356,12 @@ pub fn run(config: Config) {
|
|||||||
feed.reverse();
|
feed.reverse();
|
||||||
|
|
||||||
for tweet in &feed {
|
for tweet in &feed {
|
||||||
|
debug!("Treating Tweet {} inside feed", tweet.id);
|
||||||
// determine if the tweet is part of a thread (response to self) or a standard response
|
// determine if the tweet is part of a thread (response to self) or a standard response
|
||||||
if let Some(r) = &tweet.in_reply_to_screen_name {
|
if let Some(r) = &tweet.in_reply_to_screen_name {
|
||||||
if &r.to_lowercase() != &config.twitter.username.to_lowercase() {
|
if &r.to_lowercase() != &config.twitter.username.to_lowercase() {
|
||||||
// we are responding not threading
|
// we are responding not threading
|
||||||
|
info!("Tweet is a direct response, skipping");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -352,7 +370,7 @@ pub fn run(config: Config) {
|
|||||||
let mut status_text = match build_basic_status(tweet) {
|
let mut status_text = match build_basic_status(tweet) {
|
||||||
Ok(t) => t,
|
Ok(t) => t,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!("Could not create status from tweet {}: {}", tweet.id ,e);
|
error!("Could not create status from tweet {}: {}", tweet.id ,e);
|
||||||
continue;
|
continue;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -361,35 +379,64 @@ pub fn run(config: Config) {
|
|||||||
|
|
||||||
// reupload the attachments if any
|
// reupload the attachments if any
|
||||||
if let Some(m) = &tweet.extended_entities {
|
if let Some(m) = &tweet.extended_entities {
|
||||||
|
let (tx, mut rx) = mpsc::channel(4);
|
||||||
|
|
||||||
for media in &m.media {
|
for media in &m.media {
|
||||||
let local_tweet_media_path = match get_tweet_media(&media, &config.scootaloo.cache_path) {
|
// creating a new tx for this initial loop
|
||||||
|
let tx = tx.clone();
|
||||||
|
// creating a new mastodon from the original mutex
|
||||||
|
let mastodon = mastodon.clone();
|
||||||
|
// unfortunately for this to be thread safe, we need to clone a lot of structures
|
||||||
|
let media = media.clone();
|
||||||
|
let cache_path = config.scootaloo.cache_path.clone();
|
||||||
|
|
||||||
|
tokio::spawn(async move {
|
||||||
|
debug!("Spawing new async thread to treat {}", &media.id);
|
||||||
|
let local_tweet_media_path = match get_tweet_media(&media, &cache_path).await {
|
||||||
Ok(m) => m,
|
Ok(m) => m,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!("Cannot get tweet media for {}: {}", &media.url, e);
|
// we could have panicked here, no issue, but I’m not confortable using
|
||||||
continue;
|
// that for now
|
||||||
},
|
warn!("Cannot get tweet media for {}: {}", &media.url, e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let mastodon_media_ids = match mastodon.media(Cow::from(String::from(&local_tweet_media_path))) {
|
// we cannot directly do all the stuff inside here because mastodon lock can
|
||||||
|
// live outside this
|
||||||
|
let mas_result = mastodon.lock().unwrap().media(Cow::from(String::from(&local_tweet_media_path)));
|
||||||
|
|
||||||
|
match mas_result {
|
||||||
Ok(m) => {
|
Ok(m) => {
|
||||||
remove_file(&local_tweet_media_path).unwrap_or_else(|e|
|
remove_file(&local_tweet_media_path).await.unwrap_or_else(|e|
|
||||||
println!("Attachment for {} has been upload, but I’m unable to remove the existing file: {}", &local_tweet_media_path, e)
|
warn!("Attachment {} has been uploaded but I’m unable to remove the existing file: {}", &local_tweet_media_path, e)
|
||||||
);
|
);
|
||||||
m.id
|
// we can unwrap here because we’re in a thread
|
||||||
|
tx.send(ScootalooSpawnResponse {
|
||||||
|
mastodon_media_id: m.id.clone(),
|
||||||
|
twitter_media_url: local_tweet_media_path.clone()
|
||||||
|
}).await.unwrap();
|
||||||
},
|
},
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!("Cannot attach media {} to Mastodon Instance: {}", &local_tweet_media_path, e);
|
error!("Attachment {} cannot be uploaded to Mastodon Instance: {}", &local_tweet_media_path, e);
|
||||||
continue;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
status_medias.push(mastodon_media_ids);
|
|
||||||
|
|
||||||
// last step, removing the reference to the media from with the toot’s text
|
|
||||||
status_text = status_text.replace(&media.url, "");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// dropping the last tx otherwise recv() will wait indefinitely
|
||||||
|
drop(tx);
|
||||||
|
|
||||||
|
while let Some(i) = rx.recv().await {
|
||||||
|
// pushes the media into the media vec
|
||||||
|
status_medias.push(i.mastodon_media_id);
|
||||||
|
// removes the URL from the original Tweet text
|
||||||
|
status_text = status_text.replace(&i.twitter_media_url, "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// finished reuploading attachments, now let’s do the toot baby!
|
||||||
|
|
||||||
|
debug!("Building corresponding Mastodon status");
|
||||||
let status = StatusBuilder::new()
|
let status = StatusBuilder::new()
|
||||||
.status(&status_text)
|
.status(&status_text)
|
||||||
.media_ids(status_medias)
|
.media_ids(status_medias)
|
||||||
@@ -397,7 +444,8 @@ pub fn run(config: Config) {
|
|||||||
.expect(format!("Cannot build status with text {}", &status_text).as_str());
|
.expect(format!("Cannot build status with text {}", &status_text).as_str());
|
||||||
|
|
||||||
// publish status
|
// publish status
|
||||||
mastodon.new_status(status).unwrap();
|
// again unwrap is safe here as we are in the main thread
|
||||||
|
mastodon.lock().unwrap().new_status(status).unwrap();
|
||||||
// this will panic if it cannot publish the status, which is a good thing, it allows the
|
// this will panic if it cannot publish the status, which is a good thing, it allows the
|
||||||
// last_tweet gathered not to be written
|
// last_tweet gathered not to be written
|
||||||
|
|
||||||
|
25
src/main.rs
25
src/main.rs
@@ -4,6 +4,13 @@ use scootaloo::*;
|
|||||||
// clap
|
// clap
|
||||||
use clap::{App, Arg, SubCommand};
|
use clap::{App, Arg, SubCommand};
|
||||||
|
|
||||||
|
// log
|
||||||
|
use log::{LevelFilter, error};
|
||||||
|
use simple_logger::SimpleLogger;
|
||||||
|
|
||||||
|
// std
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let matches = App::new(env!("CARGO_PKG_NAME"))
|
let matches = App::new(env!("CARGO_PKG_NAME"))
|
||||||
.version(env!("CARGO_PKG_VERSION"))
|
.version(env!("CARGO_PKG_VERSION"))
|
||||||
@@ -15,6 +22,13 @@ fn main() {
|
|||||||
.help("TOML config file for scootaloo (default /usr/local/etc/scootaloo.toml)")
|
.help("TOML config file for scootaloo (default /usr/local/etc/scootaloo.toml)")
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.display_order(1))
|
.display_order(1))
|
||||||
|
.arg(Arg::with_name("log_level")
|
||||||
|
.short("l")
|
||||||
|
.long("loglevel")
|
||||||
|
.value_name("LOGLEVEL")
|
||||||
|
.help("Log level. Valid values are: Off, Warn, Error, Info, Debug")
|
||||||
|
.takes_value(true)
|
||||||
|
.display_order(2))
|
||||||
.subcommand(SubCommand::with_name("register")
|
.subcommand(SubCommand::with_name("register")
|
||||||
.version(env!("CARGO_PKG_VERSION"))
|
.version(env!("CARGO_PKG_VERSION"))
|
||||||
.about("Command to register to a Mastodon Instance")
|
.about("Command to register to a Mastodon Instance")
|
||||||
@@ -32,7 +46,18 @@ fn main() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if matches.is_present("log_level") {
|
||||||
|
match LevelFilter::from_str(matches.value_of("log_level").unwrap()) {
|
||||||
|
Ok(level) => { SimpleLogger::new().with_level(level).init().unwrap()},
|
||||||
|
Err(e) => {
|
||||||
|
SimpleLogger::new().with_level(LevelFilter::Error).init().unwrap();
|
||||||
|
error!("Unknown log level filter: {}", e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
let config = parse_toml(matches.value_of("config").unwrap_or("/usr/local/etc/scootaloo.toml"));
|
let config = parse_toml(matches.value_of("config").unwrap_or("/usr/local/etc/scootaloo.toml"));
|
||||||
|
|
||||||
run(config);
|
run(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user