this repo has no description
0
fork

Configure Feed

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

ui fixes and reordering of days

+273 -20
+31
shared/challenges_markdown/one/part_one.md
··· 1 + Welcome to at://advent! An atproto adventure to learn [atproto](https://atproto.com/) in an interactive and 2 + hopefully fun way in the style of advent of code. 3 + 4 + The majority of these challenges can be done without writing any code, but using community tooling. But you may find 5 + writing code will also be a good exercise to get your feet wet. Some tools that may be helpful are: 6 + 7 + - [goat](https://github.com/bluesky-social/goat) - Swiss army knife of a CLI that allows you to do most if not all 8 + atproto actions with. 9 + - [pdsls](https://pds.ls/) - A web browser base atproto explorer (and more). Will allow you to view records, create 10 + records, PLC data, download/view repo exports, and listen to firehose or jetstream. 11 + - [boat](https://boat.kelinci.net/) - Various atporot odds & ends tools. PLC, download/view repos, and more. 12 + - [atproto.at](https://atproto.at/) - A web browser base atproto explorer (and more). Will allow you to view records, 13 + create records, PLC data, download/view a users repo exports, and listen to the jetstream for that user. May need to 14 + turn on developer mode for the more advance options. 15 + 16 + We are going to start simple. Everyone knows they have a handle, but `did` you know you also have a unique id called a 17 + [did](https://atproto.com/specs/did). This is a unique identifier for your identity that never changes. This comes in 18 + two flavors for atproto [did:plc](https://web.plc.directory/) 19 + and [did:web](https://atproto.com/specs/did#did-web-in-at-protocol). 20 + Each one have a unique way to find something called a [DID document](https://atproto.com/specs/did#did-documents). Your 21 + did document holds various information about your identity. Things like where does your PDS live, the key that signs 22 + your repo, handle, and more. 23 + 24 + # plc 25 + 26 + talk about where to find the PLC 27 + 28 + # did:web 29 + 30 + talk about how to find your did:web (altho you probably already know this) 31 + 1 32 Hey! Welcome to at://advent! A 25 day challenge to learn atproto with a new set of challenges every day. 2 33 3 34 (Pretend this is going into more details explaining everything)
+2
shared/challenges_markdown/one/part_two.md
··· 1 + This will be submitting their public verification code from the did doc since each did doc will have one. 2 + 1 3 Great job beating Part 1! Now onto Part 2. 2 4 3 5 Keeping it simple proof of concept, blah, blah will have a real one here another time. Add a new field `partTwo` to the
+5
shared/challenges_markdown/three/part_one.md
··· 1 + Great job beating Part 1! Now onto Part 2. 2 + 3 + Keeping it simple proof of concept, blah, blah will have a real one here another time. Add a new field `partTwo` to the 4 + record with the value `{{code}}` 5 +
+14 -4
shared/challenges_markdown/two/part_one.md
··· 1 - Great job beating Part 1! Now onto Part 2. 1 + Hey! Welcome to at://advent! A 25 day challenge to learn atproto with a new set of challenges every day. 2 + 3 + (Pretend this is going into more details explaining everything) 2 4 3 - Keeping it simple proof of concept, blah, blah will have a real one here another time. Add a new field `partTwo` to the 4 - record with the value `{{code}}` 5 - 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. 7 + 8 + ```json 9 + { 10 + "$type": "codes.advent.challenge.day", 11 + "partOne": "{{code}}" 12 + } 13 + ``` 14 + 15 + [//]: # (<input type="file" id="part_one_input" placeholder="Enter your code here" />)
+7
shared/challenges_markdown/two/part_two.md
··· 1 + Great job beating Part 1! Now onto Part 2. 2 + 3 + Keeping it simple proof of concept, blah, blah will have a real one here another time. Add a new field `partTwo` to the 4 + record with the value `{{code}}` 5 + 6 + 7 + [//]: # (<input type="file" id="part_one_input" placeholder="Enter your code here" />)
+37
shared/src/advent/challenges/day_three.rs
··· 1 + use crate::OAuthAgentType; 2 + use crate::advent::day::Day; 3 + use crate::advent::{AdventChallenge, AdventError, ChallengeCheckResponse}; 4 + use async_trait::async_trait; 5 + use sqlx::PgPool; 6 + 7 + pub struct DayTwo { 8 + pub pool: PgPool, 9 + pub oauth_client: Option<OAuthAgentType>, 10 + } 11 + 12 + #[async_trait] 13 + impl AdventChallenge for DayTwo { 14 + fn pool(&self) -> &PgPool { 15 + &self.pool 16 + } 17 + 18 + fn day(&self) -> Day { 19 + Day::Two 20 + } 21 + 22 + fn has_part_two(&self) -> bool { 23 + false 24 + } 25 + 26 + fn requires_manual_verification_part_one(&self) -> bool { 27 + true 28 + } 29 + 30 + async fn check_part_one( 31 + &self, 32 + _did: String, 33 + _verification_code: Option<String>, 34 + ) -> Result<ChallengeCheckResponse, AdventError> { 35 + todo!() 36 + } 37 + }
+160 -9
shared/src/advent/challenges/day_two.rs
··· 1 1 use crate::OAuthAgentType; 2 2 use crate::advent::day::Day; 3 3 use crate::advent::{AdventChallenge, AdventError, ChallengeCheckResponse}; 4 + use crate::atrium::safe_check_unknown_record_parse; 5 + use crate::lexicons::codes::advent; 4 6 use async_trait::async_trait; 7 + use atrium_api::types::Collection; 5 8 use sqlx::PgPool; 6 9 7 - pub struct DayTwo { 10 + pub struct DayOne { 8 11 pub pool: PgPool, 9 12 pub oauth_client: Option<OAuthAgentType>, 10 13 } 11 14 12 15 #[async_trait] 13 - impl AdventChallenge for DayTwo { 16 + impl AdventChallenge for DayOne { 14 17 fn pool(&self) -> &PgPool { 15 18 &self.pool 16 19 } 17 20 18 21 fn day(&self) -> Day { 19 - Day::Two 22 + Day::One 20 23 } 21 24 22 25 fn has_part_two(&self) -> bool { 23 - false 26 + true 24 27 } 25 28 26 - fn requires_manual_verification_part_one(&self) -> bool { 27 - true 29 + async fn check_part_one( 30 + &self, 31 + did: String, 32 + _verification_code: Option<String>, 33 + ) -> Result<ChallengeCheckResponse, AdventError> { 34 + let client = self 35 + .oauth_client 36 + .as_ref() 37 + .ok_or(AdventError::ShouldNotHappen( 38 + "No oauth client. This should not happen".to_string(), 39 + ))?; 40 + 41 + let record_res = client 42 + .api 43 + .com 44 + .atproto 45 + .repo 46 + .get_record( 47 + atrium_api::com::atproto::repo::get_record::ParametersData { 48 + cid: None, 49 + collection: advent::challenge::Day::NSID.parse().unwrap(), 50 + repo: did.parse().unwrap(), 51 + rkey: "1".parse().unwrap(), 52 + } 53 + .into(), 54 + ) 55 + .await; 56 + 57 + let record = match record_res { 58 + Ok(r) => r, 59 + Err(e) => { 60 + log::error!("Error getting record: {}", e); 61 + return Ok(ChallengeCheckResponse::Incorrect("Does not appear to be a record in your repo in the collection codes.advent.challenge.day with the record key of 1".to_string())); 62 + } 63 + }; 64 + 65 + let Some(challenge) = self.get_days_challenge(&did).await? else { 66 + log::error!("Could not find a challenge record for day: 1 for the user: {did:?}"); 67 + return Err(AdventError::ShouldNotHappen( 68 + "Could not find challenge record".to_string(), 69 + )); 70 + }; 71 + 72 + let record_data = match safe_check_unknown_record_parse::<advent::challenge::day::RecordData>( 73 + record.value.clone(), 74 + ) { 75 + Ok(rd) => rd, 76 + Err(e) => { 77 + log::error!("Error parsing record: {e}"); 78 + return Ok(ChallengeCheckResponse::Incorrect(format!( 79 + "There is a record at the correct location, but it does not seem like it is correct. Try again:\n{e}" 80 + ))); 81 + } 82 + }; 83 + 84 + let verification_code = 85 + challenge 86 + .verification_code_one 87 + .ok_or(AdventError::ShouldNotHappen( 88 + "no verification code for day 1 challenge :/".to_string(), 89 + ))?; 90 + 91 + Ok(if record_data.part_one == verification_code { 92 + ChallengeCheckResponse::Correct 93 + } else { 94 + ChallengeCheckResponse::Incorrect(format!( 95 + "The code {} is incorrect", 96 + record_data.part_one 97 + )) 98 + }) 28 99 } 29 100 30 - async fn check_part_one( 101 + ///TODO this is just a straight copy and paste of part one since it's a proof of concept needs to share code better between the two 102 + async fn check_part_two( 31 103 &self, 32 - _did: String, 104 + did: String, 33 105 _verification_code: Option<String>, 34 106 ) -> Result<ChallengeCheckResponse, AdventError> { 35 - todo!() 107 + match &self.oauth_client { 108 + None => Err(AdventError::ShouldNotHappen( 109 + "No oauth client. This should not happen".to_string(), 110 + )), 111 + Some(client) => { 112 + match client 113 + .api 114 + .com 115 + .atproto 116 + .repo 117 + .get_record( 118 + atrium_api::com::atproto::repo::get_record::ParametersData { 119 + cid: None, 120 + collection: advent::challenge::Day::NSID.parse().unwrap(), 121 + repo: did.parse().unwrap(), 122 + rkey: "1".parse().unwrap(), 123 + } 124 + .into(), 125 + ) 126 + .await 127 + { 128 + Ok(record) => { 129 + //TODO trouble, and make it double 130 + let challenge = self.get_days_challenge(&did).await?; 131 + 132 + match challenge { 133 + None => { 134 + log::error!( 135 + "Could not find a challenge record for day: {} for the user: {}", 136 + self.day(), 137 + did.clone() 138 + ); 139 + Err(AdventError::ShouldNotHappen( 140 + "Could not find a challenge record".to_string(), 141 + )) 142 + } 143 + Some(challenge) => { 144 + let parse_record_result = 145 + safe_check_unknown_record_parse::< 146 + advent::challenge::day::RecordData, 147 + >(record.value.clone()); 148 + 149 + match parse_record_result { 150 + Ok(record_data) => match record_data.part_two { 151 + None => { 152 + Ok(ChallengeCheckResponse::Incorrect("The record is there, it's the right kind. But aren't you forgetting something?".to_string())) 153 + } 154 + Some(part_two_code) => { 155 + match part_two_code 156 + == challenge 157 + .verification_code_two 158 + .unwrap_or("".to_string()) 159 + { 160 + true => Ok(ChallengeCheckResponse::Correct), 161 + false => { 162 + Ok(ChallengeCheckResponse::Incorrect(format!( 163 + "The code {} is incorrect", 164 + record_data.part_one 165 + ))) 166 + } 167 + } 168 + } 169 + }, 170 + Err(err) => { 171 + log::error!("Error parsing record: {}", err); 172 + Ok(ChallengeCheckResponse::Incorrect(format!( 173 + "There is a record at the correct location, but it does not seem like it is correct. Try again:\n{err}" 174 + ))) 175 + } 176 + } 177 + } 178 + } 179 + } 180 + Err(err) => { 181 + log::error!("Error getting record: {}", err); 182 + Ok(ChallengeCheckResponse::Incorrect("Does not appear to be a record in your repo in the collection codes.advent.challenge.day with the record key of 1".to_string())) 183 + } 184 + } 185 + } 186 + } 36 187 } 37 188 }
+1
shared/src/advent/challenges/mod.rs
··· 1 1 pub mod day_one; 2 + pub mod day_three; 2 3 pub mod day_two;
+2 -2
web/src/handlers/day.rs
··· 11 11 use shared::advent::ChallengeCheckResponse; 12 12 use shared::{ 13 13 OAuthAgentType, OAuthClientType, 14 - advent::challenges::day_one::DayOne, 15 - advent::challenges::day_two::DayTwo, 14 + advent::challenges::day_three::DayTwo, 15 + advent::challenges::day_two::DayOne, 16 16 advent::day::Day, 17 17 advent::{AdventChallenge, AdventError}, 18 18 advent::{AdventPart, CompletionStatus},
+12 -3
web/templates/day.askama.html
··· 52 52 <label class="input validator join-item"> 53 53 <input type="text" name="verification_code_one" placeholder="Enter Part 1 code" required /> 54 54 </label> 55 - <div class="validator-hint hidden">Enter valid email address</div> 55 + <div class="validator-hint">Enter your verification code</div> 56 56 </div> 57 57 <button type="submit" class="btn btn-primary join-item">Check answer</button> 58 58 </div> ··· 109 109 {% if !challenge_two_completed %} 110 110 <form method="post" action="/day/{{ day }}"> 111 111 {% if requires_code_input_part_two %} 112 - <input type="text" name="verification_code_two" placeholder="Enter Part 2 code" class="input input-bordered mr-2" required/> 112 + <div class="join"> 113 + <div> 114 + <label class="input validator join-item"> 115 + <input type="text" name="verification_code_two" placeholder="Enter Part 2 code" required /> 116 + </label> 117 + <div class="validator-hint">Enter your verification code</div> 118 + </div> 119 + <button type="submit" class="btn btn-primary join-item">Check answer</button> 120 + </div> 121 + {% else %} 122 + <button class="btn btn-primary" type="submit">Check answer</button> 113 123 {% endif %} 114 - <button class="btn btn-primary" type="submit">Check answer</button> 115 124 </form> 116 125 {% else %} 117 126 <div class="badge badge-success badge-lg gap-2">
+2 -2
web/templates/login.askama.html
··· 20 20 <div class=""> 21 21 <fieldset class="fieldset"> 22 22 <legend class="fieldset-legend">AT Protocol Handle</legend> 23 - <input id="handle-input" name="handle" type="text" class="input input-md" placeholder="alice.bsky.social" required/> 23 + <input id="handle-input" name="handle" type="text" class="input w-full" placeholder="alice.bsky.social" required/> 24 24 </fieldset> 25 25 26 26 </div> 27 - <div class="form-control mt-6"> 27 + <div class="mt-6"> 28 28 <button class="btn btn-primary" type="submit">Continue</button> 29 29 </div> 30 30 </form>