Source: SteamWebAPILibrary.js

import "dotenv/config";
/**
 * @param {('json'|'')} format The format of the data.
 * @param {string} url The URL to fetch data from.
 * @param {('getNewsForApp'|'getGlobalAchievementPercentagesForApp'|'getPlayerSummaries'|'getFriendList'|'getPlayerAchievements'|'getUserStatsForGame'|'getOwnedGames'|'getRecentlyPlayedGames')} method The method to use.
 * @param {string} specificData Specific data to append to the endpoint.
 * @returns {Promise<any>} The fetched data.
 */
async function handleEndpointOrFormat(format, url, method, specificData) {
  //DRY template for handling if a specific format or data endpoint is specified

  const endpointMapping = {
    getNewsForApp: "appnews",
    getGlobalAchievementPercentagesForApp: "achievementpercentages",
    getPlayerSummaries: "response",
    getFriendList: "friendslist",
    getPlayerAchievements: "playerstats",
    getUserStatsForGame: "playerstats",
    getOwnedGames: "response",
    getRecentlyPlayedGames: "response",
  };

  let dataEndpoint = endpointMapping[method];

  if (!dataEndpoint) {
    console.error("Invalid method: ", method);
    return null;
  }

  if (specificData) {
    dataEndpoint += `.${specificData}`;
  }

  if (format === "json" || format === "") {
    try {
      const response = await fetch(url);
      const data = await response.json();
      const keys = dataEndpoint.split(".");
      const result = keys.reduce((acc, key) => acc[key], data);

      return result;
    } catch (error) {
      console.error("The server returned an error: ", error);
      return null;
    }
  } else if (format === "xml") {
    try {
      const response = await fetch(url);
      if (!response.ok) {
        throw new Error(
          `Error fetching XML-- response code: ${response.status}`
        );
      }
      const xml = await response.text();
      return xml;
    } catch (error) {
      console.error(`\n An error occurred, exiting. \n\n`, error);
      return null;
    }
  } else if (format === "vdf") {
    try {
      const response = await fetch(url);
      if (!response.ok) {
        throw new Error(
          `Error fetching VDF-- response code:  ${response.status}`
        );
      }
      const vdf = await response.text();
      return vdf;
    } catch (error) {
      console.error(`\n An error occurred, exiting. \n\n`, error);
      return null;
    }
  } else {
    console.error(`An unknown or invalid format was specified\n`);
    return null;
  }
}

