get all achievement data for games
This commit is contained in:
226
src/main.rs
226
src/main.rs
@@ -31,37 +31,11 @@ struct JsonAchievement {
|
||||
defaultvalue: i32,
|
||||
display_name: String,
|
||||
hidden: i32,
|
||||
description: String,
|
||||
description: Option<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,
|
||||
@@ -77,6 +51,50 @@ struct IniGame {
|
||||
achievements: Vec<IniAchievement>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Game {
|
||||
app_id: u32,
|
||||
achievements: Vec<Achievement>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Achievement {
|
||||
name: String,
|
||||
description: Option<String>,
|
||||
achieved: bool,
|
||||
hidden: bool,
|
||||
icon: String,
|
||||
icongray: String,
|
||||
current_progress: u32,
|
||||
max_progress: u32,
|
||||
unlock_time: u32,
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let client = Client::new();
|
||||
let config_dir = dirs::config_dir().unwrap().to_str().unwrap().to_owned();
|
||||
let cache_dir = dirs::cache_dir().unwrap().to_str().unwrap().to_owned();
|
||||
let game_name_cache_path = format!("{cache_dir}/achievement-watcher/game_names.txt");
|
||||
let game_name_list = get_game_name_file_cache(&game_name_cache_path);
|
||||
let api_key =
|
||||
fs::read_to_string(format!("{config_dir}/achievement-watcher/api-key.txt")).unwrap();
|
||||
let api_key = api_key.trim();
|
||||
let wine_dir = env::var("WINEPREFIX").unwrap_or(env::var("HOME").unwrap() + ".wine");
|
||||
let dirs = vec![
|
||||
wine_dir.clone() + "/drive_c/users/Public/Documents/Steam/CODEX",
|
||||
wine_dir.clone() + "/drive_c/users/Public/Documents/Steam/RUNE",
|
||||
];
|
||||
let games = get_achievement_data_all(
|
||||
&client,
|
||||
api_key,
|
||||
&game_name_cache_path,
|
||||
&game_name_list,
|
||||
dirs,
|
||||
).await;
|
||||
println!("games: {:#?}", games);
|
||||
}
|
||||
|
||||
impl IniGame {
|
||||
fn new(id: u32) -> Self {
|
||||
Self {
|
||||
@@ -98,7 +116,67 @@ impl IniAchievement {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_achievement_data_all(dirs: Vec<String>) -> Vec<IniGame> {
|
||||
async fn get_steam_data(client: &Client, key: &str, appid: u32, 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();
|
||||
serde_json::from_str(&json).unwrap()
|
||||
}
|
||||
|
||||
async fn get_achievement_data_all(
|
||||
client: &Client,
|
||||
api_key: &str,
|
||||
game_name_cache_path: &str,
|
||||
game_name_list: &Vec<App>,
|
||||
dirs: Vec<String>,
|
||||
) -> Vec<Game> {
|
||||
let mut games: Vec<Game> = Vec::new();
|
||||
let ini_games = get_achievement_data_all_ini(dirs);
|
||||
for ini_game in ini_games {
|
||||
let mut game = Game {
|
||||
app_id: ini_game.id,
|
||||
achievements: Vec::new(),
|
||||
};
|
||||
let game_data = get_steam_data(&client, api_key, ini_game.id, "en").await;
|
||||
println!(
|
||||
"game: \"{}\", id: {}, total achievements: {}/{}",
|
||||
get_game_name(&client, &game_name_cache_path, &game_name_list, ini_game.id)
|
||||
.await
|
||||
.unwrap(),
|
||||
ini_game.id,
|
||||
ini_game.achievements.len(),
|
||||
game_data.game.available_game_stats.achievements.len(),
|
||||
);
|
||||
for ini_achievement in ini_game.achievements {
|
||||
if ini_achievement.achieved {
|
||||
for json_achievement in &game_data.game.available_game_stats.achievements {
|
||||
if json_achievement.name == ini_achievement.name {
|
||||
game.achievements.push(Achievement {
|
||||
achieved: ini_achievement.achieved,
|
||||
max_progress: ini_achievement.max_progress,
|
||||
current_progress: ini_achievement.current_progress,
|
||||
description: json_achievement.description.clone(),
|
||||
name: json_achievement.display_name.clone(),
|
||||
hidden: if json_achievement.hidden == 1 {
|
||||
true
|
||||
} else if json_achievement.hidden == 0 {
|
||||
false
|
||||
} else {
|
||||
panic!("Unexpected hidden value")
|
||||
},
|
||||
icon: json_achievement.icon.clone(),
|
||||
icongray: json_achievement.icongray.clone(),
|
||||
unlock_time: ini_achievement.unlock_time,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
games.push(game);
|
||||
}
|
||||
games
|
||||
}
|
||||
|
||||
fn get_achievement_data_all_ini(dirs: Vec<String>) -> Vec<IniGame> {
|
||||
let mut ini_games = Vec::new();
|
||||
for dir in dirs {
|
||||
let game_dirs = fs::read_dir(dir).unwrap();
|
||||
@@ -109,18 +187,18 @@ fn get_achievement_data_all(dirs: Vec<String>) -> Vec<IniGame> {
|
||||
.file_name()
|
||||
.to_str()
|
||||
.unwrap()
|
||||
.to_string()
|
||||
.to_owned()
|
||||
.parse()
|
||||
.unwrap(),
|
||||
);
|
||||
let game_dir_path = game_dir.path().to_str().unwrap().to_string();
|
||||
let game_dir_path = game_dir.path().to_str().unwrap().to_owned();
|
||||
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());
|
||||
let mut ini_achievement = IniAchievement::new(section.to_owned());
|
||||
for (k, v) in prop.iter() {
|
||||
match k {
|
||||
"Achieved" => ini_achievement.achieved = v.parse::<u32>().unwrap() == 1,
|
||||
@@ -138,3 +216,89 @@ fn get_achievement_data_all(dirs: Vec<String>) -> Vec<IniGame> {
|
||||
}
|
||||
ini_games
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct AppList {
|
||||
applist: Apps,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct Apps {
|
||||
apps: Vec<App>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct App {
|
||||
name: String,
|
||||
appid: u32,
|
||||
}
|
||||
|
||||
async fn get_game_name(
|
||||
client: &Client,
|
||||
path: &str,
|
||||
game_name_list: &Vec<App>,
|
||||
appid: u32,
|
||||
) -> Option<String> {
|
||||
use std::fs::OpenOptions;
|
||||
use std::io::prelude::*;
|
||||
for app in game_name_list {
|
||||
if app.appid == appid {
|
||||
return Some(app.name.clone());
|
||||
}
|
||||
}
|
||||
let json = client
|
||||
.get("https://api.steampowered.com/ISteamApps/GetAppList/v0002/?format=json")
|
||||
.send()
|
||||
.await
|
||||
.unwrap()
|
||||
.text()
|
||||
.await
|
||||
.unwrap();
|
||||
let applist: AppList = serde_json::from_str(&json).unwrap();
|
||||
let applist = applist.applist.apps;
|
||||
for app in applist {
|
||||
if app.appid == appid {
|
||||
let mut file = OpenOptions::new()
|
||||
.write(true)
|
||||
.append(true)
|
||||
.open(path)
|
||||
.unwrap();
|
||||
writeln!(file, "{}:{}", app.appid, app.name).unwrap();
|
||||
return Some(app.name);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn get_game_name_file_cache(path: &str) -> Vec<App> {
|
||||
std::fs::read_to_string(path)
|
||||
.unwrap()
|
||||
.lines()
|
||||
.map(|m| {
|
||||
let a = m.split_once(":").unwrap();
|
||||
App {
|
||||
appid: a.0.parse().unwrap(),
|
||||
name: a.1.to_owned(),
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[tokio::test]
|
||||
async fn check_id_to_name() {
|
||||
let client = Client::new();
|
||||
let config_dir = dirs::config_dir().unwrap().to_str().unwrap().to_owned();
|
||||
let api_key =
|
||||
fs::read_to_string(format!("{config_dir}/achievement-watcher/api-key.txt")).unwrap();
|
||||
let json_game = get_steam_data(&client, &api_key, 570940, "en").await;
|
||||
println!("game name: {}", json_game.game.game_name);
|
||||
assert!(json_game.game.game_name.to_lowercase() == "Dark Souls Remastered");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user