braincell die from grng

This commit is contained in:
hex 2024-08-13 14:43:43 +02:00
commit eb13664f84
6 changed files with 3355 additions and 0 deletions

3102
Cargo.lock generated Normal file

File diff suppressed because it is too large Load diff

16
Cargo.toml Normal file
View file

@ -0,0 +1,16 @@
[package]
name = "rdiscord"
version = "0.1.0"
edition = "2021"
[dependencies]
dotenv = "0.15.0"
postgres = "0.19.8"
rand = "0.8.5"
rand_chacha = "0.3.1"
rand_core = "0.6.4"
reqwest = {version = "0.12.5", features = ["json"]}
rocket = {version="0.5.1",features=["json"]}
rocket_contrib = "0.4.11"
serde = "1.0.206"
urlencoding = "2.1.3"

BIN
favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 KiB

106
src/discord.rs Normal file
View file

@ -0,0 +1,106 @@
extern crate reqwest;
extern crate rocket;
pub mod discord {
static API: &str = "https://discord.com/api/v10";
use rocket::serde::{Deserialize, Serialize};
use std::error::Error;
use urlencoding::encode;
#[derive(Deserialize, Serialize)]
pub struct TokenResponse {
pub access_token: String,
pub token_type: String,
pub expires_in: usize,
pub refresh_token: String,
pub scope: String,
}
#[derive(Deserialize, Serialize, Clone, Debug)]
pub struct AvatarDecorationData {
asset: String,
sku_id: String,
}
#[derive(Deserialize, Serialize, Clone, Debug)]
#[serde(crate = "rocket::serde")]
pub struct User {
pub id: String,
pub username: String,
pub discriminator: String,
pub global_name: Option<String>,
pub avatar: Option<String>,
pub bot: Option<bool>,
pub system: Option<bool>,
pub mfa_enabled: Option<bool>,
pub banner: Option<String>,
pub accent_color: Option<usize>,
pub locale: Option<String>,
pub verified: Option<bool>,
pub email: Option<String>,
pub flags: Option<usize>,
pub premium_type: Option<usize>,
pub public_flags: Option<usize>,
pub avatar_decoration_data: Option<AvatarDecorationData>,
}
pub struct Client {
redirect_uri: String,
client_secret: String,
client_id: String,
}
impl Client {
pub fn new(redirect_uri: String, client_id: String, client_secret: String) -> Self {
Client {
redirect_uri,
client_id,
client_secret,
}
}
pub fn generate_authorize_url(&self, scope: &str, response_type: &str) -> String {
let AUTHORIZE_ENDPOINT = "https://discord.com/oauth2/authorize";
format!(
"{}?client_id={}&response_type={}&redirect_uri={}&scope={}",
AUTHORIZE_ENDPOINT,
self.client_id,
response_type,
&encode(&self.redirect_uri),
scope
)
}
pub async fn get_user(
&self,
token_response: &TokenResponse,
) -> Result<User, Box<dyn Error>> {
let http_client = reqwest::Client::new();
let user_request = http_client
.get(format!("{}/{}", API, "users/@me"))
.bearer_auth(&token_response.access_token)
.send()
.await?;
let res: User = user_request.json().await?;
Ok(res)
}
pub async fn exchange_code(&self, code: &String) -> Result<TokenResponse, Box<dyn Error>> {
let http_client = reqwest::Client::new();
let exchange_request = http_client
.post(format!("{}/{}", API, "/oauth2/token"))
.header("content-type", "application/x-www-form-urlencoded")
.basic_auth(&self.client_id, Some(&self.client_secret))
.form(&[
("grant_type", "authorization_code"),
("code", code),
("redirect_uri", &self.redirect_uri),
])
.send()
.await?;
let res: TokenResponse = exchange_request.json().await?;
Ok(res)
}
}
}

75
src/main.rs Normal file
View file

@ -0,0 +1,75 @@
#![allow(non_snake_case)]
extern crate reqwest;
extern crate rocket;
mod discord;
mod session;
use discord::discord::Client;
use dotenv::dotenv;
use rocket::fs::NamedFile;
use rocket::response::Redirect;
use rocket::serde::json::Json;
use rocket::tokio::sync::Mutex;
use rocket::State;
use rocket::{get, launch, routes};
use self::session::session::{Session, SessionManager};
type SharedSessionManager = Mutex<SessionManager>;
#[get("/callback?<code>")]
async fn index(
shared_session_manager: &State<SharedSessionManager>,
code: String,
client: &State<Client>,
) -> Json<Session> {
let token_exchange = client.exchange_code(&code).await.unwrap();
let user = client.get_user(&token_exchange).await.unwrap();
let mut session_manager = shared_session_manager.lock().await;
let session = session_manager
.generate_session(user, &token_exchange.access_token)
.await;
Json(session)
}
#[get("/favicon.ico")]
async fn get_favicon() -> Option<NamedFile> {
NamedFile::open("./favicon.ico").await.ok()
}
#[get("/session?<token>")]
async fn return_sess(
shared_session_manager: &State<SharedSessionManager>,
token: String,
) -> Json<Session> {
Json(
shared_session_manager
.lock()
.await
.get_session(&token)
.await
.unwrap(),
)
}
#[get("/")]
async fn redirect_auth(client: &State<Client>) -> Redirect {
Redirect::to(client.generate_authorize_url("identify", "code"))
}
#[launch]
fn launch() -> _ {
dotenv().ok();
let REDIRECT_URI = std::env::var("REDIRECT_URI").expect("yall forgot REDIRECT_URI");
let CLIENT_ID = std::env::var("CLIENT_ID").expect("yall forgot CLIENT_ID");
let CLIENT_SECRET = std::env::var("CLIENT_SECRET").expect("yall forgot CLIENT_SECRET");
let client: Client = Client::new(REDIRECT_URI, CLIENT_ID, CLIENT_SECRET);
let session_manager: SessionManager = SessionManager::new();
let shared_session_manager: SharedSessionManager = SharedSessionManager::new(session_manager);
rocket::build()
.manage(client)
.manage(shared_session_manager)
.mount("/", routes![index, redirect_auth, return_sess, get_favicon])
}

56
src/session.rs Normal file
View file

@ -0,0 +1,56 @@
pub mod session {
use std::error::Error;
use rand::{distributions::Alphanumeric, Rng};
use serde::{Deserialize, Serialize};
use crate::discord::discord::User;
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Session {
pub access_token: String,
pub user: User,
pub session_token: String,
}
pub struct SessionManager {
pub sessions: Vec<Session>,
}
impl SessionManager {
pub async fn generate_random_string(&mut self) -> String {
rand::thread_rng()
.sample_iter(&Alphanumeric)
.take(32)
.map(char::from)
.collect()
}
pub fn new() -> Self {
Self {
sessions: Vec::new(),
}
}
pub async fn get_session(&mut self, session: &String) -> Result<Session, Box<dyn Error>> {
let found_sessions: Vec<Session> = self
.sessions
.clone()
.into_iter()
.filter(|s| s.session_token.eq(session))
.collect();
Ok(found_sessions.get(0).unwrap().clone())
}
pub async fn generate_session(&mut self, user: User, access_token: &str) -> Session {
self.sessions.retain(|s| s.user.id != user.id);
let token = self.generate_random_string().await;
let session = Session {
access_token: access_token.to_string(),
user,
session_token: token,
};
self.sessions.push(session.clone());
session
}
}
}