achievements-app / psn-api

A JavaScript library that lets you get trophy, user, and game data from the PlayStation Network.
https://psn-api.achievements.app
MIT License
263 stars 31 forks source link

A LOT of games don't seem to contain trophy information. #147

Closed Octorious closed 1 year ago

Octorious commented 1 year ago

Hey, I'm trying to create a system that takes a username, and then gathers the percentage of trophies earned for each of the titles in their trophy list. However, the vast majority of titles print the error message that 'Resource was not found'. This is my first time using the API and I am quite new to coding so may have misunderstood some of the documentation, but I'd really appreciate some help with this. I just don't understand why some games work perfectly and then some just don't. Here's my code:

const PSN = require("psn-api");

async function fetchPlayedGames() {
  const { exchangeNpssoForCode, exchangeCodeForAccessToken, getUserTitles, getTitleTrophies, getUserTrophiesEarnedForTitle, makeUniversalSearch } = PSN;

  const myNpsso = "INSERT HERE";
  const accessCode = await exchangeNpssoForCode(myNpsso);
  const authorization = await exchangeCodeForAccessToken(accessCode);

  const auth = { accessToken: authorization.accessToken };

  const allAccountsSearchResults = await makeUniversalSearch(auth, "INSERT ANY USERNAME HERE", "SocialAllAccounts");
  const targetAccountId = allAccountsSearchResults.domainResponses[0].results[0].socialMetadata.accountId;

  const { trophyTitles } = await getUserTitles(auth, targetAccountId);

  for (const title of trophyTitles) {
    try {
      const { trophies: titleTrophies } = await getTitleTrophies(auth, title.npCommunicationId, "all");
      const { trophies: earnedTrophies } = await getUserTrophiesEarnedForTitle(auth, targetAccountId, title.npCommunicationId, "all");

      // Merge the two trophy lists
      const mergedTrophies = mergeTrophyLists(titleTrophies, earnedTrophies);

      // Filter only base game trophies (you might need to adjust the groupId)
      const baseGameTrophies = mergedTrophies;

      const totalTrophies = baseGameTrophies.length;
      const earnedTrophiesCount = baseGameTrophies.filter(t => t.isEarned).length;
      const completionPercentage = (earnedTrophiesCount / totalTrophies) * 100;

      console.log(`Game: ${title.trophyTitleName}`);
      console.log(`Platform: ${title.trophyTitlePlatform}`);
      console.log(`Trophy Completion: ${completionPercentage.toFixed(2)}%`);
      console.log('-----------------------------');
    } catch (error) {
      console.error(`Error fetching trophies for ${title.trophyTitleName}: ${error.message}`);
      console.log('-----------------------------');
      continue; // Skip to the next title
    }
  }
}

// Merge and normalize trophy lists
const mergeTrophyLists = (titleTrophies, earnedTrophies) => {
  const mergedTrophies = [];

  for (const earnedTrophy of earnedTrophies) {
    const foundTitleTrophy = titleTrophies.find(t => t.trophyId === earnedTrophy.trophyId);
    mergedTrophies.push({ ...earnedTrophy, ...foundTitleTrophy, isEarned: earnedTrophy.earned ?? false });
  }

  return mergedTrophies;
};

fetchPlayedGames();
Octorious commented 1 year ago

Found the problem. Didn't realise PS5 was treated differently to other platforms so added a platform check and it now does everything conditionally:

const PSN = require("psn-api");

async function fetchPlayedGames() {
  const { exchangeNpssoForCode, exchangeCodeForAccessToken, getUserTitles, getTitleTrophies, getUserTrophiesEarnedForTitle, makeUniversalSearch } = PSN;

  const myNpsso = "";
  const accessCode = await exchangeNpssoForCode(myNpsso);
  const authorization = await exchangeCodeForAccessToken(accessCode);

  const auth = { accessToken: authorization.accessToken };

  const allAccountsSearchResults = await makeUniversalSearch(auth, "name", "SocialAllAccounts");
  const targetAccountId = allAccountsSearchResults.domainResponses[0].results[0].socialMetadata.accountId;

  const { trophyTitles } = await getUserTitles(auth, targetAccountId);

  for (const title of trophyTitles) {
    try {
      // Determine the platform for the title (e.g., "PS3", "PS4", "PS Vita", "PS5")
      const platform = title.trophyTitlePlatform;

      // Set npServiceName based on the platform
      const npServiceNameOption = platform === "PS5" ? {} : { npServiceName: "trophy" };

      const { trophies: titleTrophies } = await getTitleTrophies(auth, title.npCommunicationId, "all", npServiceNameOption);
      const { trophies: earnedTrophies } = await getUserTrophiesEarnedForTitle(auth, targetAccountId, title.npCommunicationId, "all", npServiceNameOption);     

      // Merge the two trophy lists
      const mergedTrophies = mergeTrophyLists(titleTrophies, earnedTrophies);

      // Filter only base game trophies (you might need to adjust the groupId)
      const baseGameTrophies = mergedTrophies; // Adjust this line if needed

      const totalTrophies = baseGameTrophies.length;
      const earnedTrophiesCount = baseGameTrophies.filter(t => t.isEarned).length;
      const completionPercentage = (earnedTrophiesCount / totalTrophies) * 100;

      console.log(`Game: ${title.trophyTitleName}`);
      console.log(`Platform: ${title.trophyTitlePlatform}`);
      console.log(`Trophy Completion: ${completionPercentage.toFixed(2)}%`);
      console.log('-----------------------------');
    } catch (error) {
      console.error(`Error fetching trophies for ${title.trophyTitleName}: ${error.message}`);
      console.log('-----------------------------');
      continue; // Skip to the next title
    }
  }
}

// Merge and normalize trophy lists
const mergeTrophyLists = (titleTrophies, earnedTrophies) => {
  const mergedTrophies = [];

  for (const earnedTrophy of earnedTrophies) {
    const foundTitleTrophy = titleTrophies.find(t => t.trophyId === earnedTrophy.trophyId);
    mergedTrophies.push({ ...earnedTrophy, ...foundTitleTrophy, isEarned: earnedTrophy.earned ?? false });
  }

  return mergedTrophies;
};

fetchPlayedGames();