import {REST, Routes, Client, GatewayIntentBits, CommandInteraction, EmbedBuilder, CommandInteractionOption, CommandInteractionOptionResolver } from "discord.js"; import * as fs from "node:fs"; import commands from "./commands.js"; import {MakePrediction} from "./predictor.js"; import Election from "./election.js"; import Presidential_2024 from "./elections/2024.js"; import Elections from "./elections/elections.js"; const configraw = fs.readFileSync("config.json", "utf-8"); const config = JSON.parse(configraw); if (!config.token) { console.error("Please provide a Token and Client ID in config.json"); process.exit(1); } (async () => { const client = new Client({ intents: [GatewayIntentBits.Guilds] }); client.on('ready', async () => { console.log(`Logged in as ${client.user!.tag}!`); await publishSlashCommands(client.application!.id); }); client.on('interactionCreate', async i => { if (i instanceof CommandInteraction) { switch (i.commandName) { case "poll": const rcp_polls = JSON.parse(fs.readFileSync('data/rcp_polls.json', "utf-8")); let polls = ""; Object.keys(rcp_polls.approval.polls).forEach(poll => { polls += `:red_circle: **${rcp_polls.approval.polls[poll].pollster}** - (**${rcp_polls.approval.polls[poll].date}**), ${rcp_polls.approval.polls[poll].sample}, **${rcp_polls.approval.polls[poll].approve}**% approve, **${rcp_polls.approval.polls[poll].disapprove}**% disapprove (spread: **${rcp_polls.approval.polls[poll].spread}**)\n`; }); await i.reply(`Biden Approval Rating\n\n${polls}`); break; case "simulate": var electionname = (i.options as CommandInteractionOptionResolver).getSubcommand(); var election = structuredClone(Elections[electionname]); for (const candidate of election.candidates) { var option = (i.options as CommandInteractionOptionResolver).getString(candidate.party.toLowerCase().replace(/ /g, "_") + "_candidate"); if (option) { candidate.name = option; } var bias = (i.options as CommandInteractionOptionResolver).getNumber(candidate.party.toLowerCase().replace(/ /g, "_") + "_bias"); if (bias) { for (const state of Object.keys(election.states)) { election.states[state].odds[candidate.party] += bias; } } } var newcandidate = (i.options as CommandInteractionOptionResolver).getString("add_candidate"); if (newcandidate) { var party = (i.options as CommandInteractionOptionResolver).getString("with_party") || "Independent"; var color = (i.options as CommandInteractionOptionResolver).getString("with_color") || "#bfab22"; var odds = (i.options as CommandInteractionOptionResolver).getNumber("with_odds") || 0.33; if (!/^#[0-9A-Fa-f]{3,6}$/.test(color)) { await i.reply("Please provide a valid hex color code"); return; } if (election.candidates.some(c => c.party === party || c.name === newcandidate)) { await i.reply("A candidate with that name or party already exists"); return } election.candidates.push({ name: newcandidate, party: party, color: color }); for (const state of Object.keys(election.states)) election.states[state].odds[party] = odds; } var result = await MakePrediction(election); var embed = new EmbedBuilder() .setTitle(election.title) .setDescription(election.description.replace("$WINNER", result.winner)) .addFields(result.candidates.map(c => { return { name: `${(result.winner === c.name ? ":white_check_mark:" : "")} ${c.name} (${c.party})`, value: `${c.votes} Electoral Votes`, inline: true } })) .setImage("attachment://election.png") .setTimestamp(); await i.reply({embeds: [embed], files: [{attachment: result.png, name: "election.png"}]}); } } }); client.login(config.token); })(); async function publishSlashCommands(clientid : string) { const rest = new REST({ version: '10' }).setToken(config.token); await rest.put(Routes.applicationCommands(clientid), {body: commands}); console.log("Successfully registered slash commands"); }