Skip to content

Commit

Permalink
Endpoint for listing all patrons (#3)
Browse files Browse the repository at this point in the history
Tüm patreon destekçilerini gösteren endpoint ekler
Oyuna eklenecek olan credits kısmında kullanılacak

---------

Co-authored-by: Sefa <[email protected]>
  • Loading branch information
RengaN02 and Seefaaa authored Feb 1, 2025
1 parent 9e4a49e commit 0100ce3
Show file tree
Hide file tree
Showing 6 changed files with 94 additions and 11 deletions.
2 changes: 1 addition & 1 deletion config.example.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ secret = ""
dev_secret = ""
dev_routes = ["/v2/player"]
exposed_secret = ""
exposed_routes = ["/v2/patreon", "/v2/discord/user", "/v2/discord/member"]
exposed_routes = ["/v2/patreon", "/v2/patreon/patrons", "/v2/discord/user", "/v2/discord/member"]
cli_colors = true
log_level = "normal"

Expand Down
4 changes: 3 additions & 1 deletion src/byond/status.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,9 @@ pub async fn status(address: &str) -> super::Result<ServerStatus> {
"shuttle_timer" => status.shuttle_timer = value.parse()?,
_ => {
#[cfg(debug_assertions)]
tracing::warn!("Status topic responsed with unknown param: {key} = {value} ({address})");
tracing::warn!(
"Status topic responsed with unknown param: {key} = {value} ({address})"
);
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/database/verify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ pub async fn unverify_discord(
unreachable!()
}

async fn ckey_by_discord_id(
pub async fn ckey_by_discord_id(
discord_id: &str,
connection: &mut PoolConnection<MySql>,
) -> Result<String, Error> {
Expand Down
53 changes: 46 additions & 7 deletions src/http/discord.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,8 @@ pub struct User {
pub async fn get_user(id: i64, token: &str) -> Result<User, Error> {
let _lock = DISCORD_API_LOCK.lock().await;

let url = format!("https://discord.com/api/v10/users/{id}");

let response = REQWEST_CLIENT
.get(url)
.get(format!("https://discord.com/api/v10/users/{id}"))
.header("Authorization", format!("Bot {token}"))
.send()
.await?
Expand All @@ -45,7 +43,9 @@ pub async fn get_user(id: i64, token: &str) -> Result<User, Error> {

#[derive(Debug, Serialize, Deserialize)]
pub struct GuildMember {
pub roles: HashSet<String>, // other fields are not required for now (https://discord.com/developers/docs/resources/guild#guild-member-object)
// https://discord.com/developers/docs/resources/guild#guild-member-object
pub roles: HashSet<String>,
pub user: User,
}

pub async fn get_guild_member(
Expand All @@ -55,10 +55,10 @@ pub async fn get_guild_member(
) -> Result<GuildMember, Error> {
let _lock = DISCORD_API_LOCK.lock().await;

let url = format!("https://discord.com/api/v10/guilds/{guild_id}/members/{user_id}");

let response = REQWEST_CLIENT
.get(url)
.get(format!(
"https://discord.com/api/v10/guilds/{guild_id}/members/{user_id}"
))
.header("Authorization", format!("Bot {token}"))
.send()
.await?
Expand All @@ -72,3 +72,42 @@ pub async fn get_guild_member(

Ok(member)
}

pub async fn search_members(
guild_id: i64,
query: String,
token: &str,
) -> Result<Vec<GuildMember>, Error> {
let _lock = DISCORD_API_LOCK.lock().await;

let response = REQWEST_CLIENT
.post(format!(
"https://discord.com/api/v10/guilds/{guild_id}/members-search"
))
.header("Authorization", format!("Bot {token}"))
.header("Content-Type", "application/json")
.body(query)
.send()
.await?
.text()
.await?;

#[derive(Deserialize)]
struct Response {
pub members: Vec<ResponseMember>,
}

#[derive(Deserialize)]
struct ResponseMember {
pub member: GuildMember,
}

let Ok(response) = serde_json::from_str::<Response>(&response) else {
let error: ErrorMessage = serde_json::from_str(&response)?;
return Err(Error::Discord(error.code));
};

let members = response.members.into_iter().map(|m| m.member).collect();

Ok(members)
}
1 change: 1 addition & 0 deletions src/routes/v2/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ pub fn mount(rocket: Rocket<Build>) -> Rocket<Build> {
"/v2",
routes![
patreon::index,
patreon::patrons,
player::index,
player::ban,
player::characters,
Expand Down
43 changes: 42 additions & 1 deletion src/routes/v2/patreon.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ use sqlx::MySqlPool;
use crate::{
config::{self, Config},
database::{error::Error, *},
http::{self, discord::get_guild_member},
http::{
self,
discord::{get_guild_member, search_members},
},
Database,
};

Expand Down Expand Up @@ -45,3 +48,41 @@ async fn is_patron(ckey: &str, pool: &MySqlPool, discord: &config::Discord) -> R

Ok(member.roles.contains(&discord.patreon_role.to_string()))
}

#[get("/patreon/patrons")]
pub async fn patrons(
database: &State<Database>,
config: &State<Config>,
_api_key: ApiKey,
) -> Result<Json<Value>, Status> {
let Ok(patrons) = get_patrons(&database.pool, &config.discord).await else {
return Err(Status::InternalServerError);
};

Ok(Json::Ok(json!({ "patrons": patrons })))
}

async fn get_patrons(pool: &MySqlPool, discord: &config::Discord) -> Result<Vec<String>, Error> {
let mut connection = pool.acquire().await?;

let query = format!(
"{{\"or_query\":{{}},\"and_query\":{{\"role_ids\":{{\"and_query\":[\"{}\"]}}}},\"limit\":1000}}",
discord.patreon_role
);

let members = search_members(discord.guild, query, &discord.token).await?;

let mut ckeys = Vec::new();

for member in members {
match ckey_by_discord_id(&member.user.id, &mut connection).await {
Ok(ckey) => ckeys.push(ckey),
Err(Error::NotLinked) => continue,
Err(e) => return Err(e),
}
}

connection.close().await?;

Ok(ckeys)
}

0 comments on commit 0100ce3

Please sign in to comment.