use sigh::PrivateKey; use std::{sync::Arc, time::Duration}; use std::{panic, process}; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; use cave::activitypub; mod error; mod config; mod db; async fn follow_back( client: &reqwest::Client, hostname: &str, priv_key: &PrivateKey, follow: db::Follow, ) { let follow_id = format!( "https://{}/activity/follow/{}/{}", hostname, urlencoding::encode(&follow.id), urlencoding::encode(&follow.actor), ); let action = activitypub::Action { jsonld_context: Some(serde_json::Value::String("https://www.w3.org/ns/activitystreams".to_string())), action_type: "Follow".to_string(), id: follow_id, to: None, actor: follow.actor.clone(), object: Some(&follow.id), }; let key_id = format!("{}#key", follow.actor); let result = activitypub::send::send( client, &follow.inbox, &key_id, &priv_key, &action, ).await; match result { Ok(body) => { tracing::info!("Ok {}: {:?}", follow.id, body); } Err(e) => { tracing::error!("POST: {:?}", e); } } } #[tokio::main] async fn main() { exit_on_panic(); tracing_subscriber::registry() .with( tracing_subscriber::EnvFilter::try_from_default_env().unwrap_or_else(|_| { "buzzback=trace,tower_http=trace,axum=trace".into() }), ) .with(tracing_subscriber::fmt::layer()) .init(); let config = config::Config::load( &std::env::args().nth(1) .expect("Call with config.yaml") ); let priv_key = config.priv_key(); let client = Arc::new( reqwest::Client::builder() .timeout(Duration::from_secs(5)) .user_agent(concat!( env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"), )) .pool_max_idle_per_host(0) .pool_idle_timeout(Some(Duration::from_secs(1))) .build() .unwrap() ); // Follow relays from the static list in the config.yaml for relay in config.relays { let (id, inbox) = if relay.ends_with("/inbox") { (relay.replace("/inbox", "/actor"), relay.clone()) } else if relay.ends_with("/actor") { (relay.clone(), relay.replace("/actor", "/inbox")) } else { panic!("Not sure how to deal with relay {}", relay); }; tracing::trace!("Following {}", &id); let follow = db::Follow { id, actor: config.default_actor.clone(), inbox, }; follow_back(&client, &config.hostname, &priv_key, follow).await; } let database = db::Database::connect(&config.db).await; let instance_followers = database.get_instance_followers().await.unwrap(); // Follow back every instance that followed us for follow in instance_followers { tracing::trace!("Following {}", follow.id); follow_back(&client, &config.hostname, &priv_key, follow).await; } } fn exit_on_panic() { let orig_hook = panic::take_hook(); panic::set_hook(Box::new(move |panic_info| { // invoke the default handler and exit the process orig_hook(panic_info); process::exit(1); })); }