219 lines
6.9 KiB
JavaScript
219 lines
6.9 KiB
JavaScript
// backend/retroClient.js
|
|
|
|
'use strict';
|
|
|
|
const {
|
|
buildAuthorization,
|
|
getUserProfile,
|
|
getUserRecentAchievements,
|
|
getGame
|
|
} = require('@retroachievements/api');
|
|
|
|
class RetroClient {
|
|
constructor() {
|
|
const RA_USER = process.env.RA_USER;
|
|
const RA_KEY = process.env.RA_KEY;
|
|
|
|
if (!RA_USER || !RA_KEY) {
|
|
throw new Error('RetroAchievements username and API key must be set in .env file');
|
|
}
|
|
|
|
this.authorization = buildAuthorization({
|
|
username: RA_USER,
|
|
webApiKey: RA_KEY
|
|
});
|
|
|
|
console.log('🎮 RetroAchievements API client initialized');
|
|
}
|
|
|
|
// Helper method to build correct image URLs
|
|
buildImageUrl(imagePath, baseUrl) {
|
|
if (!imagePath) return null;
|
|
|
|
const toStr = (v) => (typeof v === 'string' ? v : String(v));
|
|
|
|
// If full URL provided, return as-is
|
|
if (/^https?:\/\//i.test(imagePath)) {
|
|
return imagePath;
|
|
}
|
|
|
|
const pathStr = toStr(imagePath);
|
|
|
|
// Handle full server-relative paths that start with /
|
|
if (pathStr.startsWith('/')) {
|
|
// Ensure .png for badges
|
|
if (pathStr.startsWith('/Badge/')) {
|
|
const name = pathStr.split('/').pop().replace(/\.(png|jpe?g|gif|webp)$/i, '');
|
|
return `https://media.retroachievements.org/Badge/${name}.png`;
|
|
}
|
|
return `https://media.retroachievements.org${pathStr}`;
|
|
}
|
|
|
|
// Normalize base URL (strip trailing slashes)
|
|
const normalizedBase = toStr(baseUrl || '').replace(/\/+$/, '');
|
|
|
|
// Special handling for badges - always ensure .png extension
|
|
const isBadgeContext = /\/Badge(\/|$)/i.test(normalizedBase);
|
|
if (isBadgeContext) {
|
|
const cleanImagePath = pathStr.replace(/\.(png|jpe?g|gif|webp)$/i, '');
|
|
return `${normalizedBase}/${cleanImagePath}.png`;
|
|
}
|
|
|
|
// For other images (UserPic, Images), build normally
|
|
return `${normalizedBase}/${pathStr}`;
|
|
}
|
|
|
|
async getUserProfile(username) {
|
|
try {
|
|
console.log(`📊 Fetching profile for user: ${username}`);
|
|
const profile = await getUserProfile(this.authorization, { username });
|
|
return profile;
|
|
} catch (error) {
|
|
console.error(`❌ Error fetching user profile for ${username}:`, error.message);
|
|
throw new Error(`Failed to fetch user profile: ${error.message}`);
|
|
}
|
|
}
|
|
|
|
async getUserRecentAchievements(username, count = 10) {
|
|
try {
|
|
console.log(`🏆 Fetching recent achievements for user: ${username}`);
|
|
const achievements = await getUserRecentAchievements(this.authorization, {
|
|
username,
|
|
count
|
|
});
|
|
return achievements;
|
|
} catch (error) {
|
|
console.error(`❌ Error fetching recent achievements for ${username}:`, error.message);
|
|
throw new Error(`Failed to fetch recent achievements: ${error.message}`);
|
|
}
|
|
}
|
|
|
|
async getGameInfo(gameId) {
|
|
try {
|
|
console.log(`🎮 Fetching game info for ID: ${gameId}`);
|
|
const game = await getGame(this.authorization, { gameId });
|
|
return game;
|
|
} catch (error) {
|
|
console.error(`❌ Error fetching game info for ID ${gameId}:`, error.message);
|
|
throw new Error(`Failed to fetch game info: ${error.message}`);
|
|
}
|
|
}
|
|
|
|
async getUserData(username) {
|
|
try {
|
|
const profile = await this.getUserProfile(username);
|
|
|
|
let lastGame = null;
|
|
let lastAchievement = null;
|
|
|
|
if (profile.lastGameId) {
|
|
try {
|
|
lastGame = await this.getGameInfo(profile.lastGameId);
|
|
} catch (gameError) {
|
|
console.warn(`⚠️ Could not fetch last game info: ${gameError.message}`);
|
|
}
|
|
}
|
|
|
|
try {
|
|
const recentAchievements = await this.getUserRecentAchievements(username, 1);
|
|
if (recentAchievements && recentAchievements.length > 0) {
|
|
lastAchievement = recentAchievements;
|
|
}
|
|
} catch (achievementError) {
|
|
console.warn(`⚠️ Could not fetch recent achievements: ${achievementError.message}`);
|
|
}
|
|
|
|
return {
|
|
user: {
|
|
username: profile.user,
|
|
displayName: profile.user,
|
|
avatar: this.buildImageUrl(
|
|
profile.userPic,
|
|
'https://media.retroachievements.org/UserPic'
|
|
),
|
|
motto: profile.motto || '',
|
|
totalPoints: profile.totalPoints || 0,
|
|
totalTruePoints: profile.totalTruePoints || 0,
|
|
memberSince: profile.memberSince || null,
|
|
rank: profile.rank || null
|
|
},
|
|
lastGame: lastGame
|
|
? {
|
|
id: lastGame.gameId,
|
|
title: lastGame.title,
|
|
icon: this.buildImageUrl(
|
|
lastGame.gameIcon,
|
|
'https://media.retroachievements.org/Images'
|
|
),
|
|
consoleName: lastGame.consoleName
|
|
}
|
|
: null,
|
|
lastAchievement: lastAchievement
|
|
? {
|
|
id: lastAchievement.achievementId,
|
|
title: lastAchievement.title,
|
|
description: lastAchievement.description,
|
|
points: lastAchievement.points,
|
|
trueRatio: lastAchievement.trueRatio,
|
|
gameTitle: lastAchievement.gameTitle,
|
|
gameIcon: this.buildImageUrl(
|
|
lastAchievement.gameIcon,
|
|
'https://media.retroachievements.org/Images'
|
|
),
|
|
badgeName: this.buildImageUrl(
|
|
lastAchievement.badgeName,
|
|
'https://media.retroachievements.org/Badge'
|
|
),
|
|
dateEarned: lastAchievement.dateEarned
|
|
}
|
|
: null
|
|
};
|
|
} catch (error) {
|
|
console.error(`❌ Error in getUserData for ${username}:`, error.message);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
async getOverlayData(username, count = 5) {
|
|
try {
|
|
const recentAchievements = await this.getUserRecentAchievements(username, count);
|
|
|
|
const formattedAchievements = recentAchievements.map((achievement) => {
|
|
// Debug log to see what we're getting from the API
|
|
console.log(
|
|
`🔍 Processing badge for achievement "${achievement.title}": badgeName="${achievement.badgeName}"`
|
|
);
|
|
|
|
return {
|
|
id: achievement.achievementId,
|
|
title: achievement.title,
|
|
description: achievement.description,
|
|
points: achievement.points,
|
|
trueRatio: achievement.trueRatio || achievement.points,
|
|
gameTitle: achievement.gameTitle,
|
|
gameIcon: this.buildImageUrl(
|
|
achievement.gameIcon,
|
|
'https://media.retroachievements.org/Images'
|
|
),
|
|
badgeName: this.buildImageUrl(
|
|
achievement.badgeName,
|
|
'https://media.retroachievements.org/Badge'
|
|
),
|
|
dateEarned: achievement.dateEarned
|
|
};
|
|
});
|
|
|
|
return {
|
|
username,
|
|
recentAchievements: formattedAchievements,
|
|
lastUpdated: new Date().toISOString()
|
|
};
|
|
} catch (error) {
|
|
console.error(`❌ Error in getOverlayData for ${username}:`, error.message);
|
|
throw error;
|
|
}
|
|
}
|
|
}
|
|
|
|
module.exports = new RetroClient();
|