diff --git a/scripts/update_oauth_resources.sh b/scripts/update_oauth_resources.sh
new file mode 100755
index 0000000..5a3ed6d
--- /dev/null
+++ b/scripts/update_oauth_resources.sh
@@ -0,0 +1,99 @@
+#!/bin/bash
+
+# Fetch iOS app versions
+ios_version_list=$(curl -s "https://ipaarchive.com/app/usa/1064216828" | rg "(20\d{2}\.\d+.\d+) / (\d+)" --only-matching -r "Version \$1/Build \$2" | sort | uniq)
+
+# Count the number of lines in the version list
+ios_app_count=$(echo "$ios_version_list" | wc -l)
+
+echo -e "Fetching \e[34m$ios_app_count iOS app versions...\e[0m"
+
+
+# Specify the filename as a variable
+filename="src/oauth_resources.rs"
+
+# Add comment that it is user generated
+echo "// This file was generated by scripts/update_oauth_resources.sh" >> "$filename"
+echo "// Rerun scripts/update_oauth_resources.sh to update this file" >> "$filename"
+echo "// Please do not edit manually" >> "$filename"
+echo "// Filled in with real app versions" >> "$filename"
+
+# Open the array in the source file
+echo "pub static IOS_APP_VERSION_LIST: &[&str; $ios_app_count] = &[" >> "$filename"
+
+
+# Append the version list to the source file
+echo "$ios_version_list" | while IFS= read -r line; do
+ echo " \"$line\"," >> "$filename"
+ echo -e "Fetched \e[34m$line\e[0m."
+done
+
+# Close the array in the source file
+echo "];" >> "$filename"
+
+# Fetch Android app versions
+page_1=$(curl -s "https://apkcombo.com/reddit/com.reddit.frontpage/old-versions/" | rg "" -r "https://apkcombo.com\$1" | sort | uniq)
+# Append with pages
+page_2=$(curl -s "https://apkcombo.com/reddit/com.reddit.frontpage/old-versions?page=2" | rg "" -r "https://apkcombo.com\$1" | sort | uniq)
+page_3=$(curl -s "https://apkcombo.com/reddit/com.reddit.frontpage/old-versions?page=3" | rg "" -r "https://apkcombo.com\$1" | sort | uniq)
+page_4=$(curl -s "https://apkcombo.com/reddit/com.reddit.frontpage/old-versions?page=4" | rg "" -r "https://apkcombo.com\$1" | sort | uniq)
+page_5=$(curl -s "https://apkcombo.com/reddit/com.reddit.frontpage/old-versions?page=5" | rg "" -r "https://apkcombo.com\$1" | sort | uniq)
+
+# Concatenate all pages
+versions="${page_1}"
+versions+=$'\n'
+versions+="${page_2}"
+versions+=$'\n'
+versions+="${page_3}"
+versions+=$'\n'
+versions+="${page_4}"
+versions+=$'\n'
+versions+="${page_5}"
+
+# Count the number of lines in the version list
+android_count=$(echo "$versions" | wc -l)
+
+echo -e "Fetching \e[32m$android_count Android app versions...\e[0m"
+
+# Append to the source file
+echo "pub static ANDROID_APP_VERSION_LIST: &[&str; $android_count] = &[" >> "$filename"
+
+# For each in versions, curl the page and extract the build number
+echo "$versions" | while IFS= read -r line; do
+ fetch_page=$(curl -s "$line")
+ build=$(echo "$fetch_page" | rg "\((\d+)\)" --only-matching -r "\$1" | head -n1)
+ version=$(echo "$fetch_page" | rg "Reddit (20\d{2}\.\d+\.\d+)" --only-matching -r "\$1" | head -n1)
+ echo " \"Version $version/Build $build\"," >> "$filename"
+ echo -e "Fetched \e[32mVersion $version/Build $build\e[0m"
+done
+
+# Close the array in the source file
+echo "];" >> "$filename"
+
+# Retrieve iOS versions
+table=$(curl -s "https://en.wikipedia.org/w/api.php?action=parse&page=IOS_17&prop=wikitext§ion=31&format=json" | jq ".parse.wikitext.\"*\"" | rg "(17\.[\d\.]*)\\\n\|(\w*)\\\n\|" --only-matching -r "Version \$1 (Build \$2)")
+
+# Count the number of lines in the version list
+ios_count=$(echo "$table" | wc -l)
+
+echo -e "Fetching \e[34m$ios_count iOS versions...\e[0m"
+
+# Append to the source file
+echo "pub static IOS_OS_VERSION_LIST: &[&str; $ios_count] = &[" >> "$filename"
+
+# For each in versions, curl the page and extract the build number
+echo "$table" | while IFS= read -r line; do
+ echo " \"$line\"," >> "$filename"
+ echo -e "Fetched $line\e[0m"
+done
+
+# Close the array in the source file
+echo "];" >> "$filename"
+
+echo -e "\e[34mRetrieved $ios_app_count iOS app versions.\e[0m"
+echo -e "\e[32mRetrieved $android_count Android app versions.\e[0m"
+echo -e "\e[34mRetrieved $ios_count iOS versions.\e[0m"
+
+echo -e "\e[34mTotal: $((ios_app_count + android_count + ios_count))\e[0m"
+
+echo -e "\e[32mSuccess!\e[0m"
diff --git a/src/client.rs b/src/client.rs
index e60135f..757a8e9 100644
--- a/src/client.rs
+++ b/src/client.rs
@@ -332,7 +332,7 @@ pub async fn json(path: String, quarantine: bool) -> Result {
}
}
-#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
+#[tokio::test(flavor = "multi_thread", worker_threads = 8)]
async fn test_localization_popular() {
let val = json("/r/popular/hot.json?&raw_json=1&geo_filter=GLOBAL".to_string(), false).await.unwrap();
assert_eq!("GLOBAL", val["data"]["geo_filter"].as_str().unwrap());
diff --git a/src/main.rs b/src/main.rs
index fc4069b..ff6c9ec 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -7,6 +7,7 @@ mod config;
mod duplicates;
mod instance_info;
mod oauth;
+mod oauth_resources;
mod post;
mod search;
mod settings;
diff --git a/src/oauth.rs b/src/oauth.rs
index 23a27e7..2513ff5 100644
--- a/src/oauth.rs
+++ b/src/oauth.rs
@@ -1,6 +1,9 @@
use std::{collections::HashMap, time::Duration};
-use crate::client::{CLIENT, OAUTH_CLIENT};
+use crate::{
+ client::{CLIENT, OAUTH_CLIENT},
+ oauth_resources::{ANDROID_APP_VERSION_LIST, IOS_APP_VERSION_LIST, IOS_OS_VERSION_LIST},
+};
use base64::{engine::general_purpose, Engine as _};
use hyper::{client, Body, Method, Request};
use log::info;
@@ -12,26 +15,10 @@ static REDDIT_IOS_OAUTH_CLIENT_ID: &str = "LNDo9k1o8UAEUw";
static AUTH_ENDPOINT: &str = "https://accounts.reddit.com";
-// Various Android user agents - build numbers from valid APK variants
-pub static ANDROID_USER_AGENT: [&str; 3] = [
- "Reddit/Version 2023.21.0/Build 956283/Android 13",
- "Reddit/Version 2023.21.0/Build 968223/Android 10",
- "Reddit/Version 2023.21.0/Build 946732/Android 12",
-];
-
-// Various iOS user agents - iOS versions.
-pub static IOS_USER_AGENT: [&str; 3] = [
- "Reddit/Version 2023.22.0/Build 613580/iOS Version 17.0 (Build 21A5248V)",
- "Reddit/Version 2023.22.0/Build 613580/iOS Version 16.0 (Build 20A5328h)",
- "Reddit/Version 2023.22.0/Build 613580/iOS Version 16.5",
-];
-// Various iOS device codes. iPhone 11 displays as `iPhone12,1`
-// I just changed the number a few times for some plausible values
-pub static IOS_DEVICES: [&str; 5] = ["iPhone8,1", "iPhone11,1", "iPhone12,1", "iPhone13,1", "iPhone14,1"];
-
+// Spoofed client for Android and iOS devices
#[derive(Debug, Clone, Default)]
pub struct Oauth {
- // Currently unused, may be necessary if we decide to support GQL in the future
+ pub(crate) initial_headers: HashMap,
pub(crate) headers_map: HashMap,
pub(crate) token: String,
expires_in: u64,
@@ -47,10 +34,12 @@ impl Oauth {
pub(crate) fn default() -> Self {
// Generate a random device to spoof
let device = Device::random();
- let headers = device.headers.clone();
+ let headers_map = device.headers.clone();
+ let initial_headers = device.initial_headers.clone();
// For now, just insert headers - no token request
Self {
- headers_map: headers,
+ headers_map,
+ initial_headers,
token: String::new(),
expires_in: 0,
device,
@@ -62,16 +51,8 @@ impl Oauth {
let mut builder = Request::builder().method(Method::POST).uri(&url);
// Add headers from spoofed client
- for (key, value) in self.headers_map.iter() {
- // Skip Authorization header - won't be present in `Device` struct
- // and will only be there in subsequent token refreshes.
- // Sending a bearer auth token when requesting one is a bad idea
- // Normally, you'd want to send it along to authenticate a refreshed token,
- // but neither Android nor iOS does this - it just requests a new token.
- // We try to match behavior as closely as possible.
- if key != "Authorization" {
- builder = builder.header(key, value);
- }
+ for (key, value) in self.initial_headers.iter() {
+ builder = builder.header(key, value);
}
// Set up HTTP Basic Auth - basically just the const OAuth ID's with no password,
// Base64-encoded. https://en.wikipedia.org/wiki/Basic_access_authentication
@@ -82,7 +63,7 @@ impl Oauth {
// Set JSON body. I couldn't tell you what this means. But that's what the client sends
let json = json!({
- "scopes": ["*","email","pii"]
+ "scopes": ["*","email"]
});
let body = Body::from(json.to_string());
@@ -100,6 +81,11 @@ impl Oauth {
self.headers_map.insert("x-reddit-loid".to_owned(), header.to_str().ok()?.to_string());
}
+ // Same with x-reddit-session
+ if let Some(header) = resp.headers().get("x-reddit-session") {
+ self.headers_map.insert("x-reddit-session".to_owned(), header.to_str().ok()?.to_string());
+ }
+
// Serialize response
let body_bytes = hyper::body::to_bytes(resp.into_body()).await.ok()?;
let json: serde_json::Value = serde_json::from_slice(&body_bytes).ok()?;
@@ -109,7 +95,7 @@ impl Oauth {
self.expires_in = json.get("expires_in")?.as_u64()?;
self.headers_map.insert("Authorization".to_owned(), format!("Bearer {}", self.token));
- info!("✅ Success - Retrieved token \"{}...\", expires in {}", &self.token[..32], self.expires_in);
+ info!("[✅] Success - Retrieved token \"{}...\", expires in {}", &self.token[..32], self.expires_in);
Some(())
}
@@ -132,11 +118,11 @@ pub async fn token_daemon() {
// sleep for the expiry time minus 2 minutes
let duration = Duration::from_secs(expires_in - 120);
- info!("Waiting for {duration:?} seconds before refreshing OAuth token...");
+ info!("[⏳] Waiting for {duration:?} seconds before refreshing OAuth token...");
tokio::time::sleep(duration).await;
- info!("[{duration:?} ELAPSED] Refreshing OAuth token...");
+ info!("[⌛] {duration:?} Elapsed! Refreshing OAuth token...");
// Refresh token - in its own scope
{
@@ -147,6 +133,7 @@ pub async fn token_daemon() {
#[derive(Debug, Clone, Default)]
struct Device {
oauth_id: String,
+ initial_headers: HashMap,
headers: HashMap,
}
@@ -155,8 +142,11 @@ impl Device {
// Generate uuid
let uuid = uuid::Uuid::new_v4().to_string();
- // Select random user agent from ANDROID_USER_AGENT
- let android_user_agent = choose(&ANDROID_USER_AGENT).to_string();
+ // Generate random user-agent
+ let android_app_version = choose(ANDROID_APP_VERSION_LIST).to_string();
+ let android_version = fastrand::u8(9..=14);
+
+ let android_user_agent = format!("Reddit/{android_app_version}/Android {android_version}");
// Android device headers
let headers = HashMap::from([
@@ -165,36 +155,47 @@ impl Device {
("User-Agent".into(), android_user_agent),
]);
- info!("Spoofing Android client with headers: {headers:?}, uuid: \"{uuid}\", and OAuth ID \"{REDDIT_ANDROID_OAUTH_CLIENT_ID}\"");
+ info!("[🔄] Spoofing Android client with headers: {headers:?}, uuid: \"{uuid}\", and OAuth ID \"{REDDIT_ANDROID_OAUTH_CLIENT_ID}\"");
Self {
oauth_id: REDDIT_ANDROID_OAUTH_CLIENT_ID.to_string(),
- headers,
+ headers: headers.clone(),
+ initial_headers: headers,
}
}
fn ios() -> Self {
// Generate uuid
let uuid = uuid::Uuid::new_v4().to_string();
- // Select random user agent from IOS_USER_AGENT
- let ios_user_agent = choose(&IOS_USER_AGENT).to_string();
+ // Generate random user-agent
+ let ios_app_version = choose(IOS_APP_VERSION_LIST).to_string();
+ let ios_os_version = choose(IOS_OS_VERSION_LIST).to_string();
+ let ios_user_agent = format!("Reddit/{ios_app_version}/iOS {ios_os_version}");
- // Select random iOS device from IOS_DEVICES
- let ios_device = choose(&IOS_DEVICES).to_string();
+ // Generate random device
+ let ios_device_num = fastrand::u8(8..=15).to_string();
+ let ios_device = format!("iPhone{ios_device_num},1").to_string();
- // iOS device headers
+ let initial_headers = HashMap::from([
+ ("X-Reddit-DPR".into(), "2".into()),
+ ("User-Agent".into(), ios_user_agent.clone()),
+ ("Device-Name".into(), ios_device.clone()),
+ ]);
let headers = HashMap::from([
("X-Reddit-DPR".into(), "2".into()),
("Device-Name".into(), ios_device.clone()),
- ("X-Reddit-Device-Id".into(), uuid.clone()),
("User-Agent".into(), ios_user_agent),
("Client-Vendor-Id".into(), uuid.clone()),
+ ("x-dev-ad-id".into(), "00000000-0000-0000-0000-000000000000".into()),
+ ("Reddit-User_Id".into(), "anonymous_browsing_mode".into()),
+ ("x-reddit-device-id".into(), uuid.clone()),
]);
- info!("Spoofing iOS client {ios_device} with headers: {headers:?}, uuid: \"{uuid}\", and OAuth ID \"{REDDIT_IOS_OAUTH_CLIENT_ID}\"");
+ info!("[🔄] Spoofing iOS client {ios_device} with headers: {headers:?}, uuid: \"{uuid}\", and OAuth ID \"{REDDIT_IOS_OAUTH_CLIENT_ID}\"");
Self {
oauth_id: REDDIT_IOS_OAUTH_CLIENT_ID.to_string(),
+ initial_headers,
headers,
}
}
@@ -208,18 +209,30 @@ impl Device {
}
}
-// Waiting on fastrand 2.0.0 for the `choose` function
-// https://github.com/smol-rs/fastrand/pull/59/
fn choose(list: &[T]) -> T {
*fastrand::choose_multiple(list.iter(), 1)[0]
}
-#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
+#[tokio::test(flavor = "multi_thread", worker_threads = 8)]
async fn test_oauth_client() {
assert!(!OAUTH_CLIENT.read().await.token.is_empty());
}
-#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
+#[tokio::test(flavor = "multi_thread", worker_threads = 8)]
async fn test_oauth_client_refresh() {
OAUTH_CLIENT.write().await.refresh().await.unwrap();
}
+#[tokio::test(flavor = "multi_thread", worker_threads = 8)]
+async fn test_oauth_token_exists() {
+ assert!(!OAUTH_CLIENT.read().await.token.is_empty());
+}
+
+#[tokio::test(flavor = "multi_thread", worker_threads = 8)]
+async fn test_oauth_headers_len() {
+ assert!(OAUTH_CLIENT.read().await.headers_map.len() >= 3);
+}
+
+#[test]
+fn test_creating_device() {
+ Device::random();
+}
diff --git a/src/oauth_resources.rs b/src/oauth_resources.rs
new file mode 100644
index 0000000..9b9f7d8
--- /dev/null
+++ b/src/oauth_resources.rs
@@ -0,0 +1,231 @@
+pub static IOS_APP_VERSION_LIST: &[&str; 67] = &[
+ "Version 2020.0.0/Build 306960",
+ "Version 2020.10.0/Build 307041",
+ "Version 2020.10.1/Build 307047",
+ "Version 2020.1.0/Build 306966",
+ "Version 2020.11.0/Build 307049",
+ "Version 2020.11.1/Build 307063",
+ "Version 2020.12.0/Build 307070",
+ "Version 2020.13.0/Build 307072",
+ "Version 2020.13.1/Build 307075",
+ "Version 2020.14.0/Build 307077",
+ "Version 2020.14.1/Build 307080",
+ "Version 2020.15.0/Build 307084",
+ "Version 2020.16.0/Build 307090",
+ "Version 2020.17.0/Build 307093",
+ "Version 2020.19.0/Build 307137",
+ "Version 2020.20.0/Build 307156",
+ "Version 2020.20.1/Build 307159",
+ "Version 2020.2.0/Build 306969",
+ "Version 2020.21.0/Build 307162",
+ "Version 2020.21.1/Build 307165",
+ "Version 2020.22.0/Build 307177",
+ "Version 2020.22.1/Build 307181",
+ "Version 2020.23.0/Build 307183",
+ "Version 2020.24.0/Build 307189",
+ "Version 2020.25.0/Build 307198",
+ "Version 2020.26.0/Build 307205",
+ "Version 2020.26.1/Build 307213",
+ "Version 2020.27.0/Build 307229",
+ "Version 2020.28.0/Build 307233",
+ "Version 2020.29.0/Build 307235",
+ "Version 2020.30.0/Build 307238",
+ "Version 2020.3.0/Build 306971",
+ "Version 2020.31.0/Build 307240",
+ "Version 2020.31.1/Build 307246",
+ "Version 2020.32.0/Build 307250",
+ "Version 2020.33.0/Build 307252",
+ "Version 2020.34.0/Build 307260",
+ "Version 2020.35.0/Build 307262",
+ "Version 2020.36.0/Build 307265",
+ "Version 2020.37.0/Build 307272",
+ "Version 2020.38.0/Build 307286",
+ "Version 2020.39.0/Build 307306",
+ "Version 2020.4.0/Build 306978",
+ "Version 2020.5.0/Build 306993",
+ "Version 2020.5.1/Build 307005",
+ "Version 2020.6.0/Build 307007",
+ "Version 2020.7.0/Build 307012",
+ "Version 2020.8.0/Build 307014",
+ "Version 2020.8.1/Build 307017",
+ "Version 2020.9.0/Build 307035",
+ "Version 2020.9.1/Build 307039",
+ "Version 2023.18.0/Build 310494",
+ "Version 2023.19.0/Build 310507",
+ "Version 2023.20.0/Build 310535",
+ "Version 2023.21.0/Build 310560",
+ "Version 2023.22.0/Build 613580",
+ "Version 2023.23.0/Build 310613",
+ "Version 2023.23.1/Build 613639",
+ "Version 2023.24.0/Build 613663",
+ "Version 2023.25.0/Build 613739",
+ "Version 2023.26.0/Build 613749",
+ "Version 2023.27.0/Build 613771",
+ "Version 2023.28.0/Build 613803",
+ "Version 2023.28.1/Build 613809",
+ "Version 2023.29.0/Build 613825",
+ "Version 2023.30.0/Build 613849",
+ "Version 2023.31.0/Build 613864",
+];
+pub static ANDROID_APP_VERSION_LIST: &[&str; 150] = &[
+ "Version 2023.25.1/Build 1018737",
+ "Version 2023.26.0/Build 1019073",
+ "Version 2023.27.0/Build 1031923",
+ "Version 2023.28.0/Build 1046887",
+ "Version 2023.29.0/Build 1059855",
+ "Version 2023.30.0/Build 1078734",
+ "Version 2023.31.0/Build 1091027",
+ "Version 2023.32.0/Build 1109919",
+ "Version 2023.32.1/Build 1114141",
+ "Version 2023.33.1/Build 1129741",
+ "Version 2023.34.0/Build 1144243",
+ "Version 2023.35.0/Build 1157967",
+ "Version 2023.36.0/Build 1168982",
+ "Version 2023.37.0/Build 1182743",
+ "Version 2023.38.0/Build 1198522",
+ "Version 2023.39.0/Build 1211607",
+ "Version 2023.39.1/Build 1221505",
+ "Version 2023.40.0/Build 1221521",
+ "Version 2023.41.0/Build 1233125",
+ "Version 2023.41.1/Build 1239615",
+ "Version 2023.42.0/Build 1245088",
+ "Version 2023.43.0/Build 1257426",
+ "Version 2023.44.0/Build 1268622",
+ "Version 2023.45.0/Build 1281371",
+ "Version 2023.47.0/Build 1303604",
+ "Version 2023.48.0/Build 1319123",
+ "Version 2023.49.0/Build 1321715",
+ "Version 2023.49.1/Build 1322281",
+ "Version 2023.50.0/Build 1332338",
+ "Version 2023.50.1/Build 1345844",
+ "Version 2023.02.0/Build 717912",
+ "Version 2023.03.0/Build 729220",
+ "Version 2023.04.0/Build 744681",
+ "Version 2023.05.0/Build 755453",
+ "Version 2023.06.0/Build 775017",
+ "Version 2023.07.0/Build 788827",
+ "Version 2023.07.1/Build 790267",
+ "Version 2023.08.0/Build 798718",
+ "Version 2023.09.0/Build 812015",
+ "Version 2023.09.1/Build 816833",
+ "Version 2023.10.0/Build 821148",
+ "Version 2023.11.0/Build 830610",
+ "Version 2023.12.0/Build 841150",
+ "Version 2023.13.0/Build 852246",
+ "Version 2023.14.0/Build 861593",
+ "Version 2023.14.1/Build 864826",
+ "Version 2023.15.0/Build 870628",
+ "Version 2023.16.0/Build 883294",
+ "Version 2023.16.1/Build 886269",
+ "Version 2023.17.0/Build 896030",
+ "Version 2023.17.1/Build 900542",
+ "Version 2023.18.0/Build 911877",
+ "Version 2023.19.0/Build 927681",
+ "Version 2023.20.0/Build 943980",
+ "Version 2023.20.1/Build 946732",
+ "Version 2023.21.0/Build 956283",
+ "Version 2023.22.0/Build 968223",
+ "Version 2023.23.0/Build 983896",
+ "Version 2023.24.0/Build 998541",
+ "Version 2023.25.0/Build 1014750",
+ "Version 2022.24.0/Build 510950",
+ "Version 2022.24.1/Build 513462",
+ "Version 2022.25.0/Build 515072",
+ "Version 2022.25.1/Build 516394",
+ "Version 2022.25.2/Build 519915",
+ "Version 2022.26.0/Build 521193",
+ "Version 2022.27.0/Build 527406",
+ "Version 2022.27.1/Build 529687",
+ "Version 2022.28.0/Build 533235",
+ "Version 2022.30.0/Build 548620",
+ "Version 2022.31.0/Build 556666",
+ "Version 2022.31.1/Build 562612",
+ "Version 2022.32.0/Build 567875",
+ "Version 2022.33.0/Build 572600",
+ "Version 2022.34.0/Build 579352",
+ "Version 2022.35.0/Build 588016",
+ "Version 2022.35.1/Build 589034",
+ "Version 2022.36.0/Build 593102",
+ "Version 2022.37.0/Build 601691",
+ "Version 2022.38.0/Build 607460",
+ "Version 2022.39.0/Build 615385",
+ "Version 2022.39.1/Build 619019",
+ "Version 2022.40.0/Build 624782",
+ "Version 2022.41.0/Build 630468",
+ "Version 2022.41.1/Build 634168",
+ "Version 2022.42.0/Build 638508",
+ "Version 2022.43.0/Build 648277",
+ "Version 2022.44.0/Build 664348",
+ "Version 2022.45.0/Build 677985",
+ "Version 2023.01.0/Build 709875",
+ "Version 2021.45.0/Build 387663",
+ "Version 2021.46.0/Build 392043",
+ "Version 2021.47.0/Build 394342",
+ "Version 2022.10.0/Build 429896",
+ "Version 2022.1.0/Build 402829",
+ "Version 2022.11.0/Build 433004",
+ "Version 2022.12.0/Build 436848",
+ "Version 2022.13.0/Build 442084",
+ "Version 2022.13.1/Build 444621",
+ "Version 2022.14.1/Build 452742",
+ "Version 2022.15.0/Build 455453",
+ "Version 2022.16.0/Build 462377",
+ "Version 2022.17.0/Build 468480",
+ "Version 2022.18.0/Build 473740",
+ "Version 2022.19.1/Build 482464",
+ "Version 2022.20.0/Build 487703",
+ "Version 2022.2.0/Build 405543",
+ "Version 2022.21.0/Build 492436",
+ "Version 2022.22.0/Build 498700",
+ "Version 2022.23.0/Build 502374",
+ "Version 2022.23.1/Build 506606",
+ "Version 2022.3.0/Build 408637",
+ "Version 2022.4.0/Build 411368",
+ "Version 2022.5.0/Build 414731",
+ "Version 2022.6.0/Build 418391",
+ "Version 2022.6.1/Build 419585",
+ "Version 2022.6.2/Build 420562",
+ "Version 2022.7.0/Build 420849",
+ "Version 2022.8.0/Build 423906",
+ "Version 2022.9.0/Build 426592",
+ "Version 2021.17.0/Build 323213",
+ "Version 2021.18.0/Build 324849",
+ "Version 2021.19.0/Build 325762",
+ "Version 2021.20.0/Build 326964",
+ "Version 2021.21.0/Build 327703",
+ "Version 2021.21.1/Build 328461",
+ "Version 2021.22.0/Build 329696",
+ "Version 2021.23.0/Build 331631",
+ "Version 2021.24.0/Build 333951",
+ "Version 2021.25.0/Build 335451",
+ "Version 2021.26.0/Build 336739",
+ "Version 2021.27.0/Build 338857",
+ "Version 2021.28.0/Build 340747",
+ "Version 2021.29.0/Build 342342",
+ "Version 2021.30.0/Build 343820",
+ "Version 2021.31.0/Build 346485",
+ "Version 2021.32.0/Build 349507",
+ "Version 2021.33.0/Build 351843",
+ "Version 2021.34.0/Build 353911",
+ "Version 2021.35.0/Build 355878",
+ "Version 2021.36.0/Build 359254",
+ "Version 2021.36.1/Build 360572",
+ "Version 2021.37.0/Build 361905",
+ "Version 2021.38.0/Build 365032",
+ "Version 2021.39.0/Build 369068",
+ "Version 2021.39.1/Build 372418",
+ "Version 2021.41.0/Build 376052",
+ "Version 2021.42.0/Build 378193",
+ "Version 2021.43.0/Build 382019",
+ "Version 2021.44.0/Build 385129",
+];
+pub static IOS_OS_VERSION_LIST: &[&str; 8] = &[
+ "Version 17.0.1 (Build 21A340)",
+ "Version 17.0.2 (Build 21A350)",
+ "Version 17.0.3 (Build 21A360)",
+ "Version 17.1 (Build 21B74)",
+ "Version 17.1.1 (Build 21B91)",
+ "Version 17.1.2 (Build 21B101)",
+ "Version 17.2 (Build 21C62)",
+ "Version 17.2.1 (Build 21C66)",
+];
diff --git a/src/subreddit.rs b/src/subreddit.rs
index 560b487..30499f0 100644
--- a/src/subreddit.rs
+++ b/src/subreddit.rs
@@ -443,7 +443,7 @@ async fn subreddit(sub: &str, quarantined: bool) -> Result {
})
}
-#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
+#[tokio::test(flavor = "multi_thread", worker_threads = 8)]
async fn test_fetching_subreddit() {
let subreddit = subreddit("rust", false).await;
assert!(subreddit.is_ok());
diff --git a/src/user.rs b/src/user.rs
index 8ffc366..7b7610a 100644
--- a/src/user.rs
+++ b/src/user.rs
@@ -130,7 +130,7 @@ async fn user(name: &str) -> Result {
})
}
-#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
+#[tokio::test(flavor = "multi_thread", worker_threads = 8)]
async fn test_fetching_user() {
let user = user("spez").await;
assert!(user.is_ok());