diff --git a/src/index.ts b/src/index.ts index b5e52abe5..b2f867599 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,17 +1,11 @@ import { app, BrowserWindow } from "electron"; import i18n from "i18next"; import path from "node:path"; -import { - getSteamDBAlgoliaCredentials, - logger, - resolveDatabaseUpdates, - WindowManager, -} from "@main/services"; +import { resolveDatabaseUpdates, WindowManager } from "@main/services"; import { updateElectronApp } from "update-electron-app"; import { dataSource } from "@main/data-source"; import * as resources from "@locales"; import { userPreferencesRepository } from "@main/repository"; -import { stateManager } from "@main/state-manager"; const gotTheLock = app.requestSingleInstanceLock(); if (!gotTheLock) app.quit(); @@ -49,13 +43,6 @@ if (process.defaultApp) { // Some APIs can only be used after this event occurs. app.on("ready", () => { dataSource.initialize().then(async () => { - try { - const algoliaCredentials = await getSteamDBAlgoliaCredentials(); - stateManager.setValue("steamDBAlgoliaCredentials", algoliaCredentials); - } catch (err) { - logger.error(err, { method: "getSteamDBAlgoliaCredentials" }); - } - await resolveDatabaseUpdates(); await import("./main"); diff --git a/src/main/events/helpers/search-games.ts b/src/main/events/helpers/search-games.ts index 9b3b6ef6d..b0524364b 100644 --- a/src/main/events/helpers/search-games.ts +++ b/src/main/events/helpers/search-games.ts @@ -4,7 +4,7 @@ import orderBy from "lodash/orderBy"; import type { GameRepack, GameShop, CatalogueEntry } from "@types"; import { formatName, getSteamAppAsset, repackerFormatter } from "@main/helpers"; -import { searchAlgolia } from "@main/services"; +import { searchSteamGame } from "@main/services"; import { stateManager } from "@main/state-manager"; const { Index } = flexSearch; @@ -20,8 +20,6 @@ for (let i = 0; i < repacks.length; i++) { repacksIndex.add(i, formatName(formatter(repack.title))); } -export const HITS_PER_PAGE = 12; - export const searchRepacks = (title: string): GameRepack[] => { const repacks = stateManager.getValue("repacks"); @@ -37,23 +35,13 @@ export const searchRepacks = (title: string): GameRepack[] => { export const searchGames = async (query: string): Promise => { const formattedName = formatName(query); - const steamResults = await searchAlgolia<{ objectID: string; name: string }>({ - index: "steamdb", - query: formattedName, - params: { - facetFilters: '["appType:Game"]', - hitsPerPage: `${HITS_PER_PAGE}`, - }, - headers: { - Referer: "https://steamdb.info/", - }, - }); + const steamResults = await searchSteamGame(formattedName); - const results = steamResults.hits.map((hit) => ({ - objectID: hit.objectID, - title: hit.name, + const results = steamResults.map((result) => ({ + objectID: result.objectID, + title: result.name, shop: "steam" as GameShop, - cover: getSteamAppAsset("library", hit.objectID), + cover: getSteamAppAsset("library", result.objectID), })); const gamesIndex = new Index({ diff --git a/src/main/services/algolia.ts b/src/main/services/algolia.ts deleted file mode 100644 index 673ab097d..000000000 --- a/src/main/services/algolia.ts +++ /dev/null @@ -1,61 +0,0 @@ -import axios, { RawAxiosRequestHeaders } from "axios"; -import { requestWebPage } from "./repack-tracker/helpers"; -import { stateManager } from "@main/state-manager"; - -export interface AlgoliaResponse { - hits: T[]; -} - -export interface AlgoliaSearchParams { - index: string; - query: string; - params?: Record; - headers?: RawAxiosRequestHeaders; -} - -export const getSteamDBAlgoliaCredentials = async () => { - const searchParams = new URLSearchParams({ - t: new Date().getTime().toString(), - }); - - const js = await requestWebPage( - `https://steamdb.info/static/js/instantsearch.js?${searchParams.toString()}` - ); - - const algoliaCredentialsRegExp = new RegExp( - /algoliasearch\("(.*?)",atob\("(.*?)"\)\);/ - ); - - const [, applicationId, encodedApiKey] = algoliaCredentialsRegExp.exec(js); - - return { - applicationId, - apiKey: Buffer.from(encodedApiKey, "base64").toString("utf-8"), - }; -}; - -export const searchAlgolia = async ( - params: AlgoliaSearchParams -): Promise> => { - const algoliaCredentials = stateManager.getValue("steamDBAlgoliaCredentials"); - - const searchParams = new URLSearchParams({ - "x-algolia-agent": - "Algolia for JavaScript (4.13.1); Browser (lite); JS Helper (3.9.0); react (18.1.0); react-instantsearch (6.29.0)", - "x-algolia-application-id": algoliaCredentials.applicationId, - "x-algolia-api-key": algoliaCredentials.apiKey, - query: params.query, - ...params.params, - }); - - return axios - .get( - `https://${algoliaCredentials.applicationId.toLowerCase()}-dsn.algolia.net/1/indexes/${ - params.index - }?${searchParams.toString()}`, - { - headers: params.headers, - } - ) - .then((response) => response.data); -}; diff --git a/src/main/services/index.ts b/src/main/services/index.ts index 59b5c034c..4f62c84db 100644 --- a/src/main/services/index.ts +++ b/src/main/services/index.ts @@ -1,4 +1,3 @@ -export * from "./algolia"; export * from "./logger"; export * from "./repack-tracker"; export * from "./steam"; diff --git a/src/main/services/steam.ts b/src/main/services/steam.ts index 510e3d883..c6220c710 100644 --- a/src/main/services/steam.ts +++ b/src/main/services/steam.ts @@ -1,5 +1,8 @@ -import type { SteamAppDetails } from "@types"; import axios from "axios"; +import { JSDOM } from "jsdom"; + +import type { SteamAppDetails } from "@types"; + import { logger } from "./logger"; export interface SteamAppDetailsResponse { @@ -31,3 +34,45 @@ export const getSteamAppDetails = async ( throw new Error(err); }); }; + +export const searchSteamGame = async (term: string) => { + const searchParams = new URLSearchParams({ + start: "0", + count: "50", + sort_by: "_ASC", + /* Games only */ + category1: "998", + term: term, + }); + + const response = await axios.get( + `https://store.steampowered.com/search/results/?${searchParams.toString()}` + ); + + const { window } = new JSDOM(response.data); + const { document } = window; + + const $anchors = Array.from( + document.querySelectorAll("#search_resultsRows a") + ); + + return $anchors.reduce((prev, $a) => { + const $title = $a.querySelector(".title"); + const objectIDs = $a.getAttribute("data-ds-appid"); + + if (!objectIDs) return prev; + + const [objectID] = objectIDs.split(","); + + if (!objectID || prev.some((game) => game.objectID === objectID)) + return prev; + + return [ + ...prev, + { + name: $title.textContent, + objectID, + }, + ]; + }, []); +}; diff --git a/src/main/state-manager.ts b/src/main/state-manager.ts index 28320751b..68d61c338 100644 --- a/src/main/state-manager.ts +++ b/src/main/state-manager.ts @@ -4,20 +4,12 @@ interface State { repacks: Repack[]; repackersFriendlyNames: RepackerFriendlyName[]; eventResults: Map<[string, any[]], any>; - steamDBAlgoliaCredentials: { - applicationId: string; - apiKey: string; - }; } const initialState: State = { repacks: [], repackersFriendlyNames: [], eventResults: new Map(), - steamDBAlgoliaCredentials: { - applicationId: "", - apiKey: "", - }, }; export class StateManager {