mirror of
https://github.com/cmorooney/obsidian-game-search-plugin.git
synced 2026-07-22 07:50:25 +00:00
creates syncSteamAchievements calls in steam_api and steamSync, adds models to support. adds palette command. need to acually inject into template
This commit is contained in:
parent
f124ac08ea
commit
fbb59b3ea0
4 changed files with 134 additions and 19 deletions
|
|
@ -3,7 +3,14 @@
|
|||
import type { Nullable } from '../main';
|
||||
import { requestUrl } from 'obsidian';
|
||||
import { extract } from 'fuzzball';
|
||||
import { SteamResponse, OwnedSteamGames, SteamGame, SteamWishlistedGame } from '@models/steam_game.model';
|
||||
import {
|
||||
SteamResponse,
|
||||
OwnedSteamGames,
|
||||
SteamGame,
|
||||
SteamWishlistedGame,
|
||||
SteamAchievementInfo,
|
||||
SteamUserAchievement,
|
||||
} from '@models/steam_game.model';
|
||||
|
||||
export class SteamAPI {
|
||||
constructor(private readonly key: string, private readonly steamId: string) {}
|
||||
|
|
@ -89,6 +96,7 @@ export class SteamAPI {
|
|||
});
|
||||
|
||||
const games = res?.json?.response?.games;
|
||||
|
||||
if (games) {
|
||||
const ret: { [key: string]: { playtime_forever: Nullable<number>; playtime_2weeks: Nullable<number> } } = {};
|
||||
for (const game of games) {
|
||||
|
|
@ -106,4 +114,42 @@ export class SteamAPI {
|
|||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async getPlayerAchievmentsForGame(gameId: string): Promise<SteamAchievementInfo[]> {
|
||||
try {
|
||||
const userAchievementsUrl = new URL('http://api.steampowered.com/ISteamUserStats/GetUserStatsForGame/v0002/');
|
||||
userAchievementsUrl.searchParams.append('key', this.key);
|
||||
userAchievementsUrl.searchParams.append('format', 'json');
|
||||
userAchievementsUrl.searchParams.append('steamid', this.steamId);
|
||||
userAchievementsUrl.searchParams.append('appid', gameId);
|
||||
|
||||
const achievementDataUrl = new URL('https://api.steampowered.com/ISteamUserStats/GetSchemaForGame/v2');
|
||||
achievementDataUrl.searchParams.append('key', this.key);
|
||||
achievementDataUrl.searchParams.append('format', 'json');
|
||||
achievementDataUrl.searchParams.append('steamid', this.steamId);
|
||||
achievementDataUrl.searchParams.append('appid', gameId);
|
||||
|
||||
// this has readable content we'll need to match
|
||||
const gameResult = await requestUrl({ url: achievementDataUrl.href, method: 'GET' });
|
||||
// this has the user achievement data but nothing readable
|
||||
const userResult = await requestUrl({ url: userAchievementsUrl.href, method: 'GET' });
|
||||
|
||||
const matches: SteamAchievementInfo[] = [];
|
||||
|
||||
if ((userResult?.json?.playerstats?.achievements?.length ?? 0) > 0) {
|
||||
userResult?.json?.playerstats?.achievements
|
||||
.filter(a => a.achieved)
|
||||
.forEach(async (a: SteamUserAchievement) => {
|
||||
const match = gameResult.json.game.availableGameStats.achievements.first(gs => gs.name === a.name);
|
||||
if (match) {
|
||||
matches.push(match);
|
||||
}
|
||||
});
|
||||
}
|
||||
return matches;
|
||||
} catch (error) {
|
||||
console.warn('[Game Search][Steam API][getPlayerAchievementsForGame]' + error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
25
src/main.ts
25
src/main.ts
|
|
@ -24,7 +24,7 @@ import {
|
|||
useTemplaterPluginInFile,
|
||||
executeInlineScriptsTemplates,
|
||||
} from '@utils/template';
|
||||
import { syncSteamWishlist, syncOwnedSteamGames, syncPlaytimes } from '@utils/steamSync';
|
||||
import { syncAchievements, syncSteamWishlist, syncOwnedSteamGames, syncPlaytimes } from '@utils/steamSync';
|
||||
|
||||
export type Nullable<T> = T | undefined | null;
|
||||
|
||||
|
|
@ -78,6 +78,12 @@ export default class GameSearchPlugin extends Plugin {
|
|||
callback: () => this.syncSteamPlaytime(true),
|
||||
});
|
||||
|
||||
this.addCommand({
|
||||
id: 'sync steam achievements',
|
||||
name: 'Sync Steam Achievements',
|
||||
callback: () => this.syncSteamAchievements(true),
|
||||
});
|
||||
|
||||
// This adds a settings tab so the user can configure various aspects of the plugin
|
||||
this.addSettingTab(new GameSearchSettingTab(this.app, this));
|
||||
}
|
||||
|
|
@ -239,6 +245,23 @@ export default class GameSearchPlugin extends Plugin {
|
|||
}).open();
|
||||
}
|
||||
|
||||
async syncSteamAchievements(alertUninitializedApi: boolean): Promise<void> {
|
||||
// always check to see if steamApi needs to be initialized on sync,
|
||||
// it's possible the user has entered API credentials at any point in time.
|
||||
if (this.steamApi === undefined && this.settings.steamApiKey && this.settings.steamUserId) {
|
||||
console.info('[Game Search][Steam Sync]: initializing steam api');
|
||||
this.steamApi = new SteamAPI(this.settings.steamApiKey, this.settings.steamUserId);
|
||||
}
|
||||
if (this.steamApi !== undefined) {
|
||||
new Notice('syncing steam achievements');
|
||||
await syncAchievements(this.app.vault, this.app.fileManager, this.steamApi, this.settings);
|
||||
new Notice('steam achievements sync complete');
|
||||
} else if (alertUninitializedApi) {
|
||||
console.warn('[Game Search][SteamSync]: steam api not initialized');
|
||||
this.showNotice('Steam Api not initialized. Did you enter your steam API key and user Id in plugin settings?');
|
||||
}
|
||||
}
|
||||
|
||||
async syncSteamPlaytime(alertUninitializedApi: boolean): Promise<void> {
|
||||
// always check to see if steamApi needs to be initialized on sync,
|
||||
// it's possible the user has entered API credentials at any point in time.
|
||||
|
|
|
|||
|
|
@ -43,10 +43,45 @@ export interface SteamWishlistedGame {
|
|||
win: number;
|
||||
}
|
||||
|
||||
interface SteamWishlistedGameSub {
|
||||
export interface SteamWishlistedGameSub {
|
||||
packageid: number;
|
||||
bundleid?: number;
|
||||
discount_block: string;
|
||||
discount_pct?: string;
|
||||
price: string;
|
||||
}
|
||||
|
||||
export interface SteamUserAchievement {
|
||||
name: string;
|
||||
achieved: number;
|
||||
}
|
||||
|
||||
export interface SteamAchievementInfo {
|
||||
name: string;
|
||||
defaultvalue: number;
|
||||
displayName: string;
|
||||
hidden: number;
|
||||
description: string;
|
||||
icon: string;
|
||||
icongray: string;
|
||||
}
|
||||
|
||||
export interface SteamGameStats {
|
||||
achievements: SteamAchievementInfo[];
|
||||
}
|
||||
|
||||
export interface SteamAchievementDataResponse {
|
||||
game: {
|
||||
gameName: string;
|
||||
gameVersion: string;
|
||||
availableGameStats: SteamGameStats;
|
||||
};
|
||||
}
|
||||
|
||||
export interface SteamUserStateForGameResponse {
|
||||
playerstats: {
|
||||
steamID: string;
|
||||
gameName: string;
|
||||
achievements: SteamUserAchievement[];
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -165,14 +165,6 @@ export async function syncOwnedSteamGames(
|
|||
}
|
||||
}
|
||||
|
||||
async function parseFileMetadata(fileManager: FileManager, file: TFile): Promise<any> {
|
||||
return new Promise<any>(accept => {
|
||||
fileManager.processFrontMatter(file, (data: any) => {
|
||||
accept(data);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export async function syncPlaytimes(
|
||||
vault: Vault,
|
||||
fileManager: FileManager,
|
||||
|
|
@ -192,14 +184,6 @@ export async function syncPlaytimes(
|
|||
}
|
||||
};
|
||||
|
||||
doForChild(async file => {
|
||||
const noteMetadata = await parseFileMetadata(fileManager, file);
|
||||
const steamId: Nullable<string> = noteMetadata.steamId;
|
||||
if (steamId) {
|
||||
ids.push(steamId);
|
||||
}
|
||||
});
|
||||
|
||||
const playerStats = await steamApi.getPlayerStatsForGames(ids);
|
||||
if (playerStats) {
|
||||
doForChild(async file => {
|
||||
|
|
@ -213,3 +197,30 @@ export async function syncPlaytimes(
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
export async function syncAchievements(
|
||||
vault: Vault,
|
||||
fileManager: FileManager,
|
||||
steamApi: SteamAPI,
|
||||
settings: any,
|
||||
): Promise<void> {
|
||||
const folderPath = normalizePath(settings.folder);
|
||||
const folder = vault.getFolderByPath(folderPath);
|
||||
|
||||
const doForChild = async (func: (file: TFile) => Promise<void>) => {
|
||||
for (const f of folder.children) {
|
||||
const file = f as TFile;
|
||||
if (!!file && file.name.includes('.md')) {
|
||||
await func(file);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
doForChild(async file => {
|
||||
fileManager.processFrontMatter(file, async data => {
|
||||
if (data.steamId) {
|
||||
await steamApi.getPlayerAchievmentsForGame(data.steamId);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue