Compare commits

..

6 Commits

Author SHA1 Message Date
Alie c1717d9385 Merge branch 'release/v0.2.1' 2023-07-19 10:20:02 +02:00
Alie 94add29a64 modify cargo toml 2023-07-19 10:19:52 +02:00
Alie 0a6396021a Merge branch 'feature/logfixes' into develop 2023-07-19 10:17:24 +02:00
Alie 1ebd4ffc38 changed curl requests to reqwest
to not depend on curl being installed
2023-07-19 10:17:09 +02:00
Alie 5e175bfcd8 added timeout on retries 2023-07-19 09:38:49 +02:00
Alie f6ac76f024 Merge tag 'v0.2.0' into develop
v0.2.0
2023-07-14 19:08:50 +02:00
3 changed files with 300 additions and 66 deletions

305
Cargo.lock generated
View File

@ -27,27 +27,112 @@ dependencies = [
] ]
[[package]] [[package]]
name = "async-stream" name = "async-channel"
version = "0.3.4" version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad445822218ce64be7a341abfb0b1ea43b5c23aa83902542a4542e78309d8e5e" checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35"
dependencies = [ dependencies = [
"async-stream-impl", "concurrent-queue",
"event-listener",
"futures-core", "futures-core",
"pin-project-lite",
] ]
[[package]] [[package]]
name = "async-stream-impl" name = "async-executor"
version = "0.3.4" version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e4655ae1a7b0cdf149156f780c5bf3f1352bc53cbd9e0a361a7ef7b22947e965" checksum = "6fa3dc5f2a8564f07759c008b9109dc0d39de92a88d5588b8a5036d286383afb"
dependencies = [ dependencies = [
"proc-macro2", "async-lock",
"quote", "async-task",
"syn 1.0.107", "concurrent-queue",
"fastrand",
"futures-lite",
"slab",
] ]
[[package]]
name = "async-global-executor"
version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1b6f5d7df27bd294849f8eec66ecfc63d11814df7a4f5d74168a2394467b776"
dependencies = [
"async-channel",
"async-executor",
"async-io",
"async-lock",
"blocking",
"futures-lite",
"once_cell",
]
[[package]]
name = "async-io"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af"
dependencies = [
"async-lock",
"autocfg",
"cfg-if",
"concurrent-queue",
"futures-lite",
"log",
"parking",
"polling",
"rustix",
"slab",
"socket2",
"waker-fn",
]
[[package]]
name = "async-lock"
version = "2.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa24f727524730b077666307f2734b4a1a1c57acb79193127dcc8914d5242dd7"
dependencies = [
"event-listener",
]
[[package]]
name = "async-std"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d"
dependencies = [
"async-channel",
"async-global-executor",
"async-io",
"async-lock",
"crossbeam-utils",
"futures-channel",
"futures-core",
"futures-io",
"futures-lite",
"gloo-timers",
"kv-log-macro",
"log",
"memchr",
"once_cell",
"pin-project-lite",
"pin-utils",
"slab",
"wasm-bindgen-futures",
]
[[package]]
name = "async-task"
version = "4.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ecc7ab41815b3c653ccd2978ec3255c81349336702dfdf62ee6f7069b12a3aae"
[[package]]
name = "atomic-waker"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1181e1e0d1fce796a03db1ae795d67167da795f9cf4a39c37589e85ef57f26d3"
[[package]] [[package]]
name = "atty" name = "atty"
version = "0.2.14" version = "0.2.14"
@ -77,6 +162,21 @@ version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "blocking"
version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77231a1c8f801696fc0123ec6150ce92cffb8e164a02afb9c8ddee0e9b65ad65"
dependencies = [
"async-channel",
"async-lock",
"async-task",
"atomic-waker",
"fastrand",
"futures-lite",
"log",
]
[[package]] [[package]]
name = "bumpalo" name = "bumpalo"
version = "3.12.0" version = "3.12.0"
@ -152,6 +252,15 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "concurrent-queue"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62ec6771ecfa0762d24683ee5a32ad78487a3d3afdc0fb8cae19d2c5deb50b7c"
dependencies = [
"crossbeam-utils",
]
[[package]] [[package]]
name = "core-foundation" name = "core-foundation"
version = "0.9.3" version = "0.9.3"
@ -168,6 +277,15 @@ version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
[[package]]
name = "crossbeam-utils"
version = "0.8.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294"
dependencies = [
"cfg-if",
]
[[package]] [[package]]
name = "derive_deref" name = "derive_deref"
version = "1.1.1" version = "1.1.1"
@ -209,6 +327,27 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "errno"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a"
dependencies = [
"errno-dragonfly",
"libc",
"windows-sys 0.48.0",
]
[[package]]
name = "errno-dragonfly"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
dependencies = [
"cc",
"libc",
]
[[package]] [[package]]
name = "error-chain" name = "error-chain"
version = "0.12.4" version = "0.12.4"
@ -218,6 +357,12 @@ dependencies = [
"version_check", "version_check",
] ]
[[package]]
name = "event-listener"
version = "2.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0"
[[package]] [[package]]
name = "fastrand" name = "fastrand"
version = "1.9.0" version = "1.9.0"
@ -305,6 +450,21 @@ version = "0.3.26"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfb8371b6fb2aeb2d280374607aeabfc99d95c72edfe51692e42d3d7f0d08531" checksum = "bfb8371b6fb2aeb2d280374607aeabfc99d95c72edfe51692e42d3d7f0d08531"
[[package]]
name = "futures-lite"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce"
dependencies = [
"fastrand",
"futures-core",
"futures-io",
"memchr",
"parking",
"pin-project-lite",
"waker-fn",
]
[[package]] [[package]]
name = "futures-macro" name = "futures-macro"
version = "0.3.26" version = "0.3.26"
@ -363,6 +523,18 @@ version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
[[package]]
name = "gloo-timers"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c"
dependencies = [
"futures-channel",
"futures-core",
"js-sys",
"wasm-bindgen",
]
[[package]] [[package]]
name = "h2" name = "h2"
version = "0.3.15" version = "0.3.15"
@ -412,6 +584,12 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "hermit-abi"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b"
[[package]] [[package]]
name = "http" name = "http"
version = "0.2.9" version = "0.2.9"
@ -545,6 +723,17 @@ dependencies = [
"cfg-if", "cfg-if",
] ]
[[package]]
name = "io-lifetimes"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2"
dependencies = [
"hermit-abi 0.3.2",
"libc",
"windows-sys 0.48.0",
]
[[package]] [[package]]
name = "ipnet" name = "ipnet"
version = "2.7.1" version = "2.7.1"
@ -576,6 +765,15 @@ dependencies = [
"wasm-bindgen", "wasm-bindgen",
] ]
[[package]]
name = "kv-log-macro"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f"
dependencies = [
"log",
]
[[package]] [[package]]
name = "lazy_static" name = "lazy_static"
version = "1.4.0" version = "1.4.0"
@ -588,6 +786,12 @@ version = "0.2.139"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
[[package]]
name = "linux-raw-sys"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519"
[[package]] [[package]]
name = "log" name = "log"
version = "0.4.19" version = "0.4.19"
@ -646,15 +850,15 @@ dependencies = [
[[package]] [[package]]
name = "mastodon-image-uploader-bot" name = "mastodon-image-uploader-bot"
version = "0.2.0" version = "0.2.1"
dependencies = [ dependencies = [
"async-std",
"log", "log",
"mastodon-async", "mastodon-async",
"reqwest", "reqwest",
"serde", "serde",
"stderrlog", "stderrlog",
"tokio", "tokio",
"tokio-test",
"toml 0.7.6", "toml 0.7.6",
] ]
@ -780,6 +984,12 @@ dependencies = [
"vcpkg", "vcpkg",
] ]
[[package]]
name = "parking"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14f2252c834a40ed9bb5422029649578e63aa341ac401f74e719dd1afda8394e"
[[package]] [[package]]
name = "parse_link_header" name = "parse_link_header"
version = "0.3.3" version = "0.3.3"
@ -834,6 +1044,22 @@ version = "0.3.26"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160"
[[package]]
name = "polling"
version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce"
dependencies = [
"autocfg",
"bitflags",
"cfg-if",
"concurrent-queue",
"libc",
"log",
"pin-project-lite",
"windows-sys 0.48.0",
]
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.64" version = "1.0.64"
@ -938,6 +1164,20 @@ dependencies = [
"winreg", "winreg",
] ]
[[package]]
name = "rustix"
version = "0.37.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62b24138615de35e32031d041a09032ef3487a616d901ca4db224e7d557efae2"
dependencies = [
"bitflags",
"errno",
"io-lifetimes",
"libc",
"linux-raw-sys",
"windows-sys 0.45.0",
]
[[package]] [[package]]
name = "ryu" name = "ryu"
version = "1.0.12" version = "1.0.12"
@ -1355,30 +1595,6 @@ dependencies = [
"tokio", "tokio",
] ]
[[package]]
name = "tokio-stream"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fb52b74f05dbf495a8fba459fdc331812b96aa086d9eb78101fa0d4569c3313"
dependencies = [
"futures-core",
"pin-project-lite",
"tokio",
]
[[package]]
name = "tokio-test"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53474327ae5e166530d17f2d956afcb4f8a004de581b3cae10f12006bc8163e3"
dependencies = [
"async-stream",
"bytes",
"futures-core",
"tokio",
"tokio-stream",
]
[[package]] [[package]]
name = "tokio-util" name = "tokio-util"
version = "0.7.7" version = "0.7.7"
@ -1566,6 +1782,12 @@ version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "waker-fn"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca"
[[package]] [[package]]
name = "walkdir" name = "walkdir"
version = "2.3.2" version = "2.3.2"
@ -1746,6 +1968,15 @@ dependencies = [
"windows-targets 0.42.1", "windows-targets 0.42.1",
] ]
[[package]]
name = "windows-sys"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [
"windows-targets 0.48.1",
]
[[package]] [[package]]
name = "windows-targets" name = "windows-targets"
version = "0.42.1" version = "0.42.1"

View File

@ -1,19 +1,19 @@
[package] [package]
name = "mastodon-image-uploader-bot" name = "mastodon-image-uploader-bot"
version = "0.2.0" version = "0.2.1"
edition = "2021" edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
tokio = { version = "1", features = ["macros", "rt-multi-thread"] } tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
tokio-test = "0.4.2"
reqwest = "0.11.14" reqwest = "0.11.14"
serde = "1.0.171" serde = "1.0.171"
toml = "0.7.1" toml = "0.7.1"
log = "0.4.19" log = "0.4.19"
stderrlog = "0.5.4" stderrlog = "0.5.4"
async-std = "1.12.0"
[dependencies.mastodon-async] [dependencies.mastodon-async]
version = "1.0" version = "1.0"
features = ["toml", "mt"] features = ["toml", "mt"]

View File

@ -1,20 +1,19 @@
use async_std;
use log;
use mastodon_async::entities::visibility::Visibility; use mastodon_async::entities::visibility::Visibility;
use mastodon_async::helpers::{cli, toml as masto_toml}; use mastodon_async::helpers::{cli, toml as masto_toml};
use mastodon_async::prelude::*; use mastodon_async::prelude::*;
use mastodon_async::Result as MastoResult;
use reqwest; use reqwest;
use serde::Deserialize; use serde::Deserialize;
use std::collections::HashSet; use std::collections::HashSet;
use std::error::Error;
use std::io::{Cursor, Write}; use std::io::{Cursor, Write};
use std::process::{exit, Command}; use std::process::exit;
use std::time::Duration;
use toml; use toml;
use log;
const CONFIG_FILENAME: &str = "config.toml"; const CONFIG_FILENAME: &str = "config.toml";
type DynResult<T> = Result<T, Box<dyn Error>>; type DynResult<T> = Result<T, Box<dyn std::error::Error>>;
#[derive(Deserialize)] #[derive(Deserialize)]
struct Config { struct Config {
@ -77,12 +76,14 @@ async fn main() -> DynResult<()> {
let mut retry: u8 = 0; let mut retry: u8 = 0;
while let Err(err) = post_image(&mastodon, &url, &config).await { while let Err(err) = post_image(&mastodon, &url, &config).await {
log::warn!("Cannot post image, retry: {}, {}", retry, err); log::warn!("Cannot post image, retry: {}, {}", retry, err);
async_std::task::sleep(Duration::new(1, 0)).await;
retry += 1; retry += 1;
if retry >= config.errors.retry { if retry >= config.errors.retry {
log::error!("Max ammount of retries reached on post_image"); log::error!("Max ammount of retries reached on post_image");
log::info!("Reverting file changes"); log::info!("Reverting file changes");
while let Err(err) = pop_last_line_of_file(&config.files.posted) { while let Err(err) = pop_last_line_of_file(&config.files.posted) {
log::warn!("Failed to revert, retrying: {}", err); log::warn!("Failed to revert, retrying: {}", err);
async_std::task::sleep(Duration::new(1, 0)).await;
} }
exit(1); exit(1);
} }
@ -90,6 +91,7 @@ async fn main() -> DynResult<()> {
let mut retry: u8 = 0; let mut retry: u8 = 0;
while let Err(err) = update_bio(&mastodon, &config).await { while let Err(err) = update_bio(&mastodon, &config).await {
log::warn!("Cannot update bio, retry: {}, {}", retry, err); log::warn!("Cannot update bio, retry: {}, {}", retry, err);
async_std::task::sleep(Duration::new(1, 0)).await;
retry += 1; retry += 1;
if retry >= config.errors.retry { if retry >= config.errors.retry {
log::error!("Max ammount of retries reached on update bio"); log::error!("Max ammount of retries reached on update bio");
@ -101,6 +103,7 @@ async fn main() -> DynResult<()> {
let mut retry: u8 = 0; let mut retry: u8 = 0;
while let Err(err) = post(&mastodon, &config.errors.out_of_images).await { while let Err(err) = post(&mastodon, &config.errors.out_of_images).await {
log::warn!("Cannot post, retry: {}, {}", retry, err); log::warn!("Cannot post, retry: {}, {}", retry, err);
async_std::task::sleep(Duration::new(1, 0)).await;
retry += 1; retry += 1;
if retry >= config.errors.retry { if retry >= config.errors.retry {
log::error!("Max ammount of retries reached on post"); log::error!("Max ammount of retries reached on post");
@ -157,7 +160,7 @@ fn get_next_url(config: &Config) -> DynResult<Option<String>> {
} }
async fn post_image(account: &Mastodon, url: &String, config: &Config) -> DynResult<()> { async fn post_image(account: &Mastodon, url: &String, config: &Config) -> DynResult<()> {
fetch_url(url.to_string(), &config.files.tempfile).await?; //.expect("Error fetching url"); fetch_url(url, &config.files.tempfile).await?; //.expect("Error fetching url");
let attachment = account let attachment = account
.media(&config.files.tempfile, Some(url.to_string())) .media(&config.files.tempfile, Some(url.to_string()))
.await?; //.expect("Attachment upload error"); .await?; //.expect("Attachment upload error");
@ -179,27 +182,27 @@ async fn update_bio(account: &Mastodon, config: &Config) -> DynResult<()> {
let posted = binding.lines().collect::<HashSet<&str>>(); let posted = binding.lines().collect::<HashSet<&str>>();
let binding = std::fs::read_to_string(&config.files.urls)?; //.expect("Url file not found"); let binding = std::fs::read_to_string(&config.files.urls)?; //.expect("Url file not found");
let urls = binding.lines().collect::<HashSet<&str>>(); let urls = binding.lines().collect::<HashSet<&str>>();
let remaining = urls.difference(&posted).count();
Command::new("curl") let remaining = urls.difference(&posted).count();
.args([ let client = reqwest::Client::new();
"-X",
"PATCH", let response = client
&format!("{}/api/v1/accounts/update_credentials", config.bot.instance), .patch(&format!(
"-H", "{}/api/v1/accounts/update_credentials",
&format!("Authorization:Bearer {}", account.data.token), config.bot.instance
"-d", ))
&format!( .bearer_auth(&account.data.token)
"note={}\n\n{} new images remaining", .body(format!(
config.bot.bio, remaining "note={}\n\n{} new images remaining",
), config.bot.bio, remaining
]) ))
.spawn()? //.expect("Could not spawn curl") .send()
.wait()?; //.expect("Curl failed"); .await?;
log::info!("Bio updated, response status: {}", response.status());
Ok(()) Ok(())
} }
async fn post(account: &Mastodon, msg: &str) -> MastoResult<()> { async fn post(account: &Mastodon, msg: &str) -> DynResult<()> {
let status = StatusBuilder::new() let status = StatusBuilder::new()
.visibility(Visibility::Direct) .visibility(Visibility::Direct)
.status(msg) .status(msg)
@ -209,7 +212,7 @@ async fn post(account: &Mastodon, msg: &str) -> MastoResult<()> {
Ok(()) Ok(())
} }
async fn fetch_url(url: String, file_name: &String) -> MastoResult<()> { async fn fetch_url(url: &String, file_name: &String) -> DynResult<()> {
let response = reqwest::get(url); let response = reqwest::get(url);
let mut file = std::fs::File::create(file_name)?; let mut file = std::fs::File::create(file_name)?;
let mut content = Cursor::new(response.await?.bytes().await?); let mut content = Cursor::new(response.await?.bytes().await?);
@ -217,7 +220,7 @@ async fn fetch_url(url: String, file_name: &String) -> MastoResult<()> {
Ok(()) Ok(())
} }
async fn register(config: &Config) -> MastoResult<Mastodon> { async fn register(config: &Config) -> DynResult<Mastodon> {
let registration = Registration::new(&config.bot.instance) let registration = Registration::new(&config.bot.instance)
.client_name(&config.bot.name) .client_name(&config.bot.name)
.scopes(Scopes::write_all()) .scopes(Scopes::write_all())