aboutsummaryrefslogtreecommitdiff
path: root/lib/patreon/src
diff options
context:
space:
mode:
authorChristine Dodrill <me@christine.website>2020-07-16 15:32:30 -0400
committerGitHub <noreply@github.com>2020-07-16 15:32:30 -0400
commit385d25c9f96c0acd5d932488e3bd0ed36ceb4dd7 (patch)
treeaf789f7250519b23038a7e5ea0ae7f4f4c1ffdfc /lib/patreon/src
parent449e934246c82d90dd0aac2644d67f928befeeb4 (diff)
downloadxesite-385d25c9f96c0acd5d932488e3bd0ed36ceb4dd7.tar.xz
xesite-385d25c9f96c0acd5d932488e3bd0ed36ceb4dd7.zip
Rewrite site backend in Rust (#178)
* add shell.nix changes for Rust #176 * set up base crate layout * add first set of dependencies * start adding basic app modules * start html templates * serve index page * add contact and feeds pages * add resume rendering support * resume cleanups * get signalboost page working * rewrite config to be in dhall * more work * basic generic post loading * more tests * initial blog index support * fix routing? * render blogposts * X-Clacks-Overhead * split blog handlers into blog.rs * gallery index * gallery posts * fix hashtags * remove instantpage (it messes up the metrics) * talk support + prometheus * Create rust.yml * Update rust.yml * Update codeql-analysis.yml * add jsonfeed library * jsonfeed support * rss/atom * go mod tidy * atom: add posted date * rss: add publishing date * nix: build rust program * rip out go code * rip out go templates * prepare for serving in docker * create kubernetes deployment * create automagic deployment * build docker images on non-master * more fixes * fix timestamps * fix RSS/Atom/JSONFeed validation errors * add go vanity import redirecting * templates/header: remove this * atom feed: fixes * fix? * fix?? * fix rust tests * Update rust.yml * automatically show snow during the winter * fix dates * show commit link in footer * sitemap support * fix compiler warning * start basic patreon client * integrate kankyo * fix patreon client * add patrons page * remove this * handle patron errors better * fix build * clean up deploy * sort envvars for deploy * remove deps.nix * shell.nix: remove go * update README * fix envvars for tests * nice * blog: add rewrite in rust post * blog/site-update: more words
Diffstat (limited to 'lib/patreon/src')
-rw-r--r--lib/patreon/src/lib.rs158
1 files changed, 158 insertions, 0 deletions
diff --git a/lib/patreon/src/lib.rs b/lib/patreon/src/lib.rs
new file mode 100644
index 0000000..07e6228
--- /dev/null
+++ b/lib/patreon/src/lib.rs
@@ -0,0 +1,158 @@
+#[macro_use]
+extern crate log;
+
+use serde::{Deserialize, Serialize};
+use thiserror::Error;
+use chrono::prelude::*;
+
+pub type Campaigns = Vec<Object<Campaign>>;
+pub type Pledges = Vec<Object<Pledge>>;
+pub type Users = Vec<Object<User>>;
+
+#[derive(Debug, Serialize, Deserialize, Clone)]
+pub struct Campaign {
+ pub summary: String,
+ pub creation_name: String,
+ pub display_patron_goals: bool,
+ pub pay_per_name: String,
+ pub one_liner: Option<String>,
+ pub main_video_embed: Option<String>,
+ pub main_video_url: Option<String>,
+ pub image_small_url: String,
+ pub image_url: String,
+ pub thanks_video_url: Option<String>,
+ pub thanks_embed: Option<String>,
+ pub thanks_msg: String,
+ pub is_charged_immediately: bool,
+ pub is_monthly: bool,
+ pub is_nsfw: bool,
+ pub is_plural: bool,
+ pub created_at: DateTime<Utc>,
+ pub published_at: DateTime<Utc>,
+ pub pledge_url: String,
+ pub pledge_sum: i32,
+ pub patron_count: u32,
+ pub creation_count: u32,
+ pub outstanding_payment_amount_cents: u64,
+}
+
+#[derive(Debug, Serialize, Deserialize, Clone)]
+pub struct Pledge {
+ pub amount_cents: u32,
+ pub created_at: String,
+ pub declined_since: Option<String>,
+ pub pledge_cap_cents: u32,
+ pub patron_pays_fees: bool,
+ pub total_historical_amount_cents: Option<u32>,
+ pub is_paused: Option<bool>,
+ pub has_shipping_address: Option<bool>,
+ pub outstanding_payment_amount_cents: Option<u32>,
+}
+
+#[derive(Debug, Serialize, Deserialize, Clone)]
+pub struct User {
+ pub first_name: String,
+ pub last_name: String,
+ pub full_name: String,
+ pub vanity: Option<String>,
+ pub about: Option<String>,
+ pub gender: i32,
+ pub image_url: String,
+ pub thumb_url: String,
+ pub created: DateTime<Utc>,
+ pub url: String,
+}
+
+pub type Result<T> = std::result::Result<T, Error>;
+
+#[derive(Error, Debug)]
+pub enum Error {
+ #[error("json error: {0:?}")]
+ Json(#[from] serde_json::Error),
+ #[error("request error: {0:?}")]
+ Request(#[from] reqwest::Error),
+}
+
+#[derive(Debug, Serialize, Deserialize, Clone)]
+pub struct Credentials {
+ pub client_id: String,
+ pub client_secret: String,
+ pub access_token: String,
+ pub refresh_token: String,
+}
+
+pub struct Client {
+ cli: reqwest::Client,
+ base_url: String,
+ creds: Credentials,
+}
+
+#[derive(Debug, Serialize, Deserialize, Clone)]
+pub struct Data<T, U> {
+ pub data: T,
+ pub included: Option<Vec<U>>,
+}
+
+#[derive(Debug, Serialize, Deserialize, Clone)]
+pub struct Object<T> {
+ pub id: String,
+ pub attributes: T,
+ pub r#type: String,
+ pub links: Option<Links>,
+}
+
+#[derive(Debug, Serialize, Deserialize, Clone)]
+pub struct Links {
+ related: String,
+}
+
+impl Client {
+ pub fn new(creds: Credentials) -> Self {
+ Self {
+ cli: reqwest::Client::new(),
+ base_url: "https://api.patreon.com".into(),
+ creds: creds,
+ }
+ }
+
+ pub async fn campaign(&self) -> Result<Data<Vec<Object<Campaign>>, ()>> {
+ let data = self
+ .cli
+ .get(&format!(
+ "{}/oauth2/api/current_user/campaigns",
+ self.base_url
+ ))
+ .query(&[("include", "patron.null"), ("includes", "")])
+ .header(
+ "Authorization",
+ format!("Bearer {}", self.creds.access_token),
+ )
+ .send()
+ .await?
+ .error_for_status()?.text().await?;
+ log::debug!("campaign response: {}", data);
+ Ok(serde_json::from_str(&data)?)
+ }
+
+ pub async fn pledges(&self, camp_id: String) -> Result<Vec<Object<User>>> {
+ let data = self
+ .cli
+ .get(&format!(
+ "{}/oauth2/api/campaigns/{}/pledges",
+ self.base_url, camp_id
+ ))
+ .query(&[("include", "patron.null")])
+ .header(
+ "Authorization",
+ format!("Bearer {}", self.creds.access_token),
+ )
+ .send()
+ .await?
+ .error_for_status()?
+ .text()
+ .await?;
+ log::debug!("pledges for {}: {}", camp_id, data);
+ let data : Data<Vec<Object<Pledge>>, Object<User>> = serde_json::from_str(&data)?;
+ Ok(data.included.unwrap())
+ }
+}