diff options
| author | Xe Iaso <me@christine.website> | 2022-04-02 16:15:10 +0000 |
|---|---|---|
| committer | Xe Iaso <me@christine.website> | 2022-04-02 16:15:10 +0000 |
| commit | 1c8c3396a735bd373be417df2138607f0206b736 (patch) | |
| tree | 040c7807317b5286a01960a638fef9bb4e5ff9d1 /lib | |
| parent | 0c0c5875e680ba5b76d79daddda0eed8d13e0726 (diff) | |
| download | xesite-1c8c3396a735bd373be417df2138607f0206b736.tar.xz xesite-1c8c3396a735bd373be417df2138607f0206b736.zip | |
lib/patreon: refresh token support
This should hopefully make the patrons page work consistently and no
longer require me to manually update the patreon token once per month.
Why didn't I do this age ago??????
Hacked up live on twitch: https://twitch.tv/princessxen
Closes #442
Signed-off-by: Xe <me@christine.website>
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/patreon/Cargo.toml | 1 | ||||
| -rw-r--r-- | lib/patreon/src/lib.rs | 90 |
2 files changed, 86 insertions, 5 deletions
diff --git a/lib/patreon/Cargo.toml b/lib/patreon/Cargo.toml index ea2f115..e5907b1 100644 --- a/lib/patreon/Cargo.toml +++ b/lib/patreon/Cargo.toml @@ -14,6 +14,7 @@ serde = { version = "1", features = ["derive"] } thiserror = "1" tracing = "0.1" tracing-futures = "0.2" +url = "2" [dev-dependencies] tokio = { version = "1", features = ["full"] } diff --git a/lib/patreon/src/lib.rs b/lib/patreon/src/lib.rs index 3c504fe..df40e88 100644 --- a/lib/patreon/src/lib.rs +++ b/lib/patreon/src/lib.rs @@ -1,7 +1,10 @@ +use std::{fs, io, path::Path}; + use chrono::prelude::*; use serde::{Deserialize, Serialize}; use thiserror::Error; use tracing::{debug, error, instrument}; +use url::Url; pub type Campaigns = Vec<Object<Campaign>>; pub type Pledges = Vec<Object<Pledge>>; @@ -61,14 +64,30 @@ pub struct User { pub url: String, } +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct RefreshGrant { + pub access_token: String, + pub refresh_token: String, + pub expires_in: serde_json::Value, + pub scope: serde_json::Value, + pub token_type: String, +} + pub type Result<T> = std::result::Result<T, Error>; #[derive(Error, Debug)] pub enum Error { - #[error("json error: {0:?}")] + #[error("json error: {0}")] Json(#[from] serde_json::Error), - #[error("request error: {0:?}")] + + #[error("request error: {0}")] Request(#[from] reqwest::Error), + + #[error("{0}")] + IO(#[from] io::Error), + + #[error("url parse error: {0}")] + URLParse(#[from] url::ParseError), } #[derive(Debug, Serialize, Deserialize, Clone, Default)] @@ -105,12 +124,20 @@ pub struct Links { } impl Client { - pub fn new(creds: Credentials) -> Self { - Self { + pub fn new(creds: Credentials) -> Result<Self> { + let mut creds = creds.clone(); + + let p = Path::new(".patreon.json"); + if p.exists() { + let config = fs::read_to_string(p)?; + creds = serde_json::from_str(&config)?; + } + + Ok(Self { cli: reqwest::Client::new(), base_url: "https://api.patreon.com".into(), creds: creds, - } + }) } #[instrument(skip(self))] @@ -157,4 +184,57 @@ impl Client { let data: Data<Vec<Object<Pledge>>, Object<User>> = serde_json::from_str(&data)?; Ok(data.included.unwrap()) } + + /* + POST www.patreon.com/api/oauth2/token + ?grant_type=refresh_token + &refresh_token=<the user‘s refresh_token> + &client_id=<your client id> + &client_secret=<your client secret> + + 1. grab new creds + 2. serialize new creds to disk + 3. reload current creds in ram + 4. ??? + 5. profit! + */ + #[instrument(skip(self))] + pub async fn refresh_token(&mut self) -> Result<()> { + let mut u = Url::parse(&self.base_url)?; + u.set_path("/api/oauth2/token"); + u.query_pairs_mut() + .append_pair("grant_type", "refresh_token") + .append_pair("refresh_token", &self.creds.refresh_token) + .append_pair("client_id", &self.creds.client_id) + .append_pair("client_secret", &self.creds.client_secret); + + let rg: RefreshGrant = self + .cli + .post(&u.to_string()) + .header( + "Authorization", + format!("Bearer {}", self.creds.access_token), + ) + .send() + .await? + .error_for_status()? + .json() + .await?; + + let mut creds = self.creds.clone(); + + creds.access_token = rg.access_token; + creds.refresh_token = rg.refresh_token; + + let p = Path::new(".patreon.json"); + if p.exists() { + fs::remove_file(p)?; + } + let mut fout = fs::File::create(p)?; + serde_json::to_writer(&mut fout, &creds)?; + + self.creds = creds; + + Ok(()) + } } |
