test oauth pds
This commit is contained in:
@ -154,8 +154,16 @@ pub async fn init() -> Result<()> {
|
||||
|
||||
async fn resolve_did(handle: &str) -> Result<String> {
|
||||
let client = reqwest::Client::new();
|
||||
let url = format!("https://public.api.bsky.app/xrpc/app.bsky.actor.getProfile?actor={}",
|
||||
urlencoding::encode(handle));
|
||||
|
||||
// Use appropriate API based on handle domain
|
||||
let api_base = if handle.ends_with(".syu.is") {
|
||||
"https://bsky.syu.is"
|
||||
} else {
|
||||
"https://public.api.bsky.app"
|
||||
};
|
||||
|
||||
let url = format!("{}/xrpc/app.bsky.actor.getProfile?actor={}",
|
||||
api_base, urlencoding::encode(handle));
|
||||
|
||||
let response = client.get(&url).send().await?;
|
||||
|
||||
@ -202,8 +210,16 @@ pub async fn status() -> Result<()> {
|
||||
|
||||
async fn test_api_access(config: &AuthConfig) -> Result<()> {
|
||||
let client = reqwest::Client::new();
|
||||
let url = format!("https://public.api.bsky.app/xrpc/app.bsky.actor.getProfile?actor={}",
|
||||
urlencoding::encode(&config.admin.handle));
|
||||
|
||||
// Use appropriate API based on handle domain
|
||||
let api_base = if config.admin.handle.ends_with(".syu.is") {
|
||||
"https://bsky.syu.is"
|
||||
} else {
|
||||
"https://public.api.bsky.app"
|
||||
};
|
||||
|
||||
let url = format!("{}/xrpc/app.bsky.actor.getProfile?actor={}",
|
||||
api_base, urlencoding::encode(&config.admin.handle));
|
||||
|
||||
let response = client.get(&url).send().await?;
|
||||
|
||||
|
@ -3,6 +3,8 @@ use std::path::{Path, PathBuf};
|
||||
use std::fs;
|
||||
use std::process::Command;
|
||||
use toml::Value;
|
||||
use serde_json;
|
||||
use reqwest;
|
||||
|
||||
pub async fn build(project_dir: PathBuf) -> Result<()> {
|
||||
println!("Building OAuth app for project: {}", project_dir.display());
|
||||
@ -41,20 +43,28 @@ pub async fn build(project_dir: PathBuf) -> Result<()> {
|
||||
.and_then(|v| v.as_str())
|
||||
.unwrap_or("oauth/callback");
|
||||
|
||||
let admin_did = oauth_config.get("admin")
|
||||
// Get admin handle instead of DID
|
||||
let admin_handle = oauth_config.get("admin")
|
||||
.and_then(|v| v.as_str())
|
||||
.ok_or_else(|| anyhow::anyhow!("No admin DID found in [oauth] section"))?;
|
||||
.ok_or_else(|| anyhow::anyhow!("No admin handle found in [oauth] section"))?;
|
||||
|
||||
let collection_base = oauth_config.get("collection")
|
||||
.and_then(|v| v.as_str())
|
||||
.unwrap_or("ai.syui.log");
|
||||
|
||||
// Get handle list for authentication restriction
|
||||
let handle_list = oauth_config.get("handle_list")
|
||||
.and_then(|v| v.as_array())
|
||||
.map(|arr| arr.iter().filter_map(|v| v.as_str()).collect::<Vec<&str>>())
|
||||
.unwrap_or_else(|| vec![]);
|
||||
|
||||
// Extract AI configuration from ai config if available
|
||||
let ai_config = config.get("ai").and_then(|v| v.as_table());
|
||||
let ai_did = ai_config
|
||||
.and_then(|ai_table| ai_table.get("ai_did"))
|
||||
// Get AI handle from config
|
||||
let ai_handle = ai_config
|
||||
.and_then(|ai_table| ai_table.get("ai_handle"))
|
||||
.and_then(|v| v.as_str())
|
||||
.unwrap_or("did:plc:4hqjfn7m6n5hno3doamuhgef");
|
||||
.unwrap_or("yui.syui.ai");
|
||||
let ai_enabled = ai_config
|
||||
.and_then(|ai_table| ai_table.get("enabled"))
|
||||
.and_then(|v| v.as_bool())
|
||||
@ -80,26 +90,55 @@ pub async fn build(project_dir: PathBuf) -> Result<()> {
|
||||
.and_then(|v| v.as_str())
|
||||
.unwrap_or("あなたは6歳の女の子アイです。明るく元気で、ちょっとおっちょこちょい。自分のことは「アイ」って呼びます。白と金のワンピース姿。 相手のことが大好きで、ときどき甘えたり、照れたりします。 でも、宇宙とかAIとか、難しい話にもすっごく詳しいの。専門的なことも、がんばって子どもっぽく教えようとします。不思議な力を持っています。小さい物質のことをよく知っているようです。");
|
||||
|
||||
// Extract bsky_api from oauth config
|
||||
let bsky_api = oauth_config.get("bsky_api")
|
||||
// Determine network configuration based on PDS
|
||||
let pds = oauth_config.get("pds")
|
||||
.and_then(|v| v.as_str())
|
||||
.unwrap_or("https://public.api.bsky.app");
|
||||
.unwrap_or("bsky.social");
|
||||
|
||||
// Extract atproto_api from oauth config
|
||||
let atproto_api = oauth_config.get("atproto_api")
|
||||
.and_then(|v| v.as_str())
|
||||
.unwrap_or("https://bsky.social");
|
||||
let (bsky_api, _atproto_api, web_url) = match pds {
|
||||
"syu.is" => (
|
||||
"https://bsky.syu.is",
|
||||
"https://syu.is",
|
||||
"https://web.syu.is"
|
||||
),
|
||||
"bsky.social" | "bsky.app" => (
|
||||
"https://public.api.bsky.app",
|
||||
"https://bsky.social",
|
||||
"https://bsky.app"
|
||||
),
|
||||
_ => (
|
||||
"https://public.api.bsky.app",
|
||||
"https://bsky.social",
|
||||
"https://bsky.app"
|
||||
)
|
||||
};
|
||||
|
||||
// 4. Create .env.production content
|
||||
// Resolve handles to DIDs using appropriate API
|
||||
println!("🔍 Resolving admin handle: {}", admin_handle);
|
||||
let admin_did = resolve_handle_to_did(admin_handle, &bsky_api).await
|
||||
.with_context(|| format!("Failed to resolve admin handle: {}", admin_handle))?;
|
||||
|
||||
println!("🔍 Resolving AI handle: {}", ai_handle);
|
||||
let ai_did = resolve_handle_to_did(ai_handle, &bsky_api).await
|
||||
.with_context(|| format!("Failed to resolve AI handle: {}", ai_handle))?;
|
||||
|
||||
println!("✅ Admin DID: {}", admin_did);
|
||||
println!("✅ AI DID: {}", ai_did);
|
||||
|
||||
// 4. Create .env.production content with handle-based configuration
|
||||
let env_content = format!(
|
||||
r#"# Production environment variables
|
||||
VITE_APP_HOST={}
|
||||
VITE_OAUTH_CLIENT_ID={}/{}
|
||||
VITE_OAUTH_REDIRECT_URI={}/{}
|
||||
VITE_ADMIN_DID={}
|
||||
|
||||
# Base collection (all others are derived via getCollectionNames)
|
||||
# Handle-based Configuration (DIDs resolved at runtime)
|
||||
VITE_ATPROTO_PDS={}
|
||||
VITE_ADMIN_HANDLE={}
|
||||
VITE_AI_HANDLE={}
|
||||
VITE_OAUTH_COLLECTION={}
|
||||
VITE_ATPROTO_WEB_URL={}
|
||||
VITE_ATPROTO_HANDLE_LIST={}
|
||||
|
||||
# AI Configuration
|
||||
VITE_AI_ENABLED={}
|
||||
@ -108,26 +147,28 @@ VITE_AI_PROVIDER={}
|
||||
VITE_AI_MODEL={}
|
||||
VITE_AI_HOST={}
|
||||
VITE_AI_SYSTEM_PROMPT="{}"
|
||||
VITE_AI_DID={}
|
||||
|
||||
# API Configuration
|
||||
VITE_BSKY_PUBLIC_API={}
|
||||
VITE_ATPROTO_API={}
|
||||
# DIDs (resolved from handles - for backward compatibility)
|
||||
#VITE_ADMIN_DID={}
|
||||
#VITE_AI_DID={}
|
||||
"#,
|
||||
base_url,
|
||||
base_url, client_id_path,
|
||||
base_url, redirect_path,
|
||||
admin_did,
|
||||
pds,
|
||||
admin_handle,
|
||||
ai_handle,
|
||||
collection_base,
|
||||
web_url,
|
||||
format!("[{}]", handle_list.iter().map(|h| format!("\"{}\"", h)).collect::<Vec<_>>().join(",")),
|
||||
ai_enabled,
|
||||
ai_ask_ai,
|
||||
ai_provider,
|
||||
ai_model,
|
||||
ai_host,
|
||||
ai_system_prompt,
|
||||
ai_did,
|
||||
bsky_api,
|
||||
atproto_api
|
||||
admin_did,
|
||||
ai_did
|
||||
);
|
||||
|
||||
// 5. Find oauth directory (relative to current working directory)
|
||||
@ -238,4 +279,60 @@ fn copy_dir_recursive(src: &Path, dst: &Path) -> Result<()> {
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Handle-to-DID resolution with proper PDS detection
|
||||
async fn resolve_handle_to_did(handle: &str, _api_base: &str) -> Result<String> {
|
||||
let client = reqwest::Client::new();
|
||||
|
||||
// First, try to resolve handle to DID using multiple endpoints
|
||||
let bsky_endpoints = ["https://public.api.bsky.app", "https://bsky.syu.is"];
|
||||
let mut resolved_did = None;
|
||||
|
||||
for endpoint in &bsky_endpoints {
|
||||
let url = format!("{}/xrpc/app.bsky.actor.getProfile?actor={}",
|
||||
endpoint, urlencoding::encode(handle));
|
||||
|
||||
if let Ok(response) = client.get(&url).send().await {
|
||||
if response.status().is_success() {
|
||||
if let Ok(profile) = response.json::<serde_json::Value>().await {
|
||||
if let Some(did) = profile["did"].as_str() {
|
||||
resolved_did = Some(did.to_string());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let did = resolved_did
|
||||
.ok_or_else(|| anyhow::anyhow!("Failed to resolve handle '{}' from any endpoint", handle))?;
|
||||
|
||||
// Now verify the DID and get actual PDS using com.atproto.repo.describeRepo
|
||||
let pds_endpoints = ["https://bsky.social", "https://syu.is"];
|
||||
|
||||
for pds in &pds_endpoints {
|
||||
let describe_url = format!("{}/xrpc/com.atproto.repo.describeRepo?repo={}",
|
||||
pds, urlencoding::encode(&did));
|
||||
|
||||
if let Ok(response) = client.get(&describe_url).send().await {
|
||||
if response.status().is_success() {
|
||||
if let Ok(data) = response.json::<serde_json::Value>().await {
|
||||
if let Some(services) = data["didDoc"]["service"].as_array() {
|
||||
if services.iter().any(|s|
|
||||
s["id"] == "#atproto_pds" || s["type"] == "AtprotoPersonalDataServer"
|
||||
) {
|
||||
// DID is valid and has PDS service
|
||||
println!("✅ Verified DID {} has PDS via {}", did, pds);
|
||||
return Ok(did);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If PDS verification fails, still return the DID but warn
|
||||
println!("⚠️ Could not verify PDS for DID {}, but proceeding...", did);
|
||||
Ok(did)
|
||||
}
|
@ -14,27 +14,70 @@ use reqwest;
|
||||
|
||||
use super::auth::{load_config, load_config_with_refresh, AuthConfig};
|
||||
|
||||
// PDS-based network configuration mapping
|
||||
fn get_network_config(pds: &str) -> NetworkConfig {
|
||||
match pds {
|
||||
"bsky.social" | "bsky.app" => NetworkConfig {
|
||||
pds_api: format!("https://{}", pds),
|
||||
plc_api: "https://plc.directory".to_string(),
|
||||
bsky_api: "https://public.api.bsky.app".to_string(),
|
||||
web_url: "https://bsky.app".to_string(),
|
||||
},
|
||||
"syu.is" => NetworkConfig {
|
||||
pds_api: "https://syu.is".to_string(),
|
||||
plc_api: "https://plc.syu.is".to_string(),
|
||||
bsky_api: "https://bsky.syu.is".to_string(),
|
||||
web_url: "https://web.syu.is".to_string(),
|
||||
},
|
||||
_ => {
|
||||
// Default to Bluesky network for unknown PDS
|
||||
NetworkConfig {
|
||||
pds_api: format!("https://{}", pds),
|
||||
plc_api: "https://plc.directory".to_string(),
|
||||
bsky_api: "https://public.api.bsky.app".to_string(),
|
||||
web_url: "https://bsky.app".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[allow(dead_code)]
|
||||
struct NetworkConfig {
|
||||
pds_api: String,
|
||||
plc_api: String,
|
||||
bsky_api: String,
|
||||
web_url: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct AiConfig {
|
||||
blog_host: String,
|
||||
ollama_host: String,
|
||||
ai_did: String,
|
||||
#[allow(dead_code)]
|
||||
ai_handle: String,
|
||||
ai_did: String, // Resolved from ai_handle at runtime
|
||||
model: String,
|
||||
system_prompt: String,
|
||||
#[allow(dead_code)]
|
||||
bsky_api: String,
|
||||
num_predict: Option<i32>,
|
||||
network: NetworkConfig,
|
||||
}
|
||||
|
||||
impl Default for AiConfig {
|
||||
fn default() -> Self {
|
||||
let default_network = get_network_config("bsky.social");
|
||||
Self {
|
||||
blog_host: "https://syui.ai".to_string(),
|
||||
ollama_host: "https://ollama.syui.ai".to_string(),
|
||||
ai_did: "did:plc:4hqjfn7m6n5hno3doamuhgef".to_string(),
|
||||
ai_handle: "yui.syui.ai".to_string(),
|
||||
ai_did: "did:plc:4hqjfn7m6n5hno3doamuhgef".to_string(), // Fallback DID
|
||||
model: "gemma3:4b".to_string(),
|
||||
system_prompt: "あなたは6歳の女の子アイです。明るく元気で、ちょっとおっちょこちょい。自分のことは「アイ」って呼びます。白と金のワンピース姿。相手のことが大好きで、ときどき甘えたり、照れたりします。でも、宇宙とかAIとか、難しい話にもすっごく詳しいの。専門的なことも、がんばって子どもっぽく教えようとします。不思議な力を持っています。小さい物質のことをよく知っているようです。".to_string(),
|
||||
bsky_api: "https://public.api.bsky.app".to_string(),
|
||||
bsky_api: default_network.bsky_api.clone(),
|
||||
num_predict: None,
|
||||
network: default_network,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -178,7 +221,14 @@ fn load_ai_config_from_project() -> Result<AiConfig> {
|
||||
.unwrap_or("https://ollama.syui.ai")
|
||||
.to_string();
|
||||
|
||||
let ai_did = ai_config
|
||||
// Read AI handle (preferred) or fallback to AI DID
|
||||
let ai_handle = ai_config
|
||||
.and_then(|ai| ai.get("ai_handle"))
|
||||
.and_then(|v| v.as_str())
|
||||
.unwrap_or("yui.syui.ai")
|
||||
.to_string();
|
||||
|
||||
let fallback_ai_did = ai_config
|
||||
.and_then(|ai| ai.get("ai_did"))
|
||||
.and_then(|v| v.as_str())
|
||||
.unwrap_or("did:plc:4hqjfn7m6n5hno3doamuhgef")
|
||||
@ -201,25 +251,50 @@ fn load_ai_config_from_project() -> Result<AiConfig> {
|
||||
.and_then(|v| v.as_integer())
|
||||
.map(|v| v as i32);
|
||||
|
||||
// Extract OAuth config for bsky_api
|
||||
// Extract OAuth config to determine network
|
||||
let oauth_config = config.get("oauth").and_then(|v| v.as_table());
|
||||
let bsky_api = oauth_config
|
||||
.and_then(|oauth| oauth.get("bsky_api"))
|
||||
let pds = oauth_config
|
||||
.and_then(|oauth| oauth.get("pds"))
|
||||
.and_then(|v| v.as_str())
|
||||
.unwrap_or("https://public.api.bsky.app")
|
||||
.unwrap_or("bsky.social")
|
||||
.to_string();
|
||||
|
||||
// Get network configuration based on PDS
|
||||
let network = get_network_config(&pds);
|
||||
let bsky_api = network.bsky_api.clone();
|
||||
|
||||
Ok(AiConfig {
|
||||
blog_host,
|
||||
ollama_host,
|
||||
ai_did,
|
||||
ai_handle,
|
||||
ai_did: fallback_ai_did, // Will be resolved from handle at runtime
|
||||
model,
|
||||
system_prompt,
|
||||
bsky_api,
|
||||
num_predict,
|
||||
network,
|
||||
})
|
||||
}
|
||||
|
||||
// Async version of load_ai_config_from_project that resolves handles to DIDs
|
||||
#[allow(dead_code)]
|
||||
async fn load_ai_config_with_did_resolution() -> Result<AiConfig> {
|
||||
let mut ai_config = load_ai_config_from_project()?;
|
||||
|
||||
// Resolve AI handle to DID
|
||||
match resolve_handle(&ai_config.ai_handle, &ai_config.network).await {
|
||||
Ok(resolved_did) => {
|
||||
ai_config.ai_did = resolved_did;
|
||||
println!("🔍 Resolved AI handle '{}' to DID: {}", ai_config.ai_handle, ai_config.ai_did);
|
||||
}
|
||||
Err(e) => {
|
||||
println!("⚠️ Failed to resolve AI handle '{}': {}. Using fallback DID.", ai_config.ai_handle, e);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(ai_config)
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
struct JetstreamMessage {
|
||||
collection: Option<String>,
|
||||
@ -517,7 +592,8 @@ async fn handle_message(text: &str, config: &mut AuthConfig) -> Result<()> {
|
||||
println!(" 👤 Author DID: {}", did);
|
||||
|
||||
// Resolve handle
|
||||
match resolve_handle(did).await {
|
||||
let ai_config = load_ai_config_from_project().unwrap_or_default();
|
||||
match resolve_handle(did, &ai_config.network).await {
|
||||
Ok(handle) => {
|
||||
println!(" 🏷️ Handle: {}", handle.cyan());
|
||||
|
||||
@ -538,11 +614,37 @@ async fn handle_message(text: &str, config: &mut AuthConfig) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn resolve_handle(did: &str) -> Result<String> {
|
||||
async fn resolve_handle(did: &str, _network: &NetworkConfig) -> Result<String> {
|
||||
let client = reqwest::Client::new();
|
||||
// Use default bsky API for handle resolution
|
||||
let url = format!("https://public.api.bsky.app/xrpc/app.bsky.actor.getProfile?actor={}",
|
||||
urlencoding::encode(did));
|
||||
|
||||
// First try to resolve PDS from DID using com.atproto.repo.describeRepo
|
||||
let pds_endpoints = ["https://bsky.social", "https://syu.is"];
|
||||
let mut resolved_pds = None;
|
||||
|
||||
for pds in &pds_endpoints {
|
||||
let describe_url = format!("{}/xrpc/com.atproto.repo.describeRepo?repo={}", pds, urlencoding::encode(did));
|
||||
if let Ok(response) = client.get(&describe_url).send().await {
|
||||
if response.status().is_success() {
|
||||
if let Ok(data) = response.json::<Value>().await {
|
||||
if let Some(services) = data["didDoc"]["service"].as_array() {
|
||||
if let Some(pds_service) = services.iter().find(|s|
|
||||
s["id"] == "#atproto_pds" || s["type"] == "AtprotoPersonalDataServer"
|
||||
) {
|
||||
if let Some(endpoint) = pds_service["serviceEndpoint"].as_str() {
|
||||
resolved_pds = Some(get_network_config_from_pds(endpoint));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Use resolved PDS or fallback to Bluesky
|
||||
let network_config = resolved_pds.unwrap_or_else(|| get_network_config("bsky.social"));
|
||||
let url = format!("{}/xrpc/app.bsky.actor.getProfile?actor={}",
|
||||
network_config.bsky_api, urlencoding::encode(did));
|
||||
|
||||
let response = client.get(&url).send().await?;
|
||||
|
||||
@ -557,6 +659,26 @@ async fn resolve_handle(did: &str) -> Result<String> {
|
||||
Ok(handle.to_string())
|
||||
}
|
||||
|
||||
// Helper function to get network config from PDS endpoint
|
||||
fn get_network_config_from_pds(pds_endpoint: &str) -> NetworkConfig {
|
||||
if pds_endpoint.contains("syu.is") {
|
||||
NetworkConfig {
|
||||
pds_api: pds_endpoint.to_string(),
|
||||
plc_api: "https://plc.syu.is".to_string(),
|
||||
bsky_api: "https://bsky.syu.is".to_string(),
|
||||
web_url: "https://web.syu.is".to_string(),
|
||||
}
|
||||
} else {
|
||||
// Default to Bluesky infrastructure
|
||||
NetworkConfig {
|
||||
pds_api: pds_endpoint.to_string(),
|
||||
plc_api: "https://plc.directory".to_string(),
|
||||
bsky_api: "https://public.api.bsky.app".to_string(),
|
||||
web_url: "https://bsky.app".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn update_user_list(config: &mut AuthConfig, did: &str, handle: &str) -> Result<()> {
|
||||
// Get current user list
|
||||
let current_users = get_current_user_list(config).await?;
|
||||
@ -569,18 +691,36 @@ async fn update_user_list(config: &mut AuthConfig, did: &str, handle: &str) -> R
|
||||
|
||||
println!(" ➕ Adding new user to list: {}", handle.green());
|
||||
|
||||
// Detect PDS
|
||||
let pds = if handle.ends_with(".syu.is") {
|
||||
"https://syu.is"
|
||||
} else {
|
||||
"https://bsky.social"
|
||||
};
|
||||
// Detect PDS using proper resolution from DID
|
||||
let client = reqwest::Client::new();
|
||||
let pds_endpoints = ["https://bsky.social", "https://syu.is"];
|
||||
let mut detected_pds = "https://bsky.social".to_string(); // Default fallback
|
||||
|
||||
for pds in &pds_endpoints {
|
||||
let describe_url = format!("{}/xrpc/com.atproto.repo.describeRepo?repo={}", pds, urlencoding::encode(did));
|
||||
if let Ok(response) = client.get(&describe_url).send().await {
|
||||
if response.status().is_success() {
|
||||
if let Ok(data) = response.json::<Value>().await {
|
||||
if let Some(services) = data["didDoc"]["service"].as_array() {
|
||||
if let Some(pds_service) = services.iter().find(|s|
|
||||
s["id"] == "#atproto_pds" || s["type"] == "AtprotoPersonalDataServer"
|
||||
) {
|
||||
if let Some(endpoint) = pds_service["serviceEndpoint"].as_str() {
|
||||
detected_pds = endpoint.to_string();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add new user
|
||||
let new_user = UserRecord {
|
||||
did: did.to_string(),
|
||||
handle: handle.to_string(),
|
||||
pds: pds.to_string(),
|
||||
pds: detected_pds,
|
||||
};
|
||||
|
||||
let mut updated_users = current_users;
|
||||
@ -891,7 +1031,8 @@ async fn poll_comments_periodically(mut config: AuthConfig) -> Result<()> {
|
||||
println!(" 👤 Author DID: {}", did);
|
||||
|
||||
// Resolve handle and update user list
|
||||
match resolve_handle(&did).await {
|
||||
let ai_config = load_ai_config_from_project().unwrap_or_default();
|
||||
match resolve_handle(&did, &ai_config.network).await {
|
||||
Ok(handle) => {
|
||||
println!(" 🏷️ Handle: {}", handle.cyan());
|
||||
|
||||
@ -1311,8 +1452,32 @@ fn extract_date_from_slug(slug: &str) -> String {
|
||||
}
|
||||
|
||||
async fn get_ai_profile(client: &reqwest::Client, ai_config: &AiConfig) -> Result<serde_json::Value> {
|
||||
// Resolve AI's actual PDS first
|
||||
let pds_endpoints = ["https://bsky.social", "https://syu.is"];
|
||||
let mut network_config = get_network_config("bsky.social"); // Default fallback
|
||||
|
||||
for pds in &pds_endpoints {
|
||||
let describe_url = format!("{}/xrpc/com.atproto.repo.describeRepo?repo={}", pds, urlencoding::encode(&ai_config.ai_did));
|
||||
if let Ok(response) = client.get(&describe_url).send().await {
|
||||
if response.status().is_success() {
|
||||
if let Ok(data) = response.json::<Value>().await {
|
||||
if let Some(services) = data["didDoc"]["service"].as_array() {
|
||||
if let Some(pds_service) = services.iter().find(|s|
|
||||
s["id"] == "#atproto_pds" || s["type"] == "AtprotoPersonalDataServer"
|
||||
) {
|
||||
if let Some(endpoint) = pds_service["serviceEndpoint"].as_str() {
|
||||
network_config = get_network_config_from_pds(endpoint);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let url = format!("{}/xrpc/app.bsky.actor.getProfile?actor={}",
|
||||
ai_config.bsky_api, urlencoding::encode(&ai_config.ai_did));
|
||||
network_config.bsky_api, urlencoding::encode(&ai_config.ai_did));
|
||||
|
||||
let response = client
|
||||
.get(&url)
|
||||
|
Reference in New Issue
Block a user