this repo has no description
0
fork

Configure Feed

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

wip for atpagent

+123 -16
+6 -1
.env.template
··· 7 7 #REDIS_URL=redis://127.0.0.1:6379/ 8 8 PROD=true 9 9 # Set's the hostname for oauth 10 - #OAUTH_HOST=advent.codes 10 + #OAUTH_HOST=advent.codes 11 + 12 + # Challenge account. The account that writes some records for challenges 13 + CHALLENGE_PDS=https://skeetcentral.com 14 + CHALLENGE_IDENTITY= 15 + CHALLENGE_PASSWORD=
+14 -2
Cargo.lock
··· 216 216 217 217 [[package]] 218 218 name = "atrium-xrpc" 219 - version = "0.12.3" 219 + version = "0.12.4" 220 220 source = "registry+https://github.com/rust-lang/crates.io-index" 221 - checksum = "0216ad50ce34e9ff982e171c3659e65dedaa2ed5ac2994524debdc9a9647ffa8" 221 + checksum = "944b35cc08732d40ddbb3356be9e38d11aed4b4c40c33f5b0f235e0650eff296" 222 222 dependencies = [ 223 223 "http", 224 224 "serde", ··· 226 226 "serde_json", 227 227 "thiserror 1.0.69", 228 228 "trait-variant", 229 + ] 230 + 231 + [[package]] 232 + name = "atrium-xrpc-client" 233 + version = "0.5.15" 234 + source = "registry+https://github.com/rust-lang/crates.io-index" 235 + checksum = "d9d1c0ebd71047b85ccfcad936ded56179af24beb60239ab590c549d2325e679" 236 + dependencies = [ 237 + "atrium-xrpc", 238 + "reqwest", 229 239 ] 230 240 231 241 [[package]] ··· 2758 2768 "atrium-common", 2759 2769 "atrium-identity", 2760 2770 "atrium-oauth", 2771 + "atrium-xrpc-client", 2761 2772 "axum", 2762 2773 "bb8", 2763 2774 "bb8-redis", ··· 3716 3727 "atrium-common", 3717 3728 "atrium-identity", 3718 3729 "atrium-oauth", 3730 + "atrium-xrpc-client", 3719 3731 "axum", 3720 3732 "axum-embed", 3721 3733 "bb8",
+1
Cargo.toml
··· 8 8 atrium-common = "0.1.3" 9 9 atrium-api = "0.25.7" 10 10 atrium-identity = "0.1.8" 11 + atrium-xrpc-client = "0.5.15" 11 12 atrium-oauth = "0.1.6" 12 13 chrono = { version = "0.4", features = ["serde", "now"] } 13 14 hickory-resolver = "0.24.1"
+1
shared/Cargo.toml
··· 22 22 rand = "0.9.2" 23 23 handlebars = { version = "6.3.2" } 24 24 async-trait = "0.1.88" 25 + atrium-xrpc-client.workspace = true
+29 -4
shared/challenges_markdown/two/part_one.md
··· 1 - Hey! Welcome to at://advent! A 25 day challenge to learn atproto with a new set of challenges every day. 1 + Most data on the [atmosphere](https://atproto.com/guides/glossary#atmosphere) is public and stored in things called 2 + [records](https://atproto.com/guides/glossary#record) in the 3 + user's [repo](https://atproto.com/guides/glossary#data-repo) on the 4 + [PDS](https://atproto.com/guides/glossary#pds-personal-data-server). These are stored as key/values in the user's repo 5 + divided by [collections](https://atproto.com/guides/glossary#collection). The API shows them as JSON via the api. Like 6 + this 7 + record of a Bluesky "like" 8 + 9 + ```json 10 + { 11 + "$type": "app.bsky.feed.like", 12 + "subject": { 13 + "cid": "bafyreih2odjtzokqgwajtz6ern6h2gsz5humba3thz7hvt6qkj755jvn34", 14 + "uri": "at://did:plc:hdhoaan3xa3jiuq4fg4mefid/app.bsky.feed.post/3mabnbs3vbs2i" 15 + }, 16 + "createdAt": "2026-03-17T20:15:52.571Z" 17 + } 18 + ``` 2 19 3 - (Pretend this is going into more details explaining everything) 20 + The records are stored in collections and have record keys. This is usually expressed by an at-uri like 21 + `at://did:plc:rnpkyqnmsw4ipey6eotbdnnf/app.bsky.feed.like/3mhbs2cnrl22r`. 4 22 5 - Starting out simple, create a record at the collection `codes.advent.challenge.day` 6 - with the record key `1` and put this as the record. 23 + This uri can be broken down into 3 parts: 24 + 25 + - `at://did:plc:rnpkyqnmsw4ipey6eotbdnnf` is the DID of the user 26 + - `app.bsky.feed.like` is the collection 27 + - `3mhbs2cnrl22r` is the record key 28 + 29 + Using what you learned from day 1 find the following record {generate the at://uri here} and enter the verification code 30 + found in the record 31 + for it below 7 32 8 33 ```json 9 34 {
+30 -2
shared/src/advent/challenges/day_two.rs
··· 1 - use crate::OAuthAgentType; 2 1 use crate::advent::day::Day; 3 - use crate::advent::{AdventChallenge, AdventError, ChallengeCheckResponse}; 2 + use crate::advent::{ 3 + AdventChallenge, AdventError, AdventPart, ChallengeCheckResponse, get_random_token, 4 + }; 4 5 use crate::atrium::safe_check_unknown_record_parse; 5 6 use crate::lexicons::codes::advent; 7 + use crate::{OAuthAgentType, PasswordAgent}; 6 8 use async_trait::async_trait; 7 9 use atrium_api::types::Collection; 8 10 use sqlx::PgPool; ··· 10 12 pub struct DayTwo { 11 13 pub pool: PgPool, 12 14 pub oauth_client: Option<OAuthAgentType>, 15 + pub challenge_agent: Option<PasswordAgent>, 13 16 } 14 17 15 18 #[async_trait] ··· 26 29 true 27 30 } 28 31 32 + /// We are overriding the start challenge and using extra code 33 + async fn start_challenge(&self, did: String, part: AdventPart) -> Result<String, AdventError> { 34 + let code = get_random_token(); 35 + match part { 36 + AdventPart::One => sqlx::query( 37 + "INSERT INTO challenges (user_did, day, time_started, verification_code_one) 38 + VALUES ($1, $2, NOW(), $3) 39 + ON CONFLICT (user_did, day) 40 + DO UPDATE SET verification_code_one = $3 41 + WHERE challenges.user_did = $1 AND challenges.day = $2", 42 + ), 43 + //TODO just going leave these as an update. It should never ideally be an insert 44 + AdventPart::Two => sqlx::query( 45 + "UPDATE challenges 46 + SET verification_code_two = $3 47 + WHERE challenges.user_did = $1 AND challenges.day = $2", 48 + ), 49 + } 50 + .bind(did) 51 + .bind(self.day() as i16) 52 + .bind(code.clone()) 53 + .execute(self.pool()) 54 + .await?; 55 + Ok(code) 56 + } 29 57 async fn check_part_one( 30 58 &self, 31 59 did: String,
+7 -1
shared/src/lib.rs
··· 2 2 3 3 use crate::atrium::dns_resolver::HickoryDnsTxtResolver; 4 4 use crate::atrium::stores::{AtriumSessionStore, AtriumStateStore}; 5 - use atrium_api::agent::Agent; 5 + use atrium_api::agent::atp_agent::AtpAgent; 6 + use atrium_api::{ 7 + agent::Agent, agent::atp_agent::CredentialSession, agent::atp_agent::store::MemorySessionStore, 8 + }; 6 9 use atrium_identity::did::CommonDidResolver; 7 10 use atrium_identity::handle::AtprotoHandleResolver; 8 11 use atrium_oauth::{DefaultHttpClient, OAuthClient}; 12 + use atrium_xrpc_client::reqwest::ReqwestClient; 9 13 use std::sync::Arc; 10 14 11 15 pub mod advent; ··· 41 45 AtriumSessionStore, 42 46 >, 43 47 >; 48 + 49 + pub type PasswordAgent = Arc<AtpAgent<MemorySessionStore, ReqwestClient>>;
+1
web/Cargo.toml
··· 29 29 hypertext = "0.12.1" 30 30 axum-embed = "0.1.0" 31 31 rust-embed.workspace = true 32 + atrium-xrpc-client.workspace = true 32 33 33 34 [build-dependencies] 34 35 pool = "0.1.4"
+13 -4
web/src/handlers/day.rs
··· 34 34 Day::Two => Ok(Box::new(DayTwo { 35 35 pool: state.postgres_pool, 36 36 oauth_client, 37 + challenge_agent: state.challenge_agent, 37 38 })), 38 39 Day::Three => Ok(Box::new(DayThree { 39 40 pool: state.postgres_pool, ··· 338 339 ), 339 340 ) 340 341 .await?; 341 - Ok(Redirect::to(format!("/day/{}#part_two", day as u8).as_str())) 342 + Ok(Redirect::to( 343 + format!("/day/{}#part_two", day as u8).as_str(), 344 + )) 342 345 } 343 346 ChallengeCheckResponse::Incorrect(message) => { 344 347 set_flash_message( ··· 383 386 ), 384 387 ) 385 388 .await?; 386 - Ok(Redirect::to(format!("/day/{}#part_two", day as u8).as_str())) 389 + Ok(Redirect::to( 390 + format!("/day/{}#part_two", day as u8).as_str(), 391 + )) 387 392 } 388 393 ChallengeCheckResponse::Incorrect(message) => { 389 394 set_flash_message( ··· 392 397 FlashMessage::Error(message), 393 398 ) 394 399 .await?; 395 - Ok(Redirect::to(format!("/day/{}#part_two", day as u8).as_str())) 400 + Ok(Redirect::to( 401 + format!("/day/{}#part_two", day as u8).as_str(), 402 + )) 396 403 } 397 404 } 398 405 } 399 - CompletionStatus::Both => Ok(Redirect::to(format!("/day/{}#part_two", day as u8).as_str())), 406 + CompletionStatus::Both => Ok(Redirect::to( 407 + format!("/day/{}#part_two", day as u8).as_str(), 408 + )), 400 409 } 401 410 } 402 411 }
+21 -2
web/src/main.rs
··· 4 4 templates::error::ErrorTemplate, 5 5 templates::home::{DayStatus, HomeTemplate}, 6 6 }; 7 - use atrium_common::resolver::Resolver; 7 + use atrium_api::agent::Agent; 8 + use atrium_api::agent::atp_agent::store::MemorySessionStore; 9 + use atrium_api::agent::atp_agent::{AtpAgent, CredentialSession}; 8 10 use atrium_identity::{ 9 11 did::{CommonDidResolver, CommonDidResolverConfig, DEFAULT_PLC_DIRECTORY_URL}, 10 12 handle::{AtprotoHandleResolver, AtprotoHandleResolverConfig}, ··· 13 15 AtprotoClientMetadata, AtprotoLocalhostClientMetadata, AuthMethod, DefaultHttpClient, 14 16 GrantType, KnownScope, OAuthClient, OAuthClientConfig, OAuthResolverConfig, Scope, 15 17 }; 18 + use atrium_xrpc_client::reqwest::ReqwestClient; 16 19 use axum::{ 17 20 Router, 18 21 extract::State, ··· 27 30 use dotenv::dotenv; 28 31 use redis::AsyncCommands; 29 32 use rust_embed::RustEmbed; 33 + use shared::advent::day::Day::Nine; 30 34 use shared::{ 31 - HandleResolver, OAuthClientType, 35 + HandleResolver, OAuthClientType, PasswordAgent, 32 36 advent::{CompletionStatus, get_all_days_completion_status}, 33 37 atrium::dns_resolver::HickoryDnsTxtResolver, 34 38 atrium::stores::AtriumSessionStore, ··· 67 71 oauth_client: OAuthClientType, 68 72 //Used to get did to handle leaving because I figured we'd need it 69 73 handle_resolver: HandleResolver, 74 + challenge_agent: Option<PasswordAgent>, 70 75 } 71 76 72 77 pub fn oauth_scopes() -> Vec<Scope> { ··· 221 226 .with_same_site(SameSite::Lax) 222 227 .with_secure(false); 223 228 229 + // challenge account 230 + let mut challenge_agent = None; 231 + let challenge_pds = env::var("CHALLENGE_PDS"); 232 + let challenge_identity = env::var("CHALLENGE_IDENTITY"); 233 + let challenge_password = env::var("CHALLENGE_PASSWORD"); 234 + if let (Ok(pds), Ok(identity), Ok(password)) = 235 + (challenge_pds, challenge_identity, challenge_password) 236 + { 237 + let agent = AtpAgent::new(ReqwestClient::new(pds), MemorySessionStore::default()); 238 + agent.login(identity, password).await?; 239 + challenge_agent = Some(Arc::new(agent)); 240 + } 241 + 224 242 let app_state = AppState { 225 243 postgres_pool, 226 244 redis_pool, 227 245 oauth_client: client, 228 246 handle_resolver, 247 + challenge_agent, 229 248 }; 230 249 //HACK Yeah I don't like it either - bt 231 250 let prod: bool = env::var("PROD")