Add budget spending chart command
This commit is contained in:
@@ -1,6 +1,16 @@
|
||||
const { SlashCommandBuilder, EmbedBuilder } = require('discord.js');
|
||||
const { SlashCommandBuilder, EmbedBuilder, AttachmentBuilder } = require('discord.js');
|
||||
const axios = require('axios').default;
|
||||
const { HOUSEHOLD_IDS } = require('../../core/constants.js');
|
||||
|
||||
const CHART_COLORS = {
|
||||
[HOUSEHOLD_IDS.MINZ]: '#4e79a7',
|
||||
[HOUSEHOLD_IDS.MIFFY]: '#f28e2b',
|
||||
};
|
||||
const DISPLAY_NAMES = {
|
||||
[HOUSEHOLD_IDS.MINZ]: 'Minz',
|
||||
[HOUSEHOLD_IDS.MIFFY]: 'Miffy',
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
category: 'utility',
|
||||
global: true,
|
||||
@@ -43,6 +53,15 @@ module.exports = {
|
||||
option.setName('user')
|
||||
.setRequired(false)
|
||||
.setDescription('User to view stats for'))
|
||||
)
|
||||
.addSubcommand(subcommand =>
|
||||
subcommand
|
||||
.setName('chart')
|
||||
.setDescription('Render a bar chart comparing Minz and Miffy spendings over time')
|
||||
.addIntegerOption(option =>
|
||||
option.setName('weeks')
|
||||
.setRequired(false)
|
||||
.setDescription('Number of recent weeks to include (default 8)'))
|
||||
),
|
||||
async execute(interaction) {
|
||||
let db = await interaction.client.localDB;
|
||||
@@ -159,6 +178,70 @@ module.exports = {
|
||||
|
||||
await interaction.reply({ embeds: [statsEmbed] });
|
||||
break;
|
||||
case 'chart': {
|
||||
const weeks = Math.min(Math.max(interaction.options.getInteger('weeks') ?? 8, 1), 52);
|
||||
|
||||
const chartPeriods = db.prepare(`
|
||||
SELECT id, start_date, end_date
|
||||
FROM weekly_budgets
|
||||
ORDER BY start_date DESC
|
||||
LIMIT ?
|
||||
`).all(weeks).reverse();
|
||||
|
||||
if (chartPeriods.length === 0) {
|
||||
await interaction.reply("No budget periods on record.");
|
||||
return;
|
||||
}
|
||||
|
||||
await interaction.deferReply();
|
||||
|
||||
const spentByUserAndPeriod = db.prepare(`
|
||||
SELECT COALESCE(SUM(amount), 0) as total
|
||||
FROM grocery_spendings
|
||||
WHERE budget_id = ? AND discord_id = ?
|
||||
`);
|
||||
|
||||
const labels = chartPeriods.map(period => period.start_date.slice(5));
|
||||
const datasets = [HOUSEHOLD_IDS.MINZ, HOUSEHOLD_IDS.MIFFY].map(userId => ({
|
||||
label: DISPLAY_NAMES[userId],
|
||||
backgroundColor: CHART_COLORS[userId],
|
||||
data: chartPeriods.map(period => spentByUserAndPeriod.get(period.id, userId).total),
|
||||
}));
|
||||
|
||||
const chartConfig = {
|
||||
type: 'bar',
|
||||
data: { labels, datasets },
|
||||
options: {
|
||||
title: { display: true, text: 'Weekly Grocery Spending (€)' },
|
||||
scales: {
|
||||
yAxes: [{ ticks: { beginAtZero: true }, scaleLabel: { display: true, labelString: '€' } }],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await axios.post('https://quickchart.io/chart', {
|
||||
chart: chartConfig,
|
||||
width: 800,
|
||||
height: 400,
|
||||
backgroundColor: 'white',
|
||||
}, { responseType: 'arraybuffer' });
|
||||
|
||||
const attachment = new AttachmentBuilder(Buffer.from(response.data), { name: 'budget-chart.png' });
|
||||
const chartEmbed = new EmbedBuilder()
|
||||
.setColor(0x0099ff)
|
||||
.setTitle('Weekly Grocery Spending')
|
||||
.setDescription(`Last ${chartPeriods.length} week(s): \`${chartPeriods[0].start_date}\` to \`${chartPeriods[chartPeriods.length - 1].end_date}\``)
|
||||
.setImage('attachment://budget-chart.png')
|
||||
.setTimestamp();
|
||||
|
||||
await interaction.editReply({ embeds: [chartEmbed], files: [attachment] });
|
||||
} catch (error) {
|
||||
console.error('[budget chart] Failed to render chart:', error);
|
||||
await interaction.editReply("Failed to render the chart, sorry!");
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
await interaction.reply(`Sub mismatch. Switching on ${interaction.options.getSubcommand()}`);
|
||||
break;
|
||||
|
||||
Reference in New Issue
Block a user