Features: - Add /tournaments command with status and game filters - Add tournament API integration to PlayerService - Add beautiful tournament embed builders with prize pools - Protect sync-games from overwriting manual customizations - Improve error handling with proper MessageFlags Changes: - Add getTournamentsData() and getTournamentData() to PlayerService - Add tournament command handler with pagination support - Add tournament embed builders with rich formatting - Update deployment docs for v1.2.10 features - Add issue tracker documentation - Add tournament testing script - Fix ephemeral flags throughout CommandHandler - Improve permission checks with PermissionFlagsBits Version: 1.2.11
171 lines
5.0 KiB
JavaScript
171 lines
5.0 KiB
JavaScript
// src/services/PlayerService.js
|
|
const axios = require('axios');
|
|
|
|
class PlayerService {
|
|
constructor() {
|
|
this.baseUrl = 'https://www.vrbattles.gg';
|
|
}
|
|
|
|
async findUserByUsername(username) {
|
|
try {
|
|
console.log(`Fetching data for username: ${username}`);
|
|
const url = `${this.baseUrl}/api/get_player_data_by_username/${encodeURIComponent(username)}`;
|
|
console.log(`API URL: ${url}`);
|
|
|
|
const response = await axios.get(url, {
|
|
timeout: 5000
|
|
});
|
|
|
|
console.log('API Response:', JSON.stringify(response.data, null, 2));
|
|
|
|
if (response.data && response.data.success) {
|
|
// Parse player_data if it's a string
|
|
if (typeof response.data.player_data === 'string') {
|
|
try {
|
|
response.data.player_data = JSON.parse(response.data.player_data);
|
|
} catch (parseError) {
|
|
console.error('Error parsing player_data:', parseError);
|
|
}
|
|
}
|
|
}
|
|
|
|
return response.data;
|
|
} catch (error) {
|
|
console.error('Error fetching user data:', {
|
|
message: error.message,
|
|
response: error.response?.data,
|
|
status: error.response?.status
|
|
});
|
|
return null;
|
|
}
|
|
}
|
|
|
|
getBadgeImageUrl(badgeName) {
|
|
if (!badgeName) {
|
|
return `${this.baseUrl}/assets/images/badges/xp_badges/A/BADGE_A1_72p.png`;
|
|
}
|
|
|
|
badgeName = badgeName.toUpperCase().replace(/ /g, '_');
|
|
let badgeUrl = `${this.baseUrl}/assets/images/badges/xp_badges/`;
|
|
|
|
if (badgeName.startsWith('A')) {
|
|
badgeUrl += 'A/';
|
|
} else if (badgeName.startsWith('B')) {
|
|
badgeUrl += 'B/';
|
|
} else if (badgeName.startsWith('C')) {
|
|
badgeUrl += 'C/';
|
|
} else if (badgeName.startsWith('D')) {
|
|
badgeUrl += 'D/';
|
|
} else if (badgeName.startsWith('E')) {
|
|
badgeUrl += 'E/';
|
|
} else if (badgeName.startsWith('F')) {
|
|
badgeUrl += 'F/';
|
|
} else {
|
|
badgeUrl += 'A/'; // Default to A tier
|
|
}
|
|
|
|
badgeUrl += `BADGE_${badgeName}_72p.png`;
|
|
return badgeUrl;
|
|
}
|
|
|
|
getProfileUrl(username) {
|
|
return `${this.baseUrl}/profile/${username}`;
|
|
}
|
|
|
|
getStatsUrl(username) {
|
|
return `${this.baseUrl}/profile/${username}/stats`;
|
|
}
|
|
|
|
async getTournamentsData() {
|
|
try {
|
|
console.log('Fetching tournaments data...');
|
|
const url = `${this.baseUrl}/api/fetch/tournaments`;
|
|
console.log(`API URL: ${url}`);
|
|
|
|
const response = await axios.get(url, {
|
|
timeout: 5000,
|
|
headers: {
|
|
'Accept': 'application/json',
|
|
'Content-Type': 'application/json'
|
|
},
|
|
validateStatus: function (status) {
|
|
return status >= 200 && status < 300;
|
|
}
|
|
});
|
|
|
|
console.log('Tournaments API Response:', JSON.stringify(response.data, null, 2));
|
|
return response.data;
|
|
} catch (error) {
|
|
console.error('Error fetching tournaments data:', {
|
|
message: error.message,
|
|
response: error.response?.data,
|
|
status: error.response?.status
|
|
});
|
|
return { success: false, error: 'Failed to fetch tournaments data' };
|
|
}
|
|
}
|
|
|
|
async getTournamentData(tournamentId) {
|
|
const url = `${this.baseUrl}/api/get_tournament_data/${tournamentId}`;
|
|
const response = await axios.get(url);
|
|
return response.data;
|
|
}
|
|
|
|
async findTeamByName(teamName, gameFilter) {
|
|
try {
|
|
// Double-check sanitization here as well for defense in depth
|
|
if (!teamName || typeof teamName !== 'string') {
|
|
throw new Error('Invalid team name provided');
|
|
}
|
|
|
|
// Additional sanitization at the service level
|
|
const sanitizedTeamName = teamName
|
|
.replace(/[^a-zA-Z0-9\s\-_.]/g, '')
|
|
.trim()
|
|
.slice(0, 100);
|
|
|
|
if (!sanitizedTeamName) {
|
|
throw new Error('Invalid team name after sanitization');
|
|
}
|
|
|
|
// Use URL encoding for the query parameters
|
|
const encodedTeamName = encodeURIComponent(sanitizedTeamName);
|
|
const url = `${this.baseUrl}/api/get_team_data_by_name/${encodedTeamName}`;
|
|
|
|
const response = await axios.get(url, {
|
|
timeout: 5000, // 5 second timeout
|
|
headers: {
|
|
'Accept': 'application/json',
|
|
'Content-Type': 'application/json'
|
|
},
|
|
validateStatus: function (status) {
|
|
return status >= 200 && status < 300; // Only accept success status codes
|
|
}
|
|
});
|
|
|
|
// Validate response structure
|
|
if (!response.data || typeof response.data !== 'object') {
|
|
throw new Error('Invalid response format from API');
|
|
}
|
|
|
|
// If game filter is provided, filter the teams
|
|
if (gameFilter && response.data.teams) {
|
|
response.data.teams = response.data.teams.filter(
|
|
team => team.game_name === gameFilter
|
|
);
|
|
}
|
|
|
|
return response.data;
|
|
} catch (error) {
|
|
this.logger.error('Error in findTeamByName:', {
|
|
error: error.message,
|
|
teamName,
|
|
gameFilter,
|
|
timestamp: new Date().toISOString()
|
|
});
|
|
return { success: false, error: 'Failed to fetch team data' };
|
|
}
|
|
}
|
|
}
|
|
|
|
module.exports = PlayerService; |