diff --git a/src/commands.ts b/src/commands.ts index e17a5c9..7c2caa3 100644 --- a/src/commands.ts +++ b/src/commands.ts @@ -207,6 +207,17 @@ const commands = [ }) }, ], + }, + { + name: 'upcoming', + description: "View upcoming things", + options: [ + { + name: 'elections', + description: 'View upcoming elections', + type: ApplicationCommandOptionType.Subcommand + } + ], } ]; diff --git a/src/index.ts b/src/index.ts index e8c243f..1e151cf 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,4 @@ -import {REST, Routes, Client, Guild, GatewayIntentBits, CommandInteraction, EmbedBuilder, CommandInteractionOption, CommandInteractionOptionResolver } from "discord.js"; +import {ActionRowBuilder, ButtonBuilder, ButtonStyle, REST, Routes, Client, Guild, GatewayIntentBits, CommandInteraction, EmbedBuilder, CommandInteractionOption, CommandInteractionOptionResolver } from "discord.js"; import * as fs from "node:fs"; import commands from "./commands.js"; import {MakePrediction} from "./predictor.js"; @@ -285,6 +285,76 @@ if (!config.token) { .setImage("attachment://election.png") .setTimestamp(); await i.editReply({embeds: [embed], files: [{attachment: result.png, name: "election.png"}]}); + + case "upcoming": + console.log('\x1b[33m', `Upcoming elections command executed by ${i.user.username} (${i.user.id})`, '\x1b[0m'); + function parseDate(dateStr: string) { + return new Date(Date.parse(dateStr + " 2024")); + } + + const elections = JSON.parse(fs.readFileSync(`data/upcoming_elections.json`, "utf-8")); + const currentDate = new Date(); + + const electionsArray = Object.keys(elections).map(key => ({ + name: key, + date: parseDate(elections[key].date), + dateString: elections[key].date + })).filter(election => election.date >= currentDate); + + electionsArray.sort((a, b) => a.date.getTime() - b.date.getTime()); + + function generatePageEmbed(electionsArray: { name: string; date: Date; dateString: string }[], page: number): EmbedBuilder { + return new EmbedBuilder() + .setTitle("Upcoming elections") + .setFooter({ text: `Page ${page + 1}/${Math.ceil(electionsArray.length / 6)}`}) + .setDescription( + electionsArray.slice(page * 6, page * 6 + 6) + .map((election) => `:green_circle: ${election.dateString} - **${election.name}**`) + .join('\n') + ) + .setTimestamp(); + } + + const maxPage = Math.ceil(electionsArray.length / 6) - 1; + let currentPage = 0; + + const pageButtons = new ActionRowBuilder().addComponents( + new ButtonBuilder().setCustomId('electionsPrev').setLabel('⏮️').setStyle(ButtonStyle.Primary), + new ButtonBuilder().setCustomId('electionsNext').setLabel('⏭️').setStyle(ButtonStyle.Primary) + ); + + const message = await i.editReply({ + embeds: [generatePageEmbed(electionsArray, currentPage)], + components: [pageButtons] + }); + + const buttonCollector = message.createMessageComponentCollector({ + filter: i => i.user.id === i.user.id, + time: 1000 * 60 * 2 + }); + + buttonCollector.on('collect', async (i) => { + switch (i.customId) { + case 'electionsPrev': + currentPage = currentPage === 0 ? maxPage : currentPage - 1; + break; + case 'electionsNext': + currentPage = currentPage === maxPage ? 0 : currentPage + 1; + break; + } + + await i.update({embeds: [generatePageEmbed(electionsArray, currentPage)], components: [pageButtons]}); + }); + + buttonCollector.on('end', async () => { + try { + await message.edit({ components: [] }); + } catch (e) { + // this seems to fail in DMs for some reason...? + console.log('\x1b[31m', 'Could not delete buttons in DM!', '\x1b[0m'); + } + }); + break; } } });