diff --git a/commands/100-games/chartgamegenres.js b/commands/100-games/chartgamegenres.js new file mode 100644 index 0000000..f5c1057 --- /dev/null +++ b/commands/100-games/chartgamegenres.js @@ -0,0 +1,187 @@ +const { createCanvas } = require('canvas'); +const { Chart } = require('chart.js/auto'); +const fs = require('fs'); +const { getUserRegistration, getBeatenGames, checkGameStorageId } = require('../../databaseHelperFunctions.js'); +const { getGameJson, getGenres } = require('../../igdbHelperFunctions.js'); +const { SlashCommandBuilder, EmbedBuilder } = require('discord.js'); + +module.exports = { + data: new SlashCommandBuilder() + .setName('chartgamegenres') + .setDescription('Generate a bar chart of the different genres of the games you have beat') + .addUserOption(option => option.setName('user').setDescription('The user to check')) + .addIntegerOption(option => option.setName('year').setDescription('The year to check').addChoices({ name: '2024', value: 2024 }, { name: '2025', value: 2025 })) + .addBooleanOption(option => option.setName('ignoreadventure').setDescription('Exclude the Adventure genre from the bar chart')), + async execute(interaction) { + + await interaction.deferReply(); + + let user = interaction.user; + const userOption = interaction.options.getUser('user'); + const yearOption = interaction.options.getInteger('year'); + const ignoreadventure = interaction.options.getBoolean('ignoreadventure'); + + if (userOption) { + user = userOption; + } + + const userDatabaseEntry = await getUserRegistration(user); + if (!userDatabaseEntry) return interaction.editReply({ content: `Issue checking registration with "${user.username}".`, ephemeral: true }); + + let beatenGamesDatabaseEntries; + + if (yearOption) { + beatenGamesDatabaseEntries = await getBeatenGames(userDatabaseEntry.id); + + if (beatenGamesDatabaseEntries && beatenGamesDatabaseEntries.length > 0) { + beatenGamesDatabaseEntries = await beatenGamesDatabaseEntries.filter(entry => { + const date = new Date(entry.updatedAt); + return date.getFullYear() === yearOption; + }); + } + } + else { + beatenGamesDatabaseEntries = await getBeatenGames(userDatabaseEntry.id); + } + + if (!beatenGamesDatabaseEntries || beatenGamesDatabaseEntries.length == 0) { + const embed = new EmbedBuilder() + .setTitle(`${user.username}'s beat games age`) + .setDescription(`${user.username} has not beat any games`) + .setColor(0xFF0000); + return interaction.editReply({ embeds: [embed] }); + } + + const beatGameIGDBEntries = []; + + for (let i = 0; i < beatenGamesDatabaseEntries.length; i++) { + const game = await checkGameStorageId(beatenGamesDatabaseEntries[i].gameId); + const json = await getGameJson(String.prototype.concat('where id = ', game.igdb_id, '; fields *;')); + beatGameIGDBEntries.push(json[0]); + } + + const genres = []; + const counts = []; + for (let i = 0; i < beatGameIGDBEntries.length; i++) { + if (beatGameIGDBEntries[i].genres) { + for (let j = 0; j < beatGameIGDBEntries[i].genres.length; j++) { + const genre = await getGenres(beatGameIGDBEntries[i].genres[j]); + genres.push(genre); + } + } + } + + genres.forEach(item => { + counts[item] = (counts[item] || 0) + 1; + }); + + const sortedCounts = Object.entries(counts).sort((a, b) => b[1] - a[1]); + + const keys = Object.keys(sortedCounts); + + const labels = []; + const values = []; + + for (let i = 0; i < keys.length; i++) { + const genre = keys[i]; + + if (ignoreadventure && sortedCounts[genre][0] == 'Adventure') + { + continue; + } + + labels.push(sortedCounts[genre][0]); + values.push(sortedCounts[genre][1]); + } + + // Create a canvas + const canvas = createCanvas(1920, 1080); + + // Chart data + const data = { + labels: labels, + datasets: [ + { + label: 'Game Genres', + data: values, + borderColor: '#5865F2', + backgroundColor: 'rgba(88, 101, 242, 0.5)', + borderWidth: 8, + }, + ], + }; + + const config = { + type: 'bar', + data: data, + options: { + scales: { + x: { + title: { + display: true, + text: 'Genres', + font: { + size: 48, + family: 'Tahoma', + }, + color: 'white', + }, + grid: { + color: 'rgba(255, 255, 255, 0.5)', + lineWidth: 0, + }, + ticks: { + color: 'white', + font: { + size: 24, + family: 'Tahoma', + }, + }, + }, + y: { + beginAtZero: true, + title: { + display: true, + text: 'Occurrences of genre', + font: { + size: 48, + family: 'Tahoma', + }, + color: 'white', + }, + grid: { + color: 'rgba(255, 255, 255, 0.5)', + lineWidth: 2, + }, + ticks: { + stepSize: 1, + }, + }, + }, + plugins: { + title: { + display: true, + text: `${user.username}'s most common game genres`, + font: { + size: 64, + family: 'Tahoma', + }, + color: 'white', + }, + }, + }, + }; + + // Create the chart + const chart = new Chart(canvas, config); + + // Save the chart as an image + const buffer = canvas.toBuffer('image/png'); + fs.writeFileSync('./tempbeattimeline.png', buffer); + + // Use the image in your embed + return interaction.editReply({ + files: ['./tempbeattimeline.png'], + }); + }, +}; \ No newline at end of file