diff --git a/src/bin/setup.rs b/src/bin/setup.rs new file mode 100644 index 0000000..581c824 --- /dev/null +++ b/src/bin/setup.rs @@ -0,0 +1,80 @@ +//! One-time labeler setup. +//! +//! `cargo run --bin setup -- declare` publishes the labeler declaration +//! (`app.bsky.labeler.service/self`) with one label per city. This is what +//! gives the account a Subscribe button. Safe to re-run after editing the list. +use anyhow::{Result, bail}; +use cities_labeler::cities::Cities; +use cities_labeler::config::Config; +use cities_labeler::login; +use jacquard::api::app_bsky::labeler::LabelerPolicies; +use jacquard::api::app_bsky::labeler::service::Service; +use jacquard::api::com_atproto::label::{ + LabelValue, LabelValueDefinition, LabelValueDefinitionBlurs, + LabelValueDefinitionDefaultSetting, LabelValueDefinitionSeverity, LabelValueDefinitionStrings, +}; +use jacquard::prelude::*; +use jacquard::types::language::Language; +use jacquard::types::recordkey::RecordKey; +use jacquard::types::string::Datetime; + +#[tokio::main] +async fn main() -> Result<()> { + let config = Config::from_env()?; + match std::env::args().nth(1).as_deref() { + Some("declare") => declare(&config).await, + _ => bail!("usage: setup declare"), + } +} + +async fn declare(config: &Config) -> Result<()> { + let cities = Cities::load(&config.cities_path)?; + let (agent, did) = login(&config.handle, &config.app_password).await?; + + let definitions: Vec = cities + .iter() + .map(|city| LabelValueDefinition { + identifier: city.id.as_str().into(), + // a neutral badge, shown by default, never hiding content + severity: LabelValueDefinitionSeverity::Inform, + blurs: LabelValueDefinitionBlurs::None, + default_setting: Some(LabelValueDefinitionDefaultSetting::Warn), + adult_only: Some(false), + locales: vec![LabelValueDefinitionStrings { + lang: Language::new_static("en").expect("valid language tag"), + name: city.display().into(), + description: format!("Chose {} as their city.", city.display()).into(), + extra_data: None, + }], + extra_data: None, + }) + .collect(); + + let service: Service = Service { + created_at: Datetime::now(), + labels: None, + policies: LabelerPolicies { + label_values: cities + .iter() + .map(|c| LabelValue::Other(c.id.as_str().into())) + .collect(), + label_value_definitions: Some(definitions), + extra_data: None, + }, + // these declare which reports the labeler reviews: none + reason_types: Some(vec![]), + subject_types: Some(vec![]), + subject_collections: None, + extra_data: None, + }; + + let output = agent + .put_record(RecordKey::any_static("self")?, service) + .await?; + println!( + "published {} with {} labels for {did}", + output.uri, + cities.len() + ); + Ok(()) +}