const { ApplicationCommandType, EmbedBuilder, AttachmentBuilder } = require("discord.js"); const specificActivityManager = require('../../../Database/DB_Interactions/specificActivity') const monitorChannels = require('../../../Credentials/afkChannels') const credentialManager = require('../../../Credentials/Config') const { createCanvas, loadImage } = require("canvas"); const { Chart } = require("chart.js/auto"); const fs = require('fs'); module.exports = { name: "activity", options: [ { name: "user", // Option names must be lowercase type: 6, description: "The user you want to view the activity for.", required: false }, ], type: ApplicationCommandType.ChatInput, description: "get activity information", run: async (client, interaction) => { const user = await interaction.options.getUser('user'); if (!user) { // Didn't specify a user const getData = await specificActivityManager.getSpecificData(interaction.user.id) const data = getData[0].dailyActivity[0].dayToDayActivity const activityData = getData[0] if (data.length < 2) { const notEnoughData = new EmbedBuilder() .setColor(credentialManager.embeds.colour) .setDescription("You do not have enough data to display!") return interaction.reply({ embeds: [notEnoughData], ephemeral: true }) } else { await interaction.deferReply(); // Create canvas element const width = 900; const height = 600; const canvas = createCanvas(width, height); const ctx = canvas.getContext('2d'); // Custom Background Plugin const customBackgroundColorPlugin = { id: 'customBackgroundColor', beforeDraw: (chart) => { const ctx = chart.ctx; ctx.save(); ctx.fillStyle = 'white'; // Set the background color to white ctx.fillRect(0, 0, chart.width, chart.height); ctx.restore(); } }; // Extract the date labels and data for each category from the data array const dates = data.map(entry => new Date(entry.date).toLocaleDateString('en-GB', { day: '2-digit', month: 'short' })); const stageChannelTimeData = data.map(entry => entry.stageChannelTime); const normalMessagesData = data.map(entry => entry.normalMessages); const specialMessagesData = data.map(entry => entry.specialMessages); const reactionsData = data.map(entry => entry.reactions); const voiceChannelTimeData = data.map(entry => entry.voiceChannelTime); // Create the chart instance const chartInstance = new Chart(ctx, { type: 'line', data: { labels: dates, datasets: [ { label: 'Normal Messages', data: normalMessagesData, fill: false, borderColor: 'blue', backgroundColor: 'blue', borderWidth: 5, pointStyle: false, }, { label: 'Special Messages', data: specialMessagesData, fill: false, borderColor: 'green', backgroundColor: 'green', borderWidth: 5, pointStyle: false, }, { label: 'Reactions', data: reactionsData, fill: false, borderColor: 'orange', backgroundColor: 'orange', borderWidth: 5, pointStyle: false, }, { label: 'Voice Channel Time', data: voiceChannelTimeData, fill: false, borderColor: 'red', backgroundColor: 'red', borderWidth: 5, pointStyle: false, }, { label: 'Stage Channel Time', data: stageChannelTimeData, fill: false, borderColor: 'purple', backgroundColor: 'purple', borderWidth: 5, pointStyle: false, }, ], }, options: { layout: { padding: { left: 50, // Padding on the left side of the chart right: 50, // Padding on the right side of the chart top: 40, // Padding on the top side of the chart bottom: 60, // Padding on the bottom side of the chart }, }, scales: { x: { ticks: { padding: 10, font: { size: 18, }, }, grid: { color: ['black', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent'], drawTicks: false, }, }, y: { ticks: { font: { size: 18, }, padding: 10, }, beginAtZero: true, grid: { color: ['black', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent'], drawTicks: false, }, }, }, elements: { line: { tension: 0.25 // Set the line tension to 0.5 for a smoother curve } }, plugins: { customBackgroundColor: { // Add the custom background color plugin to the options enabled: true, // Enable the plugin }, legend: { display: true, // Display the legend labels: { boxWidth: 25, // Adjust this value to increase or decrease the width of the legend boxHeight: 15, padding: 20, } }, }, }, plugins: [customBackgroundColorPlugin], // Add the custom plugin to the chart }); // Convert the chart to a buffer const buffer = canvas.toBuffer(); await fs.promises.writeFile(`${interaction.user.username}_activity_chart.png`, buffer); let afk = true; for (const element of data) { if ( element.normalMessages > 5 || element.specialMessages > 0 || element.reactions > 10 || element.voiceChannelTime > 120 || element.stageChannelTime > 30 ) { afk = false; break; } } const profilePicture = `https://cdn.discordapp.com/avatars/` + interaction.member.user.id + `/` + interaction.member.user.avatar + `.webp?size=24` const username = interaction.member.displayName const unixEpochTime = new Date(data[data.length - 1].lastUpdated).getTime(); const file = new AttachmentBuilder(`${interaction.user.username}_activity_chart.png`); const activityEmbed = new EmbedBuilder() .setColor(credentialManager.embeds.colour) .setAuthor({ name: username, iconURL: profilePicture }) .setTitle("Recent Activity Report") .setDescription(`AFK \`${afk ? "Yes" : "No"}\`\nLast Activity `) .addFields( { name: 'Daily', value: `Normal \`${activityData.dailyActivity[0].normalMessages}\`\nSpecial \`${activityData.dailyActivity[0].specialMessages}\`\nReactions \`${activityData.dailyActivity[0].reactions}\`\nVoice \`${activityData.dailyActivity[0].voiceChannelTime}m\`\nStage \`${activityData.dailyActivity[0].stageChannelTime}m\``, inline: true }, { name: 'Weekly', value: `Normal \`${activityData.weeklyActivity[0].normalMessages}\`\nSpecial \`${activityData.weeklyActivity[0].specialMessages}\`\nReactions \`${activityData.weeklyActivity[0].reactions}\`\nVoice \`${activityData.weeklyActivity[0].voiceChannelTime}m\`\nStage \`${activityData.weeklyActivity[0].stageChannelTime}m\``, inline: true }, { name: 'Past 6 Months', value: `Normal \`${activityData.lifetimeActivity[0].normalMessages}\`\nSpecial \`${activityData.lifetimeActivity[0].specialMessages}\`\nReactions \`${activityData.lifetimeActivity[0].reactions}\`\nVoice \`${activityData.lifetimeActivity[0].voiceChannelTime}m\`\nStage \`${activityData.lifetimeActivity[0].stageChannelTime}m\``, inline: true }, ) .setImage(`attachment://${interaction.user.username}_activity_chart.png`) .setTimestamp() await interaction.editReply({ embeds: [activityEmbed], files: [file] }); // Move the file fs.renameSync(`${interaction.user.username}_activity_chart.png`, `./Src/Graphs/${interaction.user.username}_activity_chart.png`); } } else { if (!interaction.member.roles.cache.has(monitorChannels.moderatorRoleId)) { const modEmbed = new EmbedBuilder() .setColor(credentialManager.embeds.colour) .setDescription("Only moderators can specify a user, please run the regular command!") return interaction.reply({ embeds: [modEmbed], ephemeral: true }) } // Specified a user that was themselves if (user.id === interaction.user.id) { // Didn't specify a user const getData = await specificActivityManager.getSpecificData(interaction.user.id) const data = getData[0].dailyActivity[0].dayToDayActivity const activityData = getData[0] if (data.length < 2) { const notEnoughData = new EmbedBuilder() .setColor(credentialManager.embeds.colour) .setDescription("You do not have enough data to display!") return interaction.reply({ embeds: [notEnoughData], ephemeral: true }) } else { await interaction.deferReply(); // Create canvas element const width = 900; const height = 600; const canvas = createCanvas(width, height); const ctx = canvas.getContext('2d'); // Custom Background Plugin const customBackgroundColorPlugin = { id: 'customBackgroundColor', beforeDraw: (chart) => { const ctx = chart.ctx; ctx.save(); ctx.fillStyle = 'white'; // Set the background color to white ctx.fillRect(0, 0, chart.width, chart.height); ctx.restore(); } }; // Extract the date labels and data for each category from the data array const dates = data.map(entry => new Date(entry.date).toLocaleDateString('en-GB', { day: '2-digit', month: 'short' })); const stageChannelTimeData = data.map(entry => entry.stageChannelTime); const normalMessagesData = data.map(entry => entry.normalMessages); const specialMessagesData = data.map(entry => entry.specialMessages); const reactionsData = data.map(entry => entry.reactions); const voiceChannelTimeData = data.map(entry => entry.voiceChannelTime); const chartInstance = new Chart(ctx, { type: 'line', data: { labels: dates, datasets: [ { label: 'Normal Messages', data: normalMessagesData, fill: false, borderColor: 'blue', backgroundColor: 'blue', borderWidth: 5, pointStyle: false, }, { label: 'Special Messages', data: specialMessagesData, fill: false, borderColor: 'green', backgroundColor: 'green', borderWidth: 5, pointStyle: false, }, { label: 'Reactions', data: reactionsData, fill: false, borderColor: 'orange', backgroundColor: 'orange', borderWidth: 5, pointStyle: false, }, { label: 'Voice Channel Time', data: voiceChannelTimeData, fill: false, borderColor: 'red', backgroundColor: 'red', borderWidth: 5, pointStyle: false, }, { label: 'Stage Channel Time', data: stageChannelTimeData, fill: false, borderColor: 'purple', backgroundColor: 'purple', borderWidth: 5, pointStyle: false, }, ], }, options: { layout: { padding: { left: 50, // Padding on the left side of the chart right: 50, // Padding on the right side of the chart top: 40, // Padding on the top side of the chart bottom: 60, // Padding on the bottom side of the chart }, }, scales: { x: { ticks: { padding: 10, font: { size: 18, }, }, grid: { color: ['black', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent'], drawTicks: false, }, }, y: { ticks: { font: { size: 18, }, padding: 10, }, beginAtZero: true, grid: { color: ['black', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent'], drawTicks: false, }, }, }, elements: { line: { tension: 0.25 // Set the line tension to 0.5 for a smoother curve } }, plugins: { customBackgroundColor: { // Add the custom background color plugin to the options enabled: true, // Enable the plugin }, legend: { display: true, // Display the legend labels: { boxWidth: 25, // Adjust this value to increase or decrease the width of the legend boxHeight: 15, padding: 20, } }, }, }, plugins: [customBackgroundColorPlugin], // Add the custom plugin to the chart }); // Convert the chart to a buffer const buffer = canvas.toBuffer(); await fs.promises.writeFile(`${interaction.user.username}_activity_chart.png`, buffer); let afk = true; for (const element of data) { if ( element.normalMessages > 5 || element.specialMessages > 0 || element.reactions > 10 || element.voiceChannelTime > 120 || element.stageChannelTime > 30 ) { afk = false; break; } } const profilePicture = `https://cdn.discordapp.com/avatars/` + interaction.member.user.id + `/` + interaction.member.user.avatar + `.webp?size=24` const username = interaction.member.displayName const unixEpochTime = new Date(data[data.length - 1].lastUpdated).getTime(); const file = new AttachmentBuilder(`${interaction.user.username}_activity_chart.png`); const activityEmbed = new EmbedBuilder() .setColor(credentialManager.embeds.colour) .setAuthor({ name: username, iconURL: profilePicture }) .setTitle("Recent Activity Report") .setDescription(`AFK \`${afk ? "Yes" : "No"}\`\nLast Activity `) .addFields( { name: 'Daily', value: `Normal \`${activityData.dailyActivity[0].normalMessages}\`\nSpecial \`${activityData.dailyActivity[0].specialMessages}\`\nReactions \`${activityData.dailyActivity[0].reactions}\`\nVoice \`${activityData.dailyActivity[0].voiceChannelTime}m\`\nStage \`${activityData.dailyActivity[0].stageChannelTime}m\``, inline: true }, { name: 'Weekly', value: `Normal \`${activityData.weeklyActivity[0].normalMessages}\`\nSpecial \`${activityData.weeklyActivity[0].specialMessages}\`\nReactions \`${activityData.weeklyActivity[0].reactions}\`\nVoice \`${activityData.weeklyActivity[0].voiceChannelTime}m\`\nStage \`${activityData.weeklyActivity[0].stageChannelTime}m\``, inline: true }, { name: 'Past 6 Months', value: `Normal \`${activityData.lifetimeActivity[0].normalMessages}\`\nSpecial \`${activityData.lifetimeActivity[0].specialMessages}\`\nReactions \`${activityData.lifetimeActivity[0].reactions}\`\nVoice \`${activityData.lifetimeActivity[0].voiceChannelTime}m\`\nStage \`${activityData.lifetimeActivity[0].stageChannelTime}m\``, inline: true }, ) .setImage(`attachment://${interaction.user.username}_activity_chart.png`) .setTimestamp() await interaction.editReply({ embeds: [activityEmbed], files: [file] }); // Move the file fs.renameSync(`${interaction.user.username}_activity_chart.png`, `./Src/Graphs/${interaction.user.username}_activity_chart.png`); } } else { // Specified a user const getData = await specificActivityManager.getSpecificData(user.id) const data = getData[0].dailyActivity[0].dayToDayActivity const activityData = getData[0] if (data.length < 2) { const notEnoughData = new EmbedBuilder() .setColor(credentialManager.embeds.colour) .setDescription("This user not have enough data to display!") return interaction.reply({ embeds: [notEnoughData], ephemeral: true }) } await interaction.deferReply(); // Create canvas element const width = 900; const height = 600; const canvas = createCanvas(width, height); const ctx = canvas.getContext('2d'); // Custom Background Plugin const customBackgroundColorPlugin = { id: 'customBackgroundColor', beforeDraw: (chart) => { const ctx = chart.ctx; ctx.save(); ctx.fillStyle = 'white'; // Set the background color to white ctx.fillRect(0, 0, chart.width, chart.height); ctx.restore(); } }; // Extract the date labels and data for each category from the data array const dates = data.map(entry => new Date(entry.date).toLocaleDateString('en-GB', { day: '2-digit', month: 'short' })); const stageChannelTimeData = data.map(entry => entry.stageChannelTime); const normalMessagesData = data.map(entry => entry.normalMessages); const specialMessagesData = data.map(entry => entry.specialMessages); const reactionsData = data.map(entry => entry.reactions); const voiceChannelTimeData = data.map(entry => entry.voiceChannelTime); const chartInstance = new Chart(ctx, { type: 'line', data: { labels: dates, datasets: [ { label: 'Normal Messages', data: normalMessagesData, fill: false, borderColor: 'blue', backgroundColor: 'blue', borderWidth: 5, pointStyle: false, }, { label: 'Special Messages', data: specialMessagesData, fill: false, borderColor: 'green', backgroundColor: 'green', borderWidth: 5, pointStyle: false, }, { label: 'Reactions', data: reactionsData, fill: false, borderColor: 'orange', backgroundColor: 'orange', borderWidth: 5, pointStyle: false, }, { label: 'Voice Channel Time', data: voiceChannelTimeData, fill: false, borderColor: 'red', backgroundColor: 'red', borderWidth: 5, pointStyle: false, }, { label: 'Stage Channel Time', data: stageChannelTimeData, fill: false, borderColor: 'purple', backgroundColor: 'purple', borderWidth: 5, pointStyle: false, }, ], }, options: { layout: { padding: { left: 50, // Padding on the left side of the chart right: 50, // Padding on the right side of the chart top: 40, // Padding on the top side of the chart bottom: 60, // Padding on the bottom side of the chart }, }, scales: { x: { ticks: { padding: 10, font: { size: 18, }, }, grid: { color: ['black', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent'], drawTicks: false, }, }, y: { ticks: { font: { size: 18, }, padding: 10, }, beginAtZero: true, grid: { color: ['black', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'transparent'], drawTicks: false, }, }, }, elements: { line: { tension: 0.25 // Set the line tension to 0.5 for a smoother curve } }, plugins: { customBackgroundColor: { // Add the custom background color plugin to the options enabled: true, // Enable the plugin }, legend: { display: true, // Display the legend labels: { boxWidth: 25, // Adjust this value to increase or decrease the width of the legend boxHeight: 15, padding: 20, } }, }, }, plugins: [customBackgroundColorPlugin], // Add the custom plugin to the chart }); // Convert the chart to a buffer const buffer = canvas.toBuffer(); await fs.promises.writeFile(`${user.username}_activity_chart.png`, buffer); let afk = true; for (const element of data) { if ( element.normalMessages > 5 || element.specialMessages > 0 || element.reactions > 10 || element.voiceChannelTime > 120 || element.stageChannelTime > 30 ) { afk = false; break; } } const profilePicture = `https://cdn.discordapp.com/avatars/` + user.id + `/` + user.avatar + `.webp?size=24` const username = user.username const unixEpochTime = new Date(data[data.length - 1].lastUpdated).getTime(); const file = new AttachmentBuilder(`${user.username}_activity_chart.png`); const activityEmbed = new EmbedBuilder() .setColor(credentialManager.embeds.colour) .setAuthor({ name: username, iconURL: profilePicture }) .setTitle("Recent Activity Report") .setDescription(`AFK \`${afk ? "Yes" : "No"}\`\nLast Activity `) .addFields( { name: 'Daily', value: `Normal \`${activityData.dailyActivity[0].normalMessages}\`\nSpecial \`${activityData.dailyActivity[0].specialMessages}\`\nReactions \`${activityData.dailyActivity[0].reactions}\`\nVoice \`${activityData.dailyActivity[0].voiceChannelTime}m\`\nStage \`${activityData.dailyActivity[0].stageChannelTime}m\``, inline: true }, { name: 'Weekly', value: `Normal \`${activityData.weeklyActivity[0].normalMessages}\`\nSpecial \`${activityData.weeklyActivity[0].specialMessages}\`\nReactions \`${activityData.weeklyActivity[0].reactions}\`\nVoice \`${activityData.weeklyActivity[0].voiceChannelTime}m\`\nStage \`${activityData.weeklyActivity[0].stageChannelTime}m\``, inline: true }, { name: 'Past 6 Months', value: `Normal \`${activityData.lifetimeActivity[0].normalMessages}\`\nSpecial \`${activityData.lifetimeActivity[0].specialMessages}\`\nReactions \`${activityData.lifetimeActivity[0].reactions}\`\nVoice \`${activityData.lifetimeActivity[0].voiceChannelTime}m\`\nStage \`${activityData.lifetimeActivity[0].stageChannelTime}m\``, inline: true }, ) .setImage(`attachment://${user.username}_activity_chart.png`) .setTimestamp() await interaction.editReply({ embeds: [activityEmbed], files: [file] }); // Move the file fs.renameSync(`${user.username}_activity_chart.png`, `./Src/Graphs/${user.username}_activity_chart.png`); } } } }