Compare commits
2
Commits
a7d410fd6d
...
049a198b1c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
049a198b1c | ||
|
|
d1b01e180d |
No files matched your search
@@ -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<LabelValueDefinition> = 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(())
|
||||||
|
}
|
||||||
@@ -75,6 +75,14 @@ impl Cities {
|
|||||||
self.keyed.len()
|
self.keyed.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.keyed.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn iter(&self) -> impl Iterator<Item = &City> {
|
||||||
|
self.keyed.iter().map(|k| &k.city)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get(&self, id: &str) -> Option<&City> {
|
pub fn get(&self, id: &str) -> Option<&City> {
|
||||||
self.by_id.get(id).map(|&i| &self.keyed[i].city)
|
self.by_id.get(id).map(|&i| &self.keyed[i].city)
|
||||||
}
|
}
|
||||||
|
|||||||
+19
@@ -0,0 +1,19 @@
|
|||||||
|
pub mod cities;
|
||||||
|
pub mod config;
|
||||||
|
pub mod db;
|
||||||
|
pub mod dms;
|
||||||
|
pub mod labeler;
|
||||||
|
pub mod labels;
|
||||||
|
pub mod server;
|
||||||
|
|
||||||
|
use anyhow::{Context, Result};
|
||||||
|
use jacquard::client::{Agent, MemoryCredentialSession};
|
||||||
|
use jacquard::types::string::Did;
|
||||||
|
|
||||||
|
/// Log in as the labeler account with an app password.
|
||||||
|
pub async fn login(handle: &str, password: &str) -> Result<(Agent<MemoryCredentialSession>, Did)> {
|
||||||
|
let (session, account) = MemoryCredentialSession::authenticated(handle, password, None, None)
|
||||||
|
.await
|
||||||
|
.with_context(|| format!("logging in as {handle}"))?;
|
||||||
|
Ok((Agent::from(session), account.did))
|
||||||
|
}
|
||||||
+8
-21
@@ -1,38 +1,25 @@
|
|||||||
mod cities;
|
|
||||||
mod config;
|
|
||||||
mod db;
|
|
||||||
mod dms;
|
|
||||||
mod labeler;
|
|
||||||
mod labels;
|
|
||||||
mod server;
|
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use jacquard::client::{Agent, MemoryCredentialSession};
|
use cities_labeler::cities::Cities;
|
||||||
|
use cities_labeler::config::Config;
|
||||||
|
use cities_labeler::db::Db;
|
||||||
|
use cities_labeler::dms::Dms;
|
||||||
|
use cities_labeler::labeler::Labeler;
|
||||||
|
use cities_labeler::{login, server};
|
||||||
use k256::ecdsa::SigningKey;
|
use k256::ecdsa::SigningKey;
|
||||||
|
|
||||||
use crate::cities::Cities;
|
|
||||||
use crate::config::Config;
|
|
||||||
use crate::db::Db;
|
|
||||||
use crate::dms::Dms;
|
|
||||||
use crate::labeler::Labeler;
|
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<()> {
|
async fn main() -> Result<()> {
|
||||||
let config = Config::from_env()?;
|
let config = Config::from_env()?;
|
||||||
|
|
||||||
let (session, account) =
|
let (agent, did) = login(&config.handle, &config.app_password).await?;
|
||||||
MemoryCredentialSession::authenticated(&config.handle, &config.app_password, None, None)
|
|
||||||
.await
|
|
||||||
.with_context(|| format!("logging in as {}", config.handle))?;
|
|
||||||
let agent = Agent::from(session);
|
|
||||||
|
|
||||||
let key = SigningKey::from_slice(&hex::decode(&config.signing_key_hex)?)
|
let key = SigningKey::from_slice(&hex::decode(&config.signing_key_hex)?)
|
||||||
.context("SIGNING_KEY_HEX")?;
|
.context("SIGNING_KEY_HEX")?;
|
||||||
let cities = Cities::load(&config.cities_path)?;
|
let cities = Cities::load(&config.cities_path)?;
|
||||||
let db = Arc::new(Db::open(&config.db_path)?);
|
let db = Arc::new(Db::open(&config.db_path)?);
|
||||||
let labeler = Arc::new(Labeler::new(db, account.did, key));
|
let labeler = Arc::new(Labeler::new(db, did, key));
|
||||||
println!(
|
println!(
|
||||||
"labeling as {}, loaded {} cities",
|
"labeling as {}, loaded {} cities",
|
||||||
labeler.did,
|
labeler.did,
|
||||||
|
|||||||
Reference in new issue
Block a user