add more election types; add image overlay to simulator (wip)

This commit is contained in:
dartz 2024-07-25 18:12:51 -04:00
parent 1c82c1d5ca
commit 4d5dc235d1
3 changed files with 52 additions and 4 deletions

View file

@ -84,6 +84,16 @@ const commands = [
description: 'The odds of the candidate to win in any state', description: 'The odds of the candidate to win in any state',
type: ApplicationCommandOptionType.Number type: ApplicationCommandOptionType.Number
}, },
{
name: 'image_overlay',
description: 'Overlay an image',
type: ApplicationCommandOptionType.Attachment
},
{
name: 'image_opacity',
description: 'Image overlay opacity',
type: ApplicationCommandOptionType.Number
},
...Elections[e].candidates.flatMap(c => [ ...Elections[e].candidates.flatMap(c => [
{ {
name: c.party.toLowerCase().replace(/ /g, "_") + "_candidate", name: c.party.toLowerCase().replace(/ /g, "_") + "_candidate",

View file

@ -72,7 +72,7 @@ if (!config.token) {
.setDescription("API test") .setDescription("API test")
.addFields(Object.values(results[year][0].candidates).map((candidate: any) => ({ .addFields(Object.values(results[year][0].candidates).map((candidate: any) => ({
name: `${candidate.winner ? ":white_check_mark:" : ""} ${CandidateEmojis[candidate.party] ?? ""} ${candidate.name} ${candidate.incumbent ? "(I)" : ""} (${candidate.party})`, name: `${candidate.winner ? ":white_check_mark:" : ""} ${CandidateEmojis[candidate.party] ?? ""} ${candidate.name} ${candidate.incumbent ? "(I)" : ""} (${candidate.party})`,
value: `${results[year][0].election_type === "electoral" ? `${candidate.electoral_votes} electoral votes\n` : ""} ${candidate.votes.toLocaleString()} votes (${candidate.percent}%)`, value: `${results[year][0].election_type === "electoral" ? `${candidate.electoral_votes} electoral votes\n` : ""}${results[year][0].election_type === "parliament" ? `${candidate.seats_won} seats\n` : ""}${candidate.states_carried !== null && candidate.states_carried !== undefined ? `${candidate.states_carried} states carried\n` : ""}${candidate.delegates !== null && candidate.delegates !== undefined ? `${candidate.delegates} delegates\n` : ""}${candidate.votes !== null && candidate.votes !== undefined ? `${candidate.votes.toLocaleString()} votes (${candidate.percent}%)` : ""}`,
inline: true inline: true
}))) })))
.setTimestamp(); .setTimestamp();
@ -105,6 +105,7 @@ if (!config.token) {
case "simulate": case "simulate":
var electionname = (i.options as CommandInteractionOptionResolver).getSubcommand(); var electionname = (i.options as CommandInteractionOptionResolver).getSubcommand();
var overlayimage = (i.options as CommandInteractionOptionResolver).getAttachment('image_overlay');
var election = structuredClone(Elections[electionname]); var election = structuredClone(Elections[electionname]);
for (const candidate of election.candidates) { for (const candidate of election.candidates) {
@ -141,14 +142,24 @@ if (!config.token) {
for (const state of Object.keys(election.states)) for (const state of Object.keys(election.states))
election.states[state].odds[party] = odds; election.states[state].odds[party] = odds;
} }
var result = await MakePrediction(election); if (overlayimage !== null) {
if (overlayimage.size > 5000000) {
await i.editReply("Keep images under 5 MB please!");
return;
}
if (overlayimage.contentType?.substring(0, 5) != "image") {
await i.editReply("Invalid image!");
return;
}
}
var result = await MakePrediction(election, overlayimage?.url);
var embed = new EmbedBuilder() var embed = new EmbedBuilder()
.setTitle(election.title) .setTitle(election.title)
.setDescription(election.description.replace("$WINNER", result.winner)) .setDescription(election.description.replace("$WINNER", result.winner))
.addFields(result.candidates.map(c => { .addFields(result.candidates.map(c => {
return { return {
name: `${(result.winner === c.name ? ":white_check_mark:" : "")} ${c.name} (${c.party})`, name: `${(result.winner === c.name ? ":white_check_mark:" : "")} ${c.name} (${c.party})`,
value: `${election.voteType === VoteType.Electoral ? `${c.electoralVotes} electoral votes\n` : ""}${c.votes} votes (${((c.votes / result.totalVotes) * 100).toFixed(2)}%)`, value: `${election.voteType === VoteType.Electoral ? `${c.electoralVotes} electoral votes\n` : ""}${c.votes.toLocaleString()} votes (${((c.votes / result.totalVotes) * 100).toFixed(2)}%)`,
inline: true inline: true
} }
})) }))

View file

@ -5,7 +5,7 @@ import crypto from "crypto";
import Election from "./election.js"; import Election from "./election.js";
import VoteType from './VoteType.js'; import VoteType from './VoteType.js';
export function MakePrediction(election : Election) : Promise<Prediction> { export function MakePrediction(election : Election, overlay_image?: string) : Promise<Prediction> {
return new Promise(async res => { return new Promise(async res => {
const window = createSVGWindow(); const window = createSVGWindow();
registerWindow(window, window.document); registerWindow(window, window.document);
@ -41,6 +41,33 @@ export function MakePrediction(election : Election) : Promise<Prediction> {
var s = sharp(Buffer.from(draw.svg())); var s = sharp(Buffer.from(draw.svg()));
var png = await s.png().toBuffer(); var png = await s.png().toBuffer();
//overlay image
if (overlay_image !== undefined) {
const url = await fetch(overlay_image);
const arrayBuffer = await url.arrayBuffer();
const overlayBuffer = Buffer.from(arrayBuffer);
const overlay = await sharp(overlayBuffer)
.composite([{
input: Buffer.from([0, 0, 0, 255]),
raw: {
width: 1,
height: 1,
channels: 4,
},
tile: true,
blend: 'dest-in',
}])
.png()
.toBuffer();
png = await sharp(png)
.composite([{ input: overlay, blend: 'over' }])
.png()
.toBuffer();
}
if (election.voteType === VoteType.Electoral) if (election.voteType === VoteType.Electoral)
pred.winner = pred.candidates.sort((a : any, b : any) => b.electoralVotes - a.electoralVotes)[0].name; pred.winner = pred.candidates.sort((a : any, b : any) => b.electoralVotes - a.electoralVotes)[0].name;
else if (election.voteType === VoteType.Popular) else if (election.voteType === VoteType.Popular)