this repo has no description
0
fork

Configure Feed

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

post the things!!!!!

phil 62ce51d3 98a37837

+163 -2
+1
shared/src/atrium/mod.rs
··· 2 2 use serde::de; 3 3 4 4 pub mod dns_resolver; 5 + pub mod post; 5 6 pub mod service_auth; 6 7 pub mod stores; 7 8
+135
shared/src/atrium/post.rs
··· 1 + use crate::PasswordAgent; 2 + use crate::HandleResolver; 3 + use atrium_api::app::bsky::feed::post::RecordData; 4 + use atrium_api::app::bsky::richtext::facet; 5 + use atrium_api::types::string::Datetime; 6 + use atrium_api::types::TryIntoUnknown; 7 + use atrium_common::resolver::Resolver; 8 + 9 + /// Fire-and-forget: spawns a task that posts a completion announcement from the 10 + /// challenge agent's account, mentioning the user who completed the challenge. 11 + pub fn post_completion( 12 + agent: Option<&PasswordAgent>, 13 + resolver: &HandleResolver, 14 + user_did: &str, 15 + day: u8, 16 + part: u8, 17 + ) { 18 + let Some(agent) = agent.cloned() else { 19 + return; 20 + }; 21 + let resolver = resolver.clone(); 22 + let user_did = user_did.to_string(); 23 + 24 + tokio::spawn(async move { 25 + let handle = resolve_handle(&resolver, &user_did).await; 26 + let (text, facets) = build_post_text(&handle, &user_did, day, part); 27 + post_status(&agent, text, facets).await; 28 + }); 29 + } 30 + 31 + async fn resolve_handle(resolver: &HandleResolver, did: &str) -> Option<String> { 32 + let did = match did.parse() { 33 + Ok(d) => d, 34 + Err(e) => { 35 + log::warn!("completion post: bad DID {did}: {e}"); 36 + return None; 37 + } 38 + }; 39 + match resolver.resolve(&did).await { 40 + Ok(doc) => doc 41 + .also_known_as 42 + .and_then(|aka: Vec<String>| aka.into_iter().find(|s| s.starts_with("at://"))) 43 + .map(|uri: String| uri.strip_prefix("at://").unwrap_or(&uri).to_string()), 44 + Err(e) => { 45 + log::warn!("completion post: failed to resolve {did:?}: {e}"); 46 + None 47 + } 48 + } 49 + } 50 + 51 + fn build_post_text(handle: &Option<String>, did: &str, day: u8, part: u8) -> (String, Option<Vec<facet::Main>>) { 52 + let mention_text = match handle { 53 + Some(h) => format!("@{h}"), 54 + None => did.to_string(), 55 + }; 56 + 57 + let byte_start = 6; 58 + 59 + let text = match part { 60 + 1 => format!("Wooo! {mention_text} just completed the first part of Day {day} of at://advent!!"), 61 + 2 => format!("Whoa, {mention_text} just did the part part of Day {day} of at://advent!!!"), 62 + n => format!("{mention_text} just broke the site for Day {day}, Part {n}????!") 63 + }; 64 + 65 + let facets = handle.as_ref().map(|_| { 66 + vec![facet::MainData { 67 + index: facet::ByteSliceData { 68 + byte_start: byte_start, 69 + byte_end: byte_start + mention_text.len(), 70 + } 71 + .into(), 72 + features: vec![atrium_api::types::Union::Refs( 73 + facet::MainFeaturesItem::Mention(Box::new( 74 + facet::MentionData { 75 + did: did.parse().unwrap(), 76 + } 77 + .into(), 78 + )), 79 + )], 80 + } 81 + .into()] 82 + }); 83 + 84 + (text, facets) 85 + } 86 + 87 + async fn post_status(agent: &PasswordAgent, text: String, facets: Option<Vec<facet::Main>>) { 88 + let Some(agent_did) = agent.did().await else { 89 + log::error!("completion post: agent has no DID"); 90 + return; 91 + }; 92 + 93 + let record = RecordData { 94 + created_at: Datetime::now(), 95 + embed: None, 96 + entities: None, 97 + facets, 98 + labels: None, 99 + langs: Some(vec!["en".parse().unwrap()]), 100 + reply: None, 101 + tags: None, 102 + text, 103 + }; 104 + 105 + let record_value = match record.try_into_unknown() { 106 + Ok(v) => v, 107 + Err(e) => { 108 + log::error!("completion post: failed to serialize record: {e}"); 109 + return; 110 + } 111 + }; 112 + 113 + let result = agent 114 + .api 115 + .com 116 + .atproto 117 + .repo 118 + .create_record( 119 + atrium_api::com::atproto::repo::create_record::InputData { 120 + collection: "app.bsky.feed.post".parse().unwrap(), 121 + repo: agent_did.as_ref().parse().unwrap(), 122 + rkey: None, 123 + record: record_value, 124 + swap_commit: None, 125 + validate: None, 126 + } 127 + .into(), 128 + ) 129 + .await; 130 + 131 + match result { 132 + Ok(output) => log::info!("completion post created: {}", output.uri), 133 + Err(e) => log::error!("completion post failed: {e}"), 134 + } 135 + }
+9
web/src/handlers/custom/day_three.rs
··· 101 101 return Ok(Redirect::to("/day/3#part_two")); 102 102 } 103 103 104 + let did_clone = did.clone(); 104 105 challenge 105 106 .complete_part_two(did) 106 107 .await 107 108 .map_err(log_and_respond(StatusCode::INTERNAL_SERVER_ERROR, "Error completing"))?; 109 + 110 + shared::atrium::post::post_completion( 111 + state.challenge_agent.as_ref(), 112 + &state.handle_resolver, 113 + &did_clone, 114 + 3, 115 + 2, 116 + ); 108 117 109 118 set_flash_message( 110 119 &mut session,
+18 -2
web/src/handlers/day.rs
··· 379 379 .map_err(log_and_respond(StatusCode::BAD_REQUEST, "Invalid DID"))?; 380 380 381 381 let day = Day::from(day); 382 + let challenge_agent = state.challenge_agent.clone(); 383 + let handle_resolver = state.handle_resolver.clone(); 382 384 let client = oauth_client.restore(&did).await.map_err(log_and_respond( 383 385 StatusCode::INTERNAL_SERVER_ERROR, 384 386 "There was an error restoring the oauth client", ··· 410 412 ))?; 411 413 match result { 412 414 ChallengeCheckResponse::Correct => { 413 - challenge.complete_part_one(did_as_string).await.map_err( 415 + challenge.complete_part_one(did_as_string.clone()).await.map_err( 414 416 log_and_respond( 415 417 StatusCode::INTERNAL_SERVER_ERROR, 416 418 "Error completing part one", 417 419 ), 418 420 )?; 421 + shared::atrium::post::post_completion( 422 + challenge_agent.as_ref(), 423 + &handle_resolver, 424 + &did_as_string, 425 + day as u8, 426 + 1, 427 + ); 419 428 set_flash_message( 420 429 &mut session, 421 430 "part_one_result", ··· 457 466 458 467 match result { 459 468 ChallengeCheckResponse::Correct => { 460 - challenge.complete_part_two(did_as_string).await.map_err( 469 + challenge.complete_part_two(did_as_string.clone()).await.map_err( 461 470 log_and_respond( 462 471 StatusCode::INTERNAL_SERVER_ERROR, 463 472 "Error completing part two", 464 473 ), 465 474 )?; 475 + shared::atrium::post::post_completion( 476 + challenge_agent.as_ref(), 477 + &handle_resolver, 478 + &did_as_string, 479 + day as u8, 480 + 2, 481 + ); 466 482 set_flash_message( 467 483 &mut session, 468 484 "part_two_result",