inital commit
This commit is contained in:
43
src-tauri/src/commands/config/mod.rs
Normal file
43
src-tauri/src/commands/config/mod.rs
Normal file
@@ -0,0 +1,43 @@
|
||||
use crate::handlers::config::{get_config_path, Config};
|
||||
|
||||
#[tauri::command]
|
||||
pub fn init_config() -> Result<(), String> {
|
||||
println!("[commands::config::init_config] Initializing config...");
|
||||
let config_path = get_config_path().map_err(|e| e.to_string())?;
|
||||
|
||||
println!(
|
||||
"[commands::config::init_config] Config path: {}",
|
||||
config_path.display()
|
||||
);
|
||||
|
||||
Config::load_or_create(&config_path).map_err(|e| e.to_string())?;
|
||||
println!("[commands::config::init_config] Config initialized successfully");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn get_config() -> Result<Config, String> {
|
||||
let config_path = get_config_path().map_err(|e| e.to_string())?;
|
||||
let config = Config::load_or_create(&config_path).map_err(|e| e.to_string())?;
|
||||
|
||||
println!(
|
||||
"[commands::config::get_config] Config loaded successfully: {:?} from path: {}",
|
||||
config,
|
||||
config_path.display()
|
||||
);
|
||||
|
||||
Ok(config)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn save_config(config: Config) -> Result<(), String> {
|
||||
let config_path = get_config_path().map_err(|e| e.to_string())?;
|
||||
println!(
|
||||
"[commands::config::save_config] Saving config: {:?} to path: {}",
|
||||
config,
|
||||
config_path.display()
|
||||
);
|
||||
|
||||
config.save(&config_path).map_err(|e| e.to_string())?;
|
||||
Ok(())
|
||||
}
|
||||
37
src-tauri/src/commands/fansly/mod.rs
Normal file
37
src-tauri/src/commands/fansly/mod.rs
Normal file
@@ -0,0 +1,37 @@
|
||||
use crate::{
|
||||
handlers::fansly::Fansly,
|
||||
structs::{FanslyAccountResponse, FanslyBaseResponse, SyncDataResponse},
|
||||
};
|
||||
use lazy_static::lazy_static;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
lazy_static! {
|
||||
static ref FANSLY: Mutex<Fansly> = Mutex::new(Fansly::new(None));
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn fansly_set_token(token: Option<String>) {
|
||||
FANSLY.lock().await.set_token(token);
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn fansly_get_me() -> Result<FanslyBaseResponse<FanslyAccountResponse>, String> {
|
||||
let fansly = FANSLY.lock().await;
|
||||
let response = fansly.get_profile().await;
|
||||
|
||||
match response {
|
||||
Ok(response) => Ok(response),
|
||||
Err(e) => Err(e.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn fansly_sync() -> Result<SyncDataResponse, String> {
|
||||
let fansly = FANSLY.lock().await;
|
||||
let response = fansly.sync().await;
|
||||
|
||||
match response {
|
||||
Ok(response) => Ok(response),
|
||||
Err(e) => Err(e.to_string()),
|
||||
}
|
||||
}
|
||||
3
src-tauri/src/commands/mod.rs
Normal file
3
src-tauri/src/commands/mod.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
pub mod config;
|
||||
pub mod fansly;
|
||||
pub mod utils;
|
||||
4
src-tauri/src/commands/utils/mod.rs
Normal file
4
src-tauri/src/commands/utils/mod.rs
Normal file
@@ -0,0 +1,4 @@
|
||||
#[tauri::command]
|
||||
pub fn quit(code: i32) {
|
||||
std::process::exit(code);
|
||||
}
|
||||
105
src-tauri/src/handlers/config/mod.rs
Normal file
105
src-tauri/src/handlers/config/mod.rs
Normal file
@@ -0,0 +1,105 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fs::{self, File};
|
||||
use std::io::{self, Write};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use crate::structs::{FanslyFollowersResponse, Subscription};
|
||||
|
||||
const CURRENT_VERSION: i32 = 1; // Set the current version of the config
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct SyncData {
|
||||
pub followers: Vec<FanslyFollowersResponse>,
|
||||
pub subscribers: Vec<Subscription>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct Config {
|
||||
pub version: i32, // Add a version field to the config (1, 2, 3, etc.)
|
||||
pub is_first_run: bool,
|
||||
pub fansly_token: String,
|
||||
pub sync_interval: u64,
|
||||
pub last_sync: u64,
|
||||
pub last_sync_data: SyncData,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
Config {
|
||||
version: CURRENT_VERSION, // Version is set to CURRENT_VERSION by default
|
||||
is_first_run: true, // First run is set to true by default
|
||||
fansly_token: String::new(), // Fansly token is stored as a string
|
||||
sync_interval: 1, // Every hour - sync interval is interpreted as hours
|
||||
last_sync: 0, // Last sync time is stored as a UNIX timestamp
|
||||
last_sync_data: SyncData {
|
||||
followers: Vec::new(),
|
||||
subscribers: Vec::new(),
|
||||
}, // Last sync data is stored as a list of followers and subscribers
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn load_or_create(path: &Path) -> io::Result<Self> {
|
||||
if path.exists() {
|
||||
let mut config: Self = serde_json::from_str(std::fs::read_to_string(path)?.as_str())
|
||||
.map_err(|e| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
format!("Could not parse config file: {}", e),
|
||||
)
|
||||
})?;
|
||||
if config.version != CURRENT_VERSION {
|
||||
config = config.migrate()?;
|
||||
config.save(path)?;
|
||||
}
|
||||
Ok(config)
|
||||
} else {
|
||||
let saved_config = Config::default().save(path);
|
||||
saved_config
|
||||
.and_then(|_| Config::load_or_create(path))
|
||||
.or_else(|e| Err(e))
|
||||
}
|
||||
}
|
||||
|
||||
fn migrate(mut self) -> io::Result<Self> {
|
||||
while self.version < CURRENT_VERSION {
|
||||
self = match self.version {
|
||||
1 => {
|
||||
// If we're on version 1, migrate to version 2 (not implemented)
|
||||
self.version += 1;
|
||||
self
|
||||
}
|
||||
_ => {
|
||||
// If we don't have a migration path, return an error
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
format!("No migration path for version {}", self.version),
|
||||
));
|
||||
}
|
||||
};
|
||||
}
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
pub fn save(&self, path: &Path) -> io::Result<()> {
|
||||
let mut file = File::create(path)?;
|
||||
file.write_all(serde_json::to_string_pretty(self).unwrap().as_bytes())?;
|
||||
|
||||
// Return the saved config
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_config_path() -> io::Result<PathBuf> {
|
||||
let mut config_dir = dirs::config_dir().ok_or_else(|| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::NotFound,
|
||||
"Could not determine user's config directory",
|
||||
)
|
||||
})?;
|
||||
config_dir.push("FanslySync");
|
||||
fs::create_dir_all(&config_dir)?;
|
||||
config_dir.push("config.json");
|
||||
Ok(config_dir)
|
||||
}
|
||||
254
src-tauri/src/handlers/fansly/mod.rs
Normal file
254
src-tauri/src/handlers/fansly/mod.rs
Normal file
@@ -0,0 +1,254 @@
|
||||
// Create a simple module for handling the Fansly API, using reqwest to make requests to the API.
|
||||
// This module will contain a struct Fansly, which will have a method to get the user's profile information.
|
||||
use crate::structs::{
|
||||
FanslyAccountResponse, FanslyBaseResponse, FanslyBaseResponseList, FanslyFollowersResponse,
|
||||
FanslySubscriptionsResponse, Subscription, SyncDataResponse,
|
||||
};
|
||||
use reqwest::header::{HeaderMap, HeaderValue, USER_AGENT};
|
||||
|
||||
pub struct Fansly {
|
||||
client: reqwest::Client,
|
||||
token: Option<String>,
|
||||
}
|
||||
|
||||
impl Fansly {
|
||||
pub fn new(token: Option<String>) -> Self {
|
||||
let mut headers = HeaderMap::new();
|
||||
|
||||
// Set the user agent to the FanslySync/0.1.0 tanner@fanslycreatorbot.com
|
||||
headers.insert(
|
||||
USER_AGENT,
|
||||
HeaderValue::from_static("FanslySync/0.1.0 tanner@fanslycreatorbot.com"),
|
||||
);
|
||||
|
||||
// If we have a token, add it to the headers\
|
||||
if let Some(token) = &token {
|
||||
headers.insert(
|
||||
"Authorization",
|
||||
HeaderValue::from_str(&format!("{}", token)).unwrap(),
|
||||
);
|
||||
}
|
||||
|
||||
// Set our default base url to https://apiv3.fansly.com/api/v1/
|
||||
let client = reqwest::Client::builder()
|
||||
.default_headers(headers)
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
Self { client, token }
|
||||
}
|
||||
|
||||
// Helper function to set our token on the fly
|
||||
pub fn set_token(&mut self, token: Option<String>) {
|
||||
self.token = token;
|
||||
|
||||
// Re-create the client with the new token (if it exists)
|
||||
let mut headers = HeaderMap::new();
|
||||
|
||||
headers.insert(
|
||||
USER_AGENT,
|
||||
HeaderValue::from_static("FanslySync/0.1.0 tanner@fanslycreatorbot.com"),
|
||||
);
|
||||
|
||||
// If we have a token, add it to the headers
|
||||
if let Some(token) = &self.token {
|
||||
headers.insert(
|
||||
"Authorization",
|
||||
HeaderValue::from_str(&format!("{}", token)).unwrap(),
|
||||
);
|
||||
}
|
||||
|
||||
self.client = reqwest::Client::builder()
|
||||
.default_headers(headers)
|
||||
.build()
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pub async fn get_profile(
|
||||
&self,
|
||||
) -> Result<FanslyBaseResponse<FanslyAccountResponse>, reqwest::Error> {
|
||||
let response = self
|
||||
.client
|
||||
.get("https://apiv3.fansly.com/api/v1/account/me")
|
||||
.send()
|
||||
.await?;
|
||||
|
||||
if !response.status().is_success() {
|
||||
eprintln!("[sync::process::get_profile] No successful response from API. Setting error state.");
|
||||
return Err(response.error_for_status().unwrap_err());
|
||||
} else {
|
||||
println!("[sync::process::get_profile] Got successful response from API.");
|
||||
}
|
||||
|
||||
let profile: FanslyBaseResponse<FanslyAccountResponse> = response.json().await?;
|
||||
Ok(profile)
|
||||
}
|
||||
|
||||
async fn fetch_followers(
|
||||
&self,
|
||||
account_id: &str,
|
||||
auth_token: &str,
|
||||
offset: u32,
|
||||
) -> Result<FanslyBaseResponseList<FanslyFollowersResponse>, reqwest::Error> {
|
||||
let url = format!("https://apiv3.fansly.com/api/v1/account/{}/followers?ngsw-bypass=true&limit=100&offset={}", account_id, offset);
|
||||
|
||||
let mut headers = reqwest::header::HeaderMap::new();
|
||||
headers.insert(
|
||||
reqwest::header::AUTHORIZATION,
|
||||
format!("{}", auth_token).parse().unwrap(),
|
||||
);
|
||||
headers.insert(
|
||||
reqwest::header::USER_AGENT,
|
||||
"FanslySync/1.0.0 (tanner@teamhydra.dev)".parse().unwrap(),
|
||||
);
|
||||
|
||||
headers.insert(
|
||||
reqwest::header::CONTENT_TYPE,
|
||||
"application/json".parse().unwrap(),
|
||||
);
|
||||
|
||||
let response = self.client.get(url).headers(headers).send().await?;
|
||||
|
||||
if !response.status().is_success() {
|
||||
eprintln!("[sync::process::fetch_followers] No successful response from API. Setting error state.");
|
||||
return Err(response.error_for_status().unwrap_err());
|
||||
}
|
||||
|
||||
let followers: FanslyBaseResponseList<FanslyFollowersResponse> = response.json().await?;
|
||||
println!(
|
||||
"[sync::process::fetch_followers] Got {} followers from API.",
|
||||
followers.response.len()
|
||||
);
|
||||
|
||||
Ok(followers)
|
||||
}
|
||||
|
||||
async fn fetch_subscribers(
|
||||
&self,
|
||||
auth_token: &str,
|
||||
offset: u32,
|
||||
) -> Result<Vec<Subscription>, reqwest::Error> {
|
||||
let url = format!("https://apiv3.fansly.com/api/v1/subscribers?status=3,4&limit=100&offset={}&ngsw-bypass=true", offset);
|
||||
|
||||
let mut headers = reqwest::header::HeaderMap::new();
|
||||
headers.insert(
|
||||
reqwest::header::AUTHORIZATION,
|
||||
format!("{}", auth_token).parse().unwrap(),
|
||||
);
|
||||
headers.insert(
|
||||
reqwest::header::USER_AGENT,
|
||||
"FanslySync/1.0.0 (sticks@teamhydra.dev)".parse().unwrap(),
|
||||
);
|
||||
headers.insert(
|
||||
reqwest::header::CONTENT_TYPE,
|
||||
"application/json".parse().unwrap(),
|
||||
);
|
||||
|
||||
let response = self.client.get(url).headers(headers).send().await?;
|
||||
|
||||
if !response.status().is_success() {
|
||||
eprintln!("[fanslySyncExt] No successful response from API. Setting error state.");
|
||||
let error = response.error_for_status().unwrap_err();
|
||||
return Err(error);
|
||||
}
|
||||
|
||||
let subscriptions: FanslyBaseResponse<FanslySubscriptionsResponse> =
|
||||
response.json().await?;
|
||||
println!(
|
||||
"[fanslySyncExt] Got {} subscriptions from API.",
|
||||
subscriptions.response.subscriptions.len()
|
||||
);
|
||||
|
||||
Ok(subscriptions.response.subscriptions)
|
||||
}
|
||||
|
||||
pub async fn sync(&self) -> Result<SyncDataResponse, String> {
|
||||
// Fetch profile
|
||||
println!("[sync::process] Fetching profile...");
|
||||
let profile = self.get_profile().await.map_err(|e| e.to_string())?;
|
||||
|
||||
if !profile.success {
|
||||
return Err("Failed to fetch profile".to_string());
|
||||
}
|
||||
|
||||
println!("[sync::process] Profile retrieved successfully.");
|
||||
|
||||
let account = profile.response.account;
|
||||
let total_followers = account.follow_count;
|
||||
let total_subscribers = account.subscriber_count;
|
||||
|
||||
println!(
|
||||
"[sync::process] Account {} has {} followers and {} subscribers. Starting sync...",
|
||||
account.id, total_followers, total_subscribers
|
||||
);
|
||||
|
||||
let mut followers: Vec<FanslyFollowersResponse> = Vec::new();
|
||||
let mut subscribers: Vec<Subscription> = Vec::new();
|
||||
|
||||
println!("[sync::process] Fetching followers and subscribers...");
|
||||
|
||||
// Fetch followers until we have all of them
|
||||
let mut offset = 0;
|
||||
let mut total_requests = 0;
|
||||
while followers.len() < total_followers as usize {
|
||||
println!(
|
||||
"[sync::process] Fetching followers for account {} with offset {} (total: {})",
|
||||
account.id, offset, total_followers
|
||||
);
|
||||
let response = self
|
||||
.fetch_followers(&account.id, &self.token.as_ref().unwrap(), offset)
|
||||
.await
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
println!(
|
||||
"[sync::process] Got {} followers from API.",
|
||||
response.response.len()
|
||||
);
|
||||
followers.extend(response.response);
|
||||
offset += 100;
|
||||
total_requests += 1;
|
||||
|
||||
// Every 10 requests, sleep for a bit to avoid rate limiting
|
||||
if total_requests % 10 == 0 {
|
||||
tokio::time::sleep(tokio::time::Duration::from_secs(5)).await;
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch subscribers until we have all of them
|
||||
offset = 0;
|
||||
while subscribers.len() < total_subscribers as usize {
|
||||
println!(
|
||||
"[sync::process] Fetching subscribers with offset {} for account {} (total: {})",
|
||||
offset, account.id, total_subscribers
|
||||
);
|
||||
|
||||
let response = self
|
||||
.fetch_subscribers(&self.token.as_ref().unwrap(), offset)
|
||||
.await
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
subscribers.extend(response);
|
||||
offset += 100;
|
||||
total_requests += 1;
|
||||
|
||||
// Every 10 requests, sleep for a bit to avoid rate limiting
|
||||
if total_requests % 10 == 0 {
|
||||
tokio::time::sleep(tokio::time::Duration::from_secs(5)).await;
|
||||
}
|
||||
}
|
||||
|
||||
println!(
|
||||
"[sync::process] Got {} followers and {} subscribers from API.",
|
||||
followers.len(),
|
||||
subscribers.len()
|
||||
);
|
||||
|
||||
println!("[sync::process] Sync complete.");
|
||||
|
||||
// Return JSON of what we fetched
|
||||
Ok(SyncDataResponse {
|
||||
followers,
|
||||
subscribers,
|
||||
})
|
||||
}
|
||||
}
|
||||
2
src-tauri/src/handlers/mod.rs
Normal file
2
src-tauri/src/handlers/mod.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
pub mod config;
|
||||
pub mod fansly;
|
||||
26
src-tauri/src/main.rs
Normal file
26
src-tauri/src/main.rs
Normal file
@@ -0,0 +1,26 @@
|
||||
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
|
||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||
|
||||
mod commands;
|
||||
mod handlers;
|
||||
mod structs;
|
||||
|
||||
use commands::config::{get_config, init_config, save_config};
|
||||
use commands::fansly::{fansly_get_me, fansly_set_token, fansly_sync};
|
||||
use commands::utils::quit;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
tauri::Builder::default()
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
init_config,
|
||||
get_config,
|
||||
save_config,
|
||||
quit,
|
||||
fansly_set_token,
|
||||
fansly_get_me,
|
||||
fansly_sync
|
||||
])
|
||||
.run(tauri::generate_context!())
|
||||
.expect("error while running tauri application");
|
||||
}
|
||||
196
src-tauri/src/structs/mod.rs
Normal file
196
src-tauri/src/structs/mod.rs
Normal file
@@ -0,0 +1,196 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
|
||||
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct SyncDataResponse {
|
||||
pub followers: Vec<FanslyFollowersResponse>,
|
||||
pub subscribers: Vec<Subscription>,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct FanslyBaseResponse<T> {
|
||||
pub success: bool,
|
||||
pub response: T,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct FanslyBaseResponseList<T> {
|
||||
pub success: bool,
|
||||
pub response: Vec<T>,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct FanslyFollowersResponse {
|
||||
pub follower_id: String,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct FanslySubscriptionsResponse {
|
||||
pub stats: SubscriptionsStats,
|
||||
pub subscriptions: Vec<Subscription>,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct SubscriptionsStats {
|
||||
pub total_active: i64,
|
||||
pub total_expired: i64,
|
||||
pub total: i64,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Subscription {
|
||||
pub id: String,
|
||||
pub history_id: String,
|
||||
pub subscriber_id: String,
|
||||
pub subscription_tier_id: String,
|
||||
pub subscription_tier_name: String,
|
||||
pub subscription_tier_color: String,
|
||||
pub plan_id: String,
|
||||
pub promo_id: Option<String>,
|
||||
pub gift_code_id: Value,
|
||||
pub payment_method_id: String,
|
||||
pub status: i64,
|
||||
pub price: i64,
|
||||
pub renew_price: i64,
|
||||
pub renew_correlation_id: String,
|
||||
pub auto_renew: i64,
|
||||
pub billing_cycle: i64,
|
||||
pub duration: i64,
|
||||
pub renew_date: i64,
|
||||
pub version: i64,
|
||||
pub created_at: i64,
|
||||
pub updated_at: i64,
|
||||
pub ends_at: i64,
|
||||
pub promo_price: Value,
|
||||
pub promo_duration: Value,
|
||||
pub promo_status: Value,
|
||||
pub promo_starts_at: Value,
|
||||
pub promo_ends_at: Value,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct FanslyAccountResponse {
|
||||
pub account: Account,
|
||||
pub correlation_id: String,
|
||||
pub check_token: Value,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Account {
|
||||
pub id: String,
|
||||
pub email: String,
|
||||
pub username: String,
|
||||
pub display_name: String,
|
||||
pub flags: i64,
|
||||
pub version: i64,
|
||||
pub created_at: i64,
|
||||
pub follow_count: i64,
|
||||
pub subscriber_count: i64,
|
||||
pub permissions: Permissions,
|
||||
pub timeline_stats: TimelineStats,
|
||||
pub profile_access_flags: i64,
|
||||
pub profile_flags: i64,
|
||||
pub about: String,
|
||||
pub location: String,
|
||||
pub profile_socials: Vec<Value>,
|
||||
pub status_id: i64,
|
||||
pub last_seen_at: i64,
|
||||
pub post_likes: i64,
|
||||
pub main_wallet: MainWallet,
|
||||
pub streaming: Streaming,
|
||||
pub account_media_likes: i64,
|
||||
pub earnings_wallet: EarningsWallet,
|
||||
pub subscription_tiers: Vec<SubscriptionTier>,
|
||||
pub profile_access: bool,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Permissions {
|
||||
pub account_permission_flags: AccountPermissionFlags,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct AccountPermissionFlags {
|
||||
pub flags: i64,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct TimelineStats {
|
||||
pub account_id: String,
|
||||
pub image_count: i64,
|
||||
pub video_count: i64,
|
||||
pub bundle_count: i64,
|
||||
pub bundle_image_count: i64,
|
||||
pub bundle_video_count: i64,
|
||||
pub fetched_at: i64,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct MainWallet {
|
||||
pub id: String,
|
||||
pub account_id: String,
|
||||
pub balance: i64,
|
||||
#[serde(rename = "type")]
|
||||
pub type_field: i64,
|
||||
pub wallet_version: i64,
|
||||
pub flags: i64,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Streaming {
|
||||
pub account_id: String,
|
||||
pub channel: Value,
|
||||
pub enabled: bool,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct EarningsWallet {
|
||||
pub id: String,
|
||||
pub account_id: String,
|
||||
pub balance: i64,
|
||||
#[serde(rename = "type")]
|
||||
pub type_field: i64,
|
||||
pub wallet_version: i64,
|
||||
pub flags: i64,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct SubscriptionTier {
|
||||
pub id: String,
|
||||
pub account_id: String,
|
||||
pub name: String,
|
||||
pub color: String,
|
||||
pub pos: i64,
|
||||
pub price: i64,
|
||||
pub max_subscribers: i64,
|
||||
pub subscription_benefits: Vec<String>,
|
||||
pub included_tier_ids: Vec<Value>,
|
||||
pub plans: Vec<Plan>,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Plan {
|
||||
pub id: String,
|
||||
pub status: i64,
|
||||
pub billing_cycle: i64,
|
||||
pub price: i64,
|
||||
pub use_amounts: i64,
|
||||
pub promos: Vec<Value>,
|
||||
pub uses: i64,
|
||||
}
|
||||
Reference in New Issue
Block a user