use anyhow::{Context, Result}; use serde_json::Value; use std::fs; use super::auth; use crate::lexicons::com_atproto_repo; use crate::tid; use crate::types::{ DeleteRecordRequest, ListRecordsResponse, PutRecordRequest, PutRecordResponse, }; use crate::xrpc::XrpcClient; /// Put a record to ATProto pub async fn put_record(file: &str, collection: &str, rkey: Option<&str>) -> Result<()> { let session = auth::refresh_session().await?; let pds = session.pds.as_deref().unwrap_or("bsky.social"); let client = XrpcClient::new(pds); let content = fs::read_to_string(file) .with_context(|| format!("Failed to read file: {}", file))?; let record: Value = serde_json::from_str(&content)?; let rkey = rkey .map(|s| s.to_string()) .unwrap_or_else(tid::generate_tid); let req = PutRecordRequest { repo: session.did.clone(), collection: collection.to_string(), rkey: rkey.clone(), record, }; let result: PutRecordResponse = client .call(&com_atproto_repo::PUT_RECORD, &req, &session.access_jwt) .await?; println!("{}", serde_json::to_string_pretty(&serde_json::json!({ "uri": result.uri, "cid": result.cid, }))?); Ok(()) } /// Put a lexicon schema pub async fn put_lexicon(file: &str) -> Result<()> { let session = auth::refresh_session().await?; let pds = session.pds.as_deref().unwrap_or("bsky.social"); let client = XrpcClient::new(pds); let content = fs::read_to_string(file) .with_context(|| format!("Failed to read file: {}", file))?; let lexicon: Value = serde_json::from_str(&content)?; let lexicon_id = lexicon["id"] .as_str() .context("Lexicon file must have 'id' field")? .to_string(); let req = PutRecordRequest { repo: session.did.clone(), collection: "com.atproto.lexicon.schema".to_string(), rkey: lexicon_id.clone(), record: lexicon, }; let result: PutRecordResponse = client .call(&com_atproto_repo::PUT_RECORD, &req, &session.access_jwt) .await?; println!("{}", serde_json::to_string_pretty(&serde_json::json!({ "uri": result.uri, "cid": result.cid, }))?); Ok(()) } /// Get records from a collection pub async fn get_records(collection: &str, limit: u32) -> Result<()> { let session = auth::refresh_session().await?; let pds = session.pds.as_deref().unwrap_or("bsky.social"); let client = XrpcClient::new(pds); let limit_str = limit.to_string(); let result: ListRecordsResponse = client .query_auth( &com_atproto_repo::LIST_RECORDS, &[ ("repo", &session.did), ("collection", collection), ("limit", &limit_str), ], &session.access_jwt, ) .await?; println!("{}", serde_json::to_string_pretty(&result)?); Ok(()) } /// Delete a record pub async fn delete_record(collection: &str, rkey: &str, is_bot: bool) -> Result<()> { let session = if is_bot { auth::refresh_bot_session().await? } else { auth::refresh_session().await? }; let pds = session.pds.as_deref().unwrap_or("bsky.social"); let client = if is_bot { XrpcClient::new_bot(pds) } else { XrpcClient::new(pds) }; let req = DeleteRecordRequest { repo: session.did.clone(), collection: collection.to_string(), rkey: rkey.to_string(), }; client .call_no_response(&com_atproto_repo::DELETE_RECORD, &req, &session.access_jwt) .await?; println!("{}", serde_json::to_string_pretty(&serde_json::json!({ "deleted": true, "collection": collection, "rkey": rkey, }))?); Ok(()) }