Skip to content

Commit

Permalink
Merge pull request #136 from starknet-id/ayush/focustree-quest
Browse files Browse the repository at this point in the history
feat: add focustree quests
  • Loading branch information
fricoben authored Dec 1, 2023
2 parents 4056c4b + 8311cf0 commit 0bd7a25
Show file tree
Hide file tree
Showing 14 changed files with 214 additions and 29 deletions.
5 changes: 5 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ pub_struct!(Clone, Deserialize; Quests {
braavos: Braavos,
element: Element,
nostra: Pairs,
focustree: Api ,
});

pub_struct!(Clone, Deserialize; Twitter {
Expand Down Expand Up @@ -122,6 +123,10 @@ pub_struct!(Clone, Deserialize; Starkscan {
api_key: String,
});

pub_struct!(Clone, Deserialize; Api {
api_endpoint: String,
});

pub_struct!(Clone, Deserialize; Achievement {
contract: FieldElement,
});
Expand Down
14 changes: 2 additions & 12 deletions src/endpoints/achievements/verify_briq.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use axum::{
use mongodb::bson::doc;
use serde_json::json;
use starknet::core::types::FieldElement;
use crate::utils::fetch_json_from_url;

pub async fn handler(
State(state): State<Arc<AppState>>,
Expand Down Expand Up @@ -46,7 +47,7 @@ pub async fn handler(
Ok(response) => {
if let Some(sets) = response.get("sets") {
match sets {
serde_json::Value::Array(sets_array) => {
serde_json::Value::Array(sets_array ) => {
for set in sets_array.iter() {
if let serde_json::Value::String(set_str) = set {
let url = format!(
Expand Down Expand Up @@ -104,17 +105,6 @@ pub async fn handler(
}
}

pub async fn fetch_json_from_url(url: String) -> Result<serde_json::Value, String> {
let client = reqwest::Client::new();
match client.get(url).send().await {
Ok(response) => match response.json::<serde_json::Value>().await {
Ok(json) => Ok(json),
Err(e) => Err(format!("Failed to get JSON response: {}", e)),
},
Err(e) => Err(format!("Failed to send request: {}", e)),
}
}

pub async fn check_for_ducks(properties: &serde_json::Value) -> bool {
if let Some(serde_json::Value::Array(value_arr)) =
properties.get("collections").and_then(|c| c.get("value"))
Expand Down
12 changes: 1 addition & 11 deletions src/endpoints/quests/element/briq/verify_own_briq.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use axum::{
};
use serde_json::json;
use starknet::core::types::FieldElement;
use crate::utils::fetch_json_from_url;

pub async fn handler(
State(state): State<Arc<AppState>>,
Expand Down Expand Up @@ -74,14 +75,3 @@ pub async fn handler(
Err(e) => get_error(e),
}
}

pub async fn fetch_json_from_url(url: String) -> Result<serde_json::Value, String> {
let client = reqwest::Client::new();
match client.get(url).send().await {
Ok(response) => match response.json::<serde_json::Value>().await {
Ok(json) => Ok(json),
Err(e) => Err(format!("Failed to get JSON response: {}", e)),
},
Err(e) => Err(format!("Failed to send request: {}", e)),
}
}
145 changes: 145 additions & 0 deletions src/endpoints/quests/focustree/engagement/discord_fw_callback.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
use std::sync::Arc;

use crate::utils::CompletedTasksTrait;
use crate::{
models::AppState,
utils::{get_error_redirect, success_redirect},
};
use axum::{
extract::{Query, State},
response::IntoResponse,
};
use mongodb::bson::doc;
use reqwest::header::AUTHORIZATION;
use serde::Deserialize;
use starknet::core::types::FieldElement;

#[derive(Deserialize)]
pub struct TwitterOAuthCallbackQuery {
code: String,
state: FieldElement,
}

#[derive(Deserialize)]
pub struct Guild {
id: String,
#[allow(dead_code)]
name: String,
}

pub async fn handler(
State(state): State<Arc<AppState>>,
Query(query): Query<TwitterOAuthCallbackQuery>,
) -> impl IntoResponse {
let quest_id = 21;
let task_id = 87;
let guild_id = "986385497888792598";
let authorization_code = &query.code;
let error_redirect_uri = format!(
"{}/quest/{}?task_id={}&res=false",
state.conf.variables.app_link, quest_id, task_id
);

// Exchange the authorization code for an access token
let params = [
("client_id", &state.conf.discord.oauth2_clientid),
("client_secret", &state.conf.discord.oauth2_secret),
("code", &authorization_code.to_string()),
(
"redirect_uri",
&format!(
"{}/quests/focustree/discord_fw_callback",
state.conf.variables.api_link
),
),
("grant_type", &"authorization_code".to_string()),
];
let access_token = match exchange_authorization_code(params).await {
Ok(token) => token,
Err(e) => {
return get_error_redirect(
error_redirect_uri,
format!("Failed to exchange authorization code: {}", e),
);
}
};

// Get user guild information
let client = reqwest::Client::new();
let response_result = client
.get("https://discord.com/api/users/@me/guilds")
.header(AUTHORIZATION, format!("Bearer {}", access_token))
.send()
.await;
let response: Vec<Guild> = match response_result {
Ok(response) => {
let json_result = response.json().await;
match json_result {
Ok(json) => json,
Err(e) => {
return get_error_redirect(
error_redirect_uri,
format!(
"Failed to get JSON response while fetching user info: {}",
e
),
);
}
}
}
Err(e) => {
return get_error_redirect(
error_redirect_uri,
format!("Failed to send request to get user info: {}", e),
);
}
};

for guild in response {
if guild.id == guild_id {
match state.upsert_completed_task(query.state, task_id).await {
Ok(_) => {
let redirect_uri = format!(
"{}/quest/{}?task_id={}&res=true",
state.conf.variables.app_link, quest_id, task_id
);
return success_redirect(redirect_uri);
}
Err(e) => return get_error_redirect(error_redirect_uri, format!("{}", e)),
}
}
}

get_error_redirect(
error_redirect_uri,
"You're not part of AVNU's Discord server".to_string(),
)
}

async fn exchange_authorization_code(
params: [(&str, &String); 5],
) -> Result<String, Box<dyn std::error::Error>> {
let client = reqwest::Client::new();
let res = client
.post("https://discord.com/api/oauth2/token")
.form(&params)
.send()
.await?;
let json: serde_json::Value = res.json().await?;
match json["access_token"].as_str() {
Some(s) => Ok(s.to_string()),
None => {
println!(
"Failed to get 'access_token' from JSON response : {:?}",
json
);
Err(Box::new(std::io::Error::new(
std::io::ErrorKind::Other,
format!(
"Failed to get 'access_token' from JSON response : {:?}",
json
),
)))
}
}
}
2 changes: 2 additions & 0 deletions src/endpoints/quests/focustree/engagement/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pub mod discord_fw_callback;
pub mod verify_twitter_rt;
24 changes: 24 additions & 0 deletions src/endpoints/quests/focustree/engagement/verify_twitter_rt.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
use std::sync::Arc;

use crate::{
models::{AppState, VerifyQuery},
utils::{get_error, CompletedTasksTrait},
};
use axum::{
extract::{Query, State},
http::StatusCode,
response::IntoResponse,
Json,
};
use serde_json::json;

pub async fn handler(
State(state): State<Arc<AppState>>,
Query(query): Query<VerifyQuery>,
) -> impl IntoResponse {
let task_id = 88;
match state.upsert_completed_task(query.addr, task_id).await {
Ok(_) => (StatusCode::OK, Json(json!({"res": true}))).into_response(),
Err(e) => get_error(format!("{}", e)),
}
}
File renamed without changes.
3 changes: 3 additions & 0 deletions src/endpoints/quests/focustree/introduction/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pub mod claimable;
pub mod verify_twitter_fw;
pub mod verify_twitter_rt;
5 changes: 2 additions & 3 deletions src/endpoints/quests/focustree/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
pub mod claimable;
pub mod verify_twitter_fw;
pub mod verify_twitter_rt;
pub mod introduction;
pub mod engagement;
14 changes: 11 additions & 3 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -319,15 +319,23 @@ async fn main() {
)
.route(
"/quests/focustree/verify_twitter_fw",
get(endpoints::quests::focustree::verify_twitter_fw::handler),
get(endpoints::quests::focustree::introduction::verify_twitter_fw::handler),
)
.route(
"/quests/focustree/verify_twitter_rt",
get(endpoints::quests::focustree::verify_twitter_rt::handler),
get(endpoints::quests::focustree::introduction::verify_twitter_rt::handler),
)
.route(
"/quests/focustree/claimable",
get(endpoints::quests::focustree::claimable::handler),
get(endpoints::quests::focustree::introduction::claimable::handler),
)
.route(
"/quests/focustree/discord_fw_callback",
get(endpoints::quests::focustree::engagement::discord_fw_callback::handler),
)
.route(
"/quests/focustree/verify_twitter_rw_user",
get(endpoints::quests::focustree::engagement::verify_twitter_rt::handler),
)
.route(
"/quests/element/element/verify_is_eligible",
Expand Down
6 changes: 6 additions & 0 deletions src/models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ pub_struct!(Debug, Serialize, Deserialize; QuestDocument {
logo: String,
rewards_img: String,
rewards_title: String,
rewards_description: Option<String>,
rewards_nfts: Vec<NFTItem>,
img_card: String,
title_card: String,
Expand Down Expand Up @@ -67,6 +68,11 @@ pub_struct!(Deserialize; VerifyQuery {
addr: FieldElement,
});

pub_struct!(Deserialize; EmailQuery {
addr: FieldElement,
email: String,
});

pub_struct!(Deserialize; VerifyQuizQuery {
addr: FieldElement,
quiz_name: String,
Expand Down
13 changes: 13 additions & 0 deletions src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -368,12 +368,24 @@ impl DeployedTimesTrait for AppState {
}
}

pub async fn fetch_json_from_url(url: String) -> Result<serde_json::Value, String> {
let client = reqwest::Client::new();
match client.get(url).send().await {
Ok(response) => match response.json::<serde_json::Value>().await {
Ok(json) => Ok(json),
Err(e) => Err(format!("Failed to get JSON response: {}", e)),
},
Err(e) => Err(format!("Failed to send request: {}", e)),
}
}

pub async fn update_leaderboard(
view_collection: Collection<LeaderboardTable>,
address: String,
experience: i64,
timestamp: f64,
) {

// get current experience and new experience to it
let mut old_experience = 0;
let filter = doc! { "_id": &*address };
Expand Down Expand Up @@ -605,3 +617,4 @@ pub fn run_boosts_raffle(db: &Database, interval: u64) {
interval,
));
}

0 comments on commit 0bd7a25

Please sign in to comment.