aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorXe Iaso <me@christine.website>2022-04-02 16:15:10 +0000
committerXe Iaso <me@christine.website>2022-04-02 16:15:10 +0000
commit1c8c3396a735bd373be417df2138607f0206b736 (patch)
tree040c7807317b5286a01960a638fef9bb4e5ff9d1
parent0c0c5875e680ba5b76d79daddda0eed8d13e0726 (diff)
downloadxesite-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>
-rw-r--r--.gitignore1
-rw-r--r--Cargo.lock1
-rw-r--r--lib/patreon/Cargo.toml1
-rw-r--r--lib/patreon/src/lib.rs90
-rw-r--r--src/app/mod.rs9
-rw-r--r--src/post/mod.rs5
6 files changed, 93 insertions, 14 deletions
diff --git a/.gitignore b/.gitignore
index 44098b9..049d0ed 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,3 +6,4 @@ cw.tar
/result
.#*
/target
+.patreon.json
diff --git a/Cargo.lock b/Cargo.lock
index 69f652d..d9a4b72 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1514,6 +1514,7 @@ dependencies = [
"tokio",
"tracing",
"tracing-futures",
+ "url",
]
[[package]]
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(())
+ }
}
diff --git a/src/app/mod.rs b/src/app/mod.rs
index 2c4abcd..429018c 100644
--- a/src/app/mod.rs
+++ b/src/app/mod.rs
@@ -9,14 +9,9 @@ pub mod poke;
#[derive(Clone, Deserialize)]
pub struct Config {
- #[serde(rename = "clackSet")]
- pub(crate) clack_set: Vec<String>,
pub(crate) signalboost: Vec<Person>,
- pub(crate) port: u16,
#[serde(rename = "resumeFname")]
pub(crate) resume_fname: PathBuf,
- #[serde(rename = "webMentionEndpoint")]
- pub(crate) webmention_url: String,
#[serde(rename = "miToken")]
pub(crate) mi_token: String,
}
@@ -27,7 +22,9 @@ async fn patrons() -> Result<Option<patreon::Users>> {
let creds: Credentials = envy::prefixed("PATREON_")
.from_env()
.unwrap_or(Credentials::default());
- let cli = Client::new(creds);
+ let mut cli = Client::new(creds)?;
+
+ cli.refresh_token().await?;
match cli.campaign().await {
Ok(camp) => {
diff --git a/src/post/mod.rs b/src/post/mod.rs
index 1cd7f92..a152bc9 100644
--- a/src/post/mod.rs
+++ b/src/post/mod.rs
@@ -36,7 +36,7 @@ impl Into<jsonfeed::Item> for Post {
.date_published(self.date.to_rfc3339())
.author(
jsonfeed::Author::new()
- .name("Christine Dodrill")
+ .name("Xe Iaso")
.url("https://christine.website")
.avatar("https://christine.website/static/img/avatar.png"),
);
@@ -83,8 +83,7 @@ impl Post {
async fn read_post(dir: &str, fname: PathBuf, cli: &Option<mi::Client>) -> Result<Post> {
debug!(
- "loading {}/{}",
- dir,
+ "loading {}",
fname.clone().into_os_string().into_string().unwrap()
);