class CallSteamAPI {
  static #baseURL = `http://api.steampowered.com`;
  cache = new Map();
  /**
   * @constructor
   * @returns {Object} - Steam Web API Library object
   */
  constructor() {
    this.key = process.env.STEAM_KEY;
    try {
      const key = this.key;
      if (!key) {
        throw new Error("A Steam Web API key was not found.");
      }
      const keyRegex = /^[a-zA-Z0-9]{32}$/;
      if (!keyRegex.test(key)) {
        throw new Error(
          "The Steam Web API key provided is invalid. It must be a 32-character alphanumeric string with no special characters."
        );
      }
    } catch (error) {
      console.error(
        `There was a problem instantiating the Steam Web API Library object: \n\n${error}\n`
      );
      return null;
    }
  }
  /**
   *
   * @param {String} appid - App instance ID
   * @param {Number} count - Number of news items to retrieve
   * @param {Number} maxlength - Maximum length of each news item
   * @param {String} format - Format of the data to return
   * @param {String} specificData - Specific data to return from the response
   * @returns {String} - JSON, XML, or VDF data
   */
  async getNewsForApp({
    appid,
    count = 3,
    maxlength = 300,
    format = "json",
    specificData,
    useCache = true,
  }) {
    const hashKey = `${appid}-${count}-${maxlength}-${format}-${specificData}`;

    const endpoint = `/ISteamNews/GetNewsForApp/v0002/`;
    const query = `?appid=${appid}&count=${count}&maxlength=${maxlength}&format=${format}`;
    const url = `${CallSteamAPI.#baseURL}` + endpoint + query;

    try {
      if (!useCache) {
        return handleEndpointOrFormat(
          format,
          url,
          "getNewsForApp",
          specificData
        );
      }
      return this.#cachedFetch(
        hashKey,
        format,
        url,
        "getNewsForApp",
        specificData
      );
    } catch (error) {
      console.error(
        `There was a problem instantiating the Steam Web API Library object: \n\n${error}\n`
      );
      return null;
    }
  }
  /**
   *
   * @param {Number} gameid - Game instance ID
   * @param {String} format - Format of the data to return
   * @param {String} specificData - Specific data to return from the response
   * @returns {String} - JSON, XML, or VDF data
   */
  async getGlobalAchievementPercentagesForApp({
    gameid,
    format = "json",
    specificData,
    useCache = true,
  }) {
    const hashKey = `${gameid}-${format}-${specificData}`;
    const endpoint = `/ISteamUserStats/GetGlobalAchievementPercentagesForApp/v002/`;
    const query = `?gameid=${gameid}&format=${format}`;
    const url = `${CallSteamAPI.#baseURL}` + endpoint + query;

    try {
      if (!useCache) {
        return handleEndpointOrFormat(
          format,
          url,
          "getGlobalAchievementPercentagesForApp",
          specificData
        );
      }
      return this.#cachedFetch(
        hashKey,
        format,
        url,
        "getGlobalAchievementPercentagesForApp",
        specificData
      );
    } catch (error) {
      console.error(
        `There was a problem instantiating the Steam Web API Library object: \n\n${error}\n`
      );
      return null;
    }
  }
  /**
   *
   * @param {Number} steamids - Comma-separated list of Steam IDs
   * @param {String} format - Format of the data to return
   * @param {String} specificData - Specific data to return from the response
   * @returns {String} - JSON, XML, or VDF data
   */
  async getPlayerSummaries({
    steamids,
    format = "json",
    specificData,
    useCache = true,
  }) {
    const hashKey = `${steamids}-${format}-${specificData}`;
    const endpoint = `/ISteamUser/GetPlayerSummaries/v0002/`;
    const query = `?key=${this.key}&steamids=${steamids}&format=${format}`;
    const url = `${CallSteamAPI.#baseURL}` + endpoint + query;

    try {
      if (!useCache) {
        return handleEndpointOrFormat(
          format,
          url,
          "getPlayerSummaries",
          specificData
        );
      }
      return this.#cachedFetch(
        hashKey,
        format,
        url,
        "getPlayerSummaries",
        specificData
      );
    } catch (error) {
      console.error(
        `There was a problem instantiating the Steam Web API Library object: \n\n${error}\n`
      );
      return null;
    }
  }
  /**
   *
   * @param {Number} steamid - Steam ID of the user
   * @param {String} relationship - Relationship type to retrieve
   * @param {String} format - Format of the data to return
   * @param {String} specificData - Specific data to return from the response
   * @returns {String} - JSON, XML, or VDF data
   */
  async getFriendList({
    steamid,
    relationship = `friend`,
    format = "json",
    specificData,
    useCache = true,
  }) {
    const hashKey = `${steamid}-${relationship}-${format}-${specificData}`;
    const endpoint = `/ISteamUser/GetFriendList/v0001/`;
    const query = `?key=${this.key}&steamid=${steamid}&relationship=${relationship}&format=${format}`;
    const url = `${CallSteamAPI.#baseURL}` + endpoint + query;

    try {
      if (!useCache) {
        return handleEndpointOrFormat(
          format,
          url,
          "getFriendList",
          specificData
        );
      }
      return this.#cachedFetch(
        hashKey,
        format,
        url,
        "getFriendList",
        specificData
      );
    } catch (error) {
      console.error(
        `There was a problem instantiating the Steam Web API Library object: \n\n${error}\n`
      );
      return null;
    }
  }
  /**
   *
   * @param {Number} steamid - Steam ID of the user
   * @param {Number} appid - App instance ID
   * @param {String} format - Format of the data to return
   * @param {String} specificData - Specific data to return from the response
   * @returns {String} - JSON, XML, or VDF data
   */
  async getPlayerAchievements({
    steamid,
    appid,
    format = "json",
    specificData,
    useCache = true,
  }) {
    const hashKey = `${steamid}-${appid}-${format}-${specificData}`;
    const endpoint = `/ISteamUserStats/GetPlayerAchievements/v0001/`;
    const query = `?appid=${appid}&key=${this.key}&steamid=${steamid}&format=${format}`;
    const url = `${CallSteamAPI.#baseURL}` + endpoint + query;

    try {
      if (!useCache) {
        return handleEndpointOrFormat(
          format,
          url,
          "getPlayerAchievements",
          specificData
        );
      }
      return this.#cachedFetch(
        hashKey,
        format,
        url,
        "getPlayerAchievements",
        specificData
      );
    } catch (error) {
      console.error(
        `There was a problem instantiating the Steam Web API Library object: \n\n${error}\n`
      );
      return null;
    }
  }
  /**
   *
   * @param {Number} steamid - Steam ID of the user
   * @param {Number} appid - App instance ID
   * @param {String} format - Format of the data to return
   * @param {String} specificData - Specific data to return from the response
   * @returns {String} - JSON, XML, or VDF data
   */
  async getUserStatsForGame({
    steamid,
    appid,
    format = "json",
    specificData,
    useCache = true,
  }) {
    const hashKey = `${steamid}-${appid}-${format}-${specificData}`;
    const endpoint = `/ISteamUserStats/GetUserStatsForGame/v0002/`;
    const query = `?appid=${appid}&key=${this.key}&steamid=${steamid}&format=${format}`;
    const url = `${CallSteamAPI.#baseURL}` + endpoint + query;

    try {
      if (!useCache) {
        return handleEndpointOrFormat(
          format,
          url,
          "getUserStatsForGame",
          specificData
        );
      }
      return this.#cachedFetch(
        hashKey,
        format,
        url,
        "getUserStatsForGame",
        specificData
      );
    } catch (error) {
      console.error(
        `There was a problem instantiating the Steam Web API Library object: \n\n${error}\n`
      );
      return null;
    }
  }
  /**
   *
   * @param {Number} steamid - Steam ID of the user
   * @param {String} format - Format of the data to return
   * @param {String} specificData - Specific data to return from the response
   * @param {Boolean} includeAppInfo - Include app information in the response
   * @param {Boolean} includePlayedFreeGames - Include played free games in the response
   * @returns {String} - JSON, XML, or VDF data
   */
  async getOwnedGames({
    steamid,
    format = "json",
    specificData,
    includeAppInfo = true,
    includePlayedFreeGames = true,
    useCache = true,
  }) {
    const hashKey = `${steamid}-${format}-${specificData}-${includeAppInfo}-${includePlayedFreeGames}`;
    const includeAppInfoParam = includeAppInfo ? `&include_appinfo=true` : "";
    const includePlayedFreeGamesParam = includePlayedFreeGames
      ? `&include_played_free_games=true`
      : "";

    const endpoint = `/IPlayerService/GetOwnedGames/v0001/`;
    const query = `?key=${this.key}&steamid=${steamid}${includeAppInfoParam}${includePlayedFreeGamesParam}&format=${format}`;
    const url = `${CallSteamAPI.#baseURL}` + endpoint + query;

    try {
      if (!useCache) {
        return handleEndpointOrFormat(
          format,
          url,
          "getOwnedGames",
          specificData
        );
      }
      return this.#cachedFetch(
        hashKey,
        format,
        url,
        "getOwnedGames",
        specificData
      );
    } catch (error) {
      console.error(
        `There was a problem instantiating the Steam Web API Library object: \n\n${error}\n`
      );
      return null;
    }
  }
  /**
   *
   * @param {Number} steamid - Steam ID of the user
   * @param {String} format - Format of the data to return
   * @param {String} specificData - Specific data to return from the response
   * @param {Number} count - Number of games to retrieve
   * @returns {String} - JSON, XML, or VDF data
   */
  async getRecentlyPlayedGames({
    steamid,
    format,
    count = null,
    specificData,
    useCache = true,
  }) {
    const hashKey = `${steamid}-${format}-${count}-${specificData}`;
    const countParam = count ? `&count=${count}` : "";

    const endpoint = `/IPlayerService/GetRecentlyPlayedGames/v0001/`;
    const query = `?key=${this.key}&steamid=${steamid}${countParam}&format=${format}`;
    const url = `${CallSteamAPI.#baseURL}` + endpoint + query;

    try {
      if (!useCache) {
        return handleEndpointOrFormat(
          format,
          url,
          "getRecentlyPlayedGames",
          specificData
        );
      }
      return this.#cachedFetch(
        hashKey,
        format,
        url,
        "getRecentlyPlayedGames",
        specificData
      );
    } catch (error) {
      console.error(
        `There was a problem instantiating the Steam Web API Library object: \n\n${error}\n`
      );
      return null;
    }
  }
  async #cachedFetch(cacheKey, ...args) {
    const cacheResult = this.cache.has(cacheKey);
    if (this.cache.has(cacheKey) == true) {
      return this.cache.get(cacheKey);
    }
    const response = await handleEndpointOrFormat(...args);
    this.cache.set(cacheKey, response);
    return response;
  }
}

export default CallSteamAPI;