initial
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
/target
|
||||
*.json
|
||||
Generated
+1694
File diff suppressed because it is too large
Load Diff
+13
@@ -0,0 +1,13 @@
|
||||
[package]
|
||||
name = "achievement-watcher"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
dirs = "6.0.0"
|
||||
futures = "0.3.31"
|
||||
reqwest = "0.12.12"
|
||||
rust-ini = "0.21.1"
|
||||
serde = { version = "1.0.217", features = ["derive"] }
|
||||
serde_json = "1.0.137"
|
||||
tokio = { version = "1.43.0", default-features = false, features = ["rt-multi-thread", "macros"] }
|
||||
+140
@@ -0,0 +1,140 @@
|
||||
use dirs;
|
||||
use ini::Ini;
|
||||
use reqwest::Client;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json;
|
||||
use std::{env, fs};
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct JsonGame {
|
||||
game: JsonGameContent,
|
||||
}
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct JsonGameContent {
|
||||
game_name: String,
|
||||
game_version: String,
|
||||
available_game_stats: JsonAvailableGameStats,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct JsonAvailableGameStats {
|
||||
achievements: Vec<JsonAchievement>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct JsonAchievement {
|
||||
name: String,
|
||||
defaultvalue: i32,
|
||||
display_name: String,
|
||||
hidden: i32,
|
||||
description: String,
|
||||
icon: String,
|
||||
icongray: String,
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let client = Client::new();
|
||||
let config_home = dirs::config_dir().unwrap().to_str().unwrap().to_string();
|
||||
let api_key =
|
||||
fs::read_to_string(format!("{config_home}/achievement-watcher/api-key.txt")).unwrap();
|
||||
let api_key = api_key.trim();
|
||||
let wine_home = env::var("WINEPREFIX").unwrap_or(env::var("HOME").unwrap() + ".wine");
|
||||
let dirs = vec![
|
||||
wine_home.clone() + "/drive_c/users/Public/Documents/Steam/CODEX",
|
||||
wine_home.clone() + "/drive_c/users/Public/Documents/Steam/RUNE",
|
||||
];
|
||||
let ini_games = get_achievement_data_all(dirs);
|
||||
println!("ini games: {:#?}", ini_games);
|
||||
let game_data = get_steam_data(&client, api_key, "2310", "en").await;
|
||||
println!("game_data: {:#?}", game_data);
|
||||
}
|
||||
|
||||
async fn get_steam_data(client: &Client, key: &str, appid: &str, lang: &str) -> JsonGame {
|
||||
let url = format!("https://api.steampowered.com/ISteamUserStats/GetSchemaForGame/v0002/?key={key}&appid={appid}&l={lang}&format=json");
|
||||
let json = client.get(url).send().await.unwrap().text().await.unwrap();
|
||||
// fs::write("out.json", &json).unwrap();
|
||||
// let json = fs::read_to_string("out.json").unwrap();
|
||||
serde_json::from_str(&json).unwrap()
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct IniAchievement {
|
||||
name: String,
|
||||
achieved: bool,
|
||||
current_progress: u32,
|
||||
max_progress: u32,
|
||||
unlock_time: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct IniGame {
|
||||
id: u32,
|
||||
achievements: Vec<IniAchievement>,
|
||||
}
|
||||
|
||||
impl IniGame {
|
||||
fn new(id: u32) -> Self {
|
||||
Self {
|
||||
id,
|
||||
achievements: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IniAchievement {
|
||||
fn new(name: String) -> Self {
|
||||
Self {
|
||||
name,
|
||||
achieved: false,
|
||||
current_progress: 0,
|
||||
max_progress: 0,
|
||||
unlock_time: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_achievement_data_all(dirs: Vec<String>) -> Vec<IniGame> {
|
||||
let mut ini_games = Vec::new();
|
||||
for dir in dirs {
|
||||
let game_dirs = fs::read_dir(dir).unwrap();
|
||||
for game_dir in game_dirs {
|
||||
let game_dir = game_dir.unwrap();
|
||||
let mut ini_game = IniGame::new(
|
||||
game_dir
|
||||
.file_name()
|
||||
.to_str()
|
||||
.unwrap()
|
||||
.to_string()
|
||||
.parse()
|
||||
.unwrap(),
|
||||
);
|
||||
let game_dir_path = game_dir.path().to_str().unwrap().to_string();
|
||||
let i = Ini::load_from_file(format!("{game_dir_path}/achievements.ini",)).unwrap();
|
||||
for (sec, prop) in i.iter() {
|
||||
if let Some(section) = sec {
|
||||
if section == "SteamAchievements" {
|
||||
continue;
|
||||
}
|
||||
let mut ini_achievement = IniAchievement::new(section.to_string());
|
||||
for (k, v) in prop.iter() {
|
||||
match k {
|
||||
"Achieved" => ini_achievement.achieved = v.parse::<u32>().unwrap() == 1,
|
||||
"CurProgress" => ini_achievement.current_progress = v.parse().unwrap(),
|
||||
"MaxProgress" => ini_achievement.max_progress = v.parse().unwrap(),
|
||||
"UnlockTime" => ini_achievement.unlock_time = v.parse().unwrap(),
|
||||
s => panic!("unexpected achievement stat {s}"),
|
||||
}
|
||||
}
|
||||
ini_game.achievements.push(ini_achievement);
|
||||
}
|
||||
}
|
||||
ini_games.push(ini_game);
|
||||
}
|
||||
}
|
||||
ini_games
|
||||
}
|
||||
Reference in New Issue
Block a user