···11# Welcome to at://advent!
2233-An atproto adventure to learn [atproto](https://atproto.com/) in an interactive and
33+at://advent is an atproto adventure to learn [atproto](https://atproto.com/) in an interactive and
44hopefully fun way in the style of advent of code.
5566The majority of these challenges can be done without writing any code, but using community tooling. But you may find
···1414- [atproto.at](https://atproto.at/) - A web browser base atproto explorer (and more). Will allow you to view records,
1515 create records, PLC data, download/view a users repo exports, and listen to the jetstream for that user. May need to
1616 turn on developer mode for the more advance options.
1717+- [various sdks](https://atproto.com/sdks) - atproto.com has a list of various sdks if you'd like to write the code to
1818+ solve these challenges.
17191820We are going to start simple. Everyone knows they have a handle, but `did` you know you also have a unique id called a
1921[did](https://atproto.com/specs/did)? This is a unique identifier for your identity that never changes. This comes in
+90-133
shared/src/advent/challenges/day_one.rs
···11-use crate::OAuthAgentType;
21use crate::advent::day::Day;
32use crate::advent::{AdventChallenge, AdventError, ChallengeCheckResponse};
43use crate::atrium::safe_check_unknown_record_parse;
54use crate::lexicons::codes::advent;
55+use crate::{HandleResolver, OAuthAgentType};
66use async_trait::async_trait;
77-use atrium_api::types::Collection;
88-use log::info;
77+use atrium_api::agent::Configure;
88+use atrium_common::resolver::Resolver;
99use sqlx::PgPool;
10101111pub struct DayOne {
1212 pub pool: PgPool,
1313 pub oauth_client: Option<OAuthAgentType>,
1414+ pub handle_resolver: HandleResolver,
1515+}
1616+1717+impl DayOne {
1818+ pub fn new(
1919+ pool: PgPool,
2020+ oauth_client: Option<OAuthAgentType>,
2121+ handle_resolver: HandleResolver,
2222+ ) -> Self {
2323+ Self {
2424+ pool,
2525+ oauth_client,
2626+ handle_resolver,
2727+ }
2828+ }
1429}
15301631#[async_trait]
···3146 true
3247 }
33484949+ fn requires_manual_verification_part_two(&self) -> bool {
5050+ true
5151+ }
5252+3453 async fn check_part_one(
3554 &self,
3655 did: String,
3737- _verification_code: Option<String>,
5656+ verification_code: Option<String>,
3857 ) -> Result<ChallengeCheckResponse, AdventError> {
3958 let client = self
4059 .oauth_client
···4362 "No oauth client. This should not happen".to_string(),
4463 ))?;
45644646- let record_res = client
4747- .api
4848- .com
4949- .atproto
5050- .repo
5151- .get_record(
5252- atrium_api::com::atproto::repo::get_record::ParametersData {
5353- cid: None,
5454- collection: advent::challenge::Day::NSID.parse().unwrap(),
5555- repo: did.parse().unwrap(),
5656- rkey: "1".parse().unwrap(),
5757- }
5858- .into(),
5959- )
6060- .await;
6161-6262- let record = match record_res {
6363- Ok(r) => r,
6464- Err(e) => {
6565- log::error!("Error getting record: {}", e);
6666- 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()));
6565+ let users_did = match client.did().await {
6666+ None => {
6767+ return Ok(ChallengeCheckResponse::Incorrect(
6868+ "You are not logged in!".to_string(),
6969+ ));
6770 }
7171+ Some(did) => did,
6872 };
7373+ let submitted_did = verification_code.unwrap_or_default();
69747070- let Some(challenge) = self.get_days_challenge(&did).await? else {
7171- log::error!("Could not find a challenge record for day: 1 for the user: {did:?}");
7272- return Err(AdventError::ShouldNotHappen(
7373- "Could not find challenge record".to_string(),
7575+ if users_did.to_string() != submitted_did {
7676+ return Ok(ChallengeCheckResponse::Incorrect(
7777+ format!(
7878+ "{} is not the correct did that for the account you are logged in as! Try again.",
7979+ submitted_did
8080+ )
8181+ .to_string(),
7482 ));
7575- };
7676-7777- let record_data = match safe_check_unknown_record_parse::<advent::challenge::day::RecordData>(
7878- record.value.clone(),
7979- ) {
8080- Ok(rd) => rd,
8181- Err(e) => {
8282- log::error!("Error parsing record: {e}");
8383- return Ok(ChallengeCheckResponse::Incorrect(format!(
8484- "There is a record at the correct location, but it does not seem like it is correct. Try again:\n{e}"
8585- )));
8686- }
8787- };
8888-8989- let verification_code =
9090- challenge
9191- .verification_code_one
9292- .ok_or(AdventError::ShouldNotHappen(
9393- "no verification code for day 1 challenge :/".to_string(),
9494- ))?;
9595-9696- Ok(if record_data.part_one == verification_code {
9797- ChallengeCheckResponse::Correct
9898- } else {
9999- ChallengeCheckResponse::Incorrect(format!(
100100- "The code {} is incorrect",
101101- record_data.part_one
102102- ))
103103- })
8383+ }
8484+ Ok(ChallengeCheckResponse::Correct)
10485 }
1058610687 ///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
10788 async fn check_part_two(
10889 &self,
10990 did: String,
110110- _verification_code: Option<String>,
9191+ verification_code: Option<String>,
11192 ) -> Result<ChallengeCheckResponse, AdventError> {
112112- match &self.oauth_client {
113113- None => Err(AdventError::ShouldNotHappen(
9393+ let client = self
9494+ .oauth_client
9595+ .as_ref()
9696+ .ok_or(AdventError::ShouldNotHappen(
11497 "No oauth client. This should not happen".to_string(),
115115- )),
116116- Some(client) => {
117117- match client
118118- .api
119119- .com
120120- .atproto
121121- .repo
122122- .get_record(
123123- atrium_api::com::atproto::repo::get_record::ParametersData {
124124- cid: None,
125125- collection: advent::challenge::Day::NSID.parse().unwrap(),
126126- repo: did.parse().unwrap(),
127127- rkey: "1".parse().unwrap(),
128128- }
129129- .into(),
130130- )
131131- .await
132132- {
133133- Ok(record) => {
134134- //TODO trouble, and make it double
135135- let challenge = self.get_days_challenge(&did).await?;
9898+ ))?;
9999+100100+ let users_did = match client.did().await {
101101+ None => {
102102+ return Ok(ChallengeCheckResponse::Incorrect(
103103+ "You are not logged in!".to_string(),
104104+ ));
105105+ }
106106+ Some(did) => did,
107107+ };
136108137137- match challenge {
138138- None => {
139139- log::error!(
140140- "Could not find a challenge record for day: {} for the user: {}",
141141- self.day(),
142142- did.clone()
143143- );
144144- Err(AdventError::ShouldNotHappen(
145145- "Could not find a challenge record".to_string(),
146146- ))
147147- }
148148- Some(challenge) => {
149149- let parse_record_result =
150150- safe_check_unknown_record_parse::<
151151- advent::challenge::day::RecordData,
152152- >(record.value.clone());
109109+ let did_doc = self
110110+ .handle_resolver
111111+ .resolve(&users_did)
112112+ .await
113113+ .map_err(|err| {
114114+ log::error!("Failed to resolve DID document: {}", err);
115115+ AdventError::ShouldNotHappen(format!("Failed to resolve DID document: {}", err))
116116+ })?;
153117154154- match parse_record_result {
155155- Ok(record_data) => match record_data.part_two {
156156- None => {
157157- Ok(ChallengeCheckResponse::Incorrect("The record is there, it's the right kind. But aren't you forgetting something?".to_string()))
158158- }
159159- Some(part_two_code) => {
160160- match part_two_code
161161- == challenge
162162- .verification_code_two
163163- .unwrap_or("".to_string())
164164- {
165165- true => Ok(ChallengeCheckResponse::Correct),
166166- false => {
167167- Ok(ChallengeCheckResponse::Incorrect(format!(
168168- "The code {} is incorrect",
169169- record_data.part_one
170170- )))
171171- }
172172- }
173173- }
174174- },
175175- Err(err) => {
176176- log::error!("Error parsing record: {}", err);
177177- Ok(ChallengeCheckResponse::Incorrect(format!(
178178- "There is a record at the correct location, but it does not seem like it is correct. Try again:\n{err}"
179179- )))
180180- }
181181- }
182182- }
183183- }
184184- }
185185- Err(err) => {
186186- log::error!("Error getting record: {}", err);
187187- 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()))
188188- }
118118+ let multi_base_key = match did_doc.get_signing_key() {
119119+ Some(verification_method) => match verification_method.public_key_multibase.clone() {
120120+ Some(key) => key,
121121+ None => {
122122+ return Ok(ChallengeCheckResponse::Incorrect(
123123+ "We couldn't find a signing key for your DID? Hmmm this should be there...."
124124+ .to_string(),
125125+ ));
189126 }
127127+ },
128128+ None => {
129129+ return Ok(ChallengeCheckResponse::Incorrect(
130130+ "We couldn't find a signing key for your DID? Hmmm this should be there...."
131131+ .to_string(),
132132+ ));
190133 }
134134+ };
135135+136136+ let verification_code = verification_code.unwrap_or_default();
137137+138138+ if multi_base_key.to_string() != verification_code {
139139+ return Ok(ChallengeCheckResponse::Incorrect(
140140+ format!(
141141+ "{} is not the correct key that for the account you are logged in as! Try again.",
142142+ verification_code
143143+ )
144144+ .to_string(),
145145+ ));
191146 }
147147+148148+ Ok(ChallengeCheckResponse::Correct)
192149 }
193150}