Parakeet is a Rust-based Bluesky AppServer aiming to implement most of the functionality required to support the Bluesky client
appview atproto bluesky rust appserver
66
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat(parakeet): app.bsky.actor.status labels

Mia 2632a569 36a85770

+85 -7
+2
crates/lexica/src/app_bsky/actor.rs
··· 278 278 pub expires_at: Option<Datetime>, 279 279 #[serde(skip_serializing_if = "Option::is_none")] 280 280 pub is_active: Option<bool>, 281 + #[serde(skip_serializing_if = "Vec::is_empty")] 282 + pub labels: Vec<Label>, 281 283 } 282 284 283 285 #[derive(Clone, Debug, Serialize)]
+20
crates/parakeet/src/hydration/mod.rs
··· 87 87 } 88 88 89 89 #[tracing::instrument(skip_all)] 90 + async fn get_profile_status_label(&self, did: &str) -> Vec<parakeet_db::models::Label> { 91 + let uri = format!("at://{did}/app.bsky.actor.status/self"); 92 + 93 + self.get_label(&uri).await 94 + } 95 + 96 + #[tracing::instrument(skip_all)] 90 97 async fn get_label_many( 91 98 &self, 92 99 uris: &[String], ··· 120 127 } 121 128 122 129 labels 130 + } 131 + 132 + #[tracing::instrument(skip_all)] 133 + async fn get_profile_status_label_many( 134 + &self, 135 + dids: &[String], 136 + ) -> HashMap<String, Vec<parakeet_db::models::Label>> { 137 + let uris = dids 138 + .iter() 139 + .map(|did| format!("at://{did}/app.bsky.actor.status/self")) 140 + .collect::<Vec<_>>(); 141 + 142 + self.get_label_many(&uris).await 123 143 } 124 144 }
+63 -7
crates/parakeet/src/hydration/profile.rs
··· 149 149 } 150 150 } 151 151 152 - fn build_status(status: models::Status, cdn: &BskyCdn) -> Option<StatusView> { 152 + fn build_status( 153 + status: models::Status, 154 + labels: Vec<models::Label>, 155 + cdn: &BskyCdn, 156 + ) -> Option<StatusView> { 153 157 let s = Status::from_str(&status.status).ok()?; 154 158 let embed = status 155 159 .embed_uri ··· 178 182 embed, 179 183 expires_at: expires_at.map(|v| v.into_jacquard()), 180 184 is_active, 185 + labels: map_labels(labels), 181 186 }) 182 187 } 183 188 ··· 185 190 (handle, profile, chat_decl, is_labeler, status, notif_decl): ProfileLoaderRet, 186 191 stats: Option<ProfileStats>, 187 192 labels: Vec<models::Label>, 193 + status_labels: Vec<models::Label>, 188 194 verifications: Option<Vec<models::VerificationEntry>>, 189 195 viewer: Option<ProfileViewerState>, 190 196 cdn: &BskyCdn, 191 197 ) -> ProfileViewBasic { 192 198 let associated = build_associated(chat_decl, is_labeler, stats, notif_decl); 193 199 let verification = build_verification(&profile, &handle, verifications); 194 - let status = status.and_then(|status| build_status(status, cdn)); 200 + let status = status.and_then(|status| build_status(status, status_labels, cdn)); 195 201 let avatar = profile.avatar_cid.map(|cid| cdn.avatar(&profile.did, &cid)); 196 202 197 203 ProfileViewBasic { ··· 213 219 (handle, profile, chat_decl, is_labeler, status, notif_decl): ProfileLoaderRet, 214 220 stats: Option<ProfileStats>, 215 221 labels: Vec<models::Label>, 222 + status_labels: Vec<models::Label>, 216 223 verifications: Option<Vec<models::VerificationEntry>>, 217 224 viewer: Option<ProfileViewerState>, 218 225 cdn: &BskyCdn, 219 226 ) -> ProfileView { 220 227 let associated = build_associated(chat_decl, is_labeler, stats, notif_decl); 221 228 let verification = build_verification(&profile, &handle, verifications); 222 - let status = status.and_then(|status| build_status(status, cdn)); 229 + let status = status.and_then(|status| build_status(status, status_labels, cdn)); 223 230 let avatar = profile.avatar_cid.map(|cid| cdn.avatar(&profile.did, &cid)); 224 231 225 232 ProfileView { ··· 243 250 (handle, profile, chat_decl, is_labeler, status, notif_decl): ProfileLoaderRet, 244 251 stats: Option<ProfileStats>, 245 252 labels: Vec<models::Label>, 253 + status_labels: Vec<models::Label>, 246 254 verifications: Option<Vec<models::VerificationEntry>>, 247 255 viewer: Option<ProfileViewerState>, 248 256 cdn: &BskyCdn, 249 257 ) -> ProfileViewDetailed { 250 258 let associated = build_associated(chat_decl, is_labeler, stats, notif_decl); 251 259 let verification = build_verification(&profile, &handle, verifications); 252 - let status = status.and_then(|status| build_status(status, cdn)); 260 + let status = status.and_then(|status| build_status(status, status_labels, cdn)); 253 261 let avatar = profile.avatar_cid.map(|cid| cdn.avatar(&profile.did, &cid)); 254 262 let banner = profile.banner_cid.map(|cid| cdn.banner(&profile.did, &cid)); 255 263 ··· 283 291 let stats = self.loaders.profile_stats.load(did.clone()).await; 284 292 let profile_info = self.loaders.profile.load(did).await?; 285 293 294 + let status_labels = match &profile_info.4 { 295 + Some(status) => self.get_profile_status_label(&status.did).await, 296 + None => Vec::default(), 297 + }; 298 + 286 299 Some(build_basic( 287 300 profile_info, 288 301 stats, 289 302 labels, 303 + status_labels, 290 304 verif, 291 305 viewer, 292 306 &self.cdn, ··· 299 313 dids: Vec<String>, 300 314 ) -> HashMap<String, ProfileViewBasic> { 301 315 let labels = self.get_profile_label_many(&dids).await; 316 + let status_labels = self.get_profile_status_label_many(&dids).await; 302 317 let viewers = self.get_profile_viewer_states(&dids).await; 303 318 let verif = self.loaders.verification.load_many(dids.clone()).await; 304 319 let stats = self.loaders.profile_stats.load_many(dids.clone()).await; ··· 308 323 .into_iter() 309 324 .map(|(k, profile_info)| { 310 325 let labels = labels.get(&k).cloned().unwrap_or_default(); 326 + let status_labels = status_labels.get(&k).cloned().unwrap_or_default(); 311 327 let verif = verif.get(&k).cloned(); 312 328 let viewer = viewers.get(&k).cloned(); 313 329 let stats = stats.get(&k).cloned(); 314 330 315 - let v = build_basic(profile_info, stats, labels, verif, viewer, &self.cdn); 331 + let v = build_basic( 332 + profile_info, 333 + stats, 334 + labels, 335 + status_labels, 336 + verif, 337 + viewer, 338 + &self.cdn, 339 + ); 316 340 (k, v) 317 341 }) 318 342 .collect() ··· 326 350 let stats = self.loaders.profile_stats.load(did.clone()).await; 327 351 let profile_info = self.loaders.profile.load(did).await?; 328 352 353 + let status_labels = match &profile_info.4 { 354 + Some(status) => self.get_profile_status_label(&status.did).await, 355 + None => Vec::default(), 356 + }; 357 + 329 358 Some(build_profile( 330 359 profile_info, 331 360 stats, 332 361 labels, 362 + status_labels, 333 363 verif, 334 364 viewer, 335 365 &self.cdn, ··· 339 369 #[instrument(skip_all)] 340 370 pub async fn hydrate_profiles(&self, dids: Vec<String>) -> HashMap<String, ProfileView> { 341 371 let labels = self.get_profile_label_many(&dids).await; 372 + let status_labels = self.get_profile_status_label_many(&dids).await; 342 373 let viewers = self.get_profile_viewer_states(&dids).await; 343 374 let verif = self.loaders.verification.load_many(dids.clone()).await; 344 375 let stats = self.loaders.profile_stats.load_many(dids.clone()).await; ··· 348 379 .into_iter() 349 380 .map(|(k, profile_info)| { 350 381 let labels = labels.get(&k).cloned().unwrap_or_default(); 382 + let status_labels = status_labels.get(&k).cloned().unwrap_or_default(); 351 383 let verif = verif.get(&k).cloned(); 352 384 let viewer = viewers.get(&k).cloned(); 353 385 let stats = stats.get(&k).cloned(); 354 386 355 - let v = build_profile(profile_info, stats, labels, verif, viewer, &self.cdn); 387 + let v = build_profile( 388 + profile_info, 389 + stats, 390 + labels, 391 + status_labels, 392 + verif, 393 + viewer, 394 + &self.cdn, 395 + ); 356 396 (k, v) 357 397 }) 358 398 .collect() ··· 366 406 let stats = self.loaders.profile_stats.load(did.clone()).await; 367 407 let profile_info = self.loaders.profile.load(did).await?; 368 408 409 + let status_labels = match &profile_info.4 { 410 + Some(status) => self.get_profile_status_label(&status.did).await, 411 + None => Vec::default(), 412 + }; 413 + 369 414 Some(build_detailed( 370 415 profile_info, 371 416 stats, 372 417 labels, 418 + status_labels, 373 419 verif, 374 420 viewer, 375 421 &self.cdn, ··· 382 428 dids: Vec<String>, 383 429 ) -> HashMap<String, ProfileViewDetailed> { 384 430 let labels = self.get_profile_label_many(&dids).await; 431 + let status_labels = self.get_profile_status_label_many(&dids).await; 385 432 let viewers = self.get_profile_viewer_states(&dids).await; 386 433 let verif = self.loaders.verification.load_many(dids.clone()).await; 387 434 let stats = self.loaders.profile_stats.load_many(dids.clone()).await; ··· 391 438 .into_iter() 392 439 .map(|(k, profile_info)| { 393 440 let labels = labels.get(&k).cloned().unwrap_or_default(); 441 + let status_labels = status_labels.get(&k).cloned().unwrap_or_default(); 394 442 let verif = verif.get(&k).cloned(); 395 443 let viewer = viewers.get(&k).cloned(); 396 444 let stats = stats.get(&k).cloned(); 397 445 398 - let v = build_detailed(profile_info, stats, labels, verif, viewer, &self.cdn); 446 + let v = build_detailed( 447 + profile_info, 448 + stats, 449 + labels, 450 + status_labels, 451 + verif, 452 + viewer, 453 + &self.cdn, 454 + ); 399 455 (k, v) 400 456 }) 401 457 .collect()