braincell die from grng
This commit is contained in:
commit
eb13664f84
6 changed files with 3355 additions and 0 deletions
3102
Cargo.lock
generated
Normal file
3102
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
16
Cargo.toml
Normal file
16
Cargo.toml
Normal 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
BIN
favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 190 KiB |
106
src/discord.rs
Normal file
106
src/discord.rs
Normal 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
75
src/main.rs
Normal 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
56
src/session.rs
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue