From 4d5dc235d13e5366aff05a50c0a39209d34c9b2d Mon Sep 17 00:00:00 2001 From: dartz Date: Thu, 25 Jul 2024 18:12:51 -0400 Subject: [PATCH] add more election types; add image overlay to simulator (wip) --- src/commands.ts | 10 ++++++++++ src/index.ts | 17 ++++++++++++++--- src/predictor.ts | 29 ++++++++++++++++++++++++++++- 3 files changed, 52 insertions(+), 4 deletions(-) diff --git a/src/commands.ts b/src/commands.ts index 0ef6978..da268ec 100644 --- a/src/commands.ts +++ b/src/commands.ts @@ -84,6 +84,16 @@ const commands = [ description: 'The odds of the candidate to win in any state', 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 => [ { name: c.party.toLowerCase().replace(/ /g, "_") + "_candidate", diff --git a/src/index.ts b/src/index.ts index e2bc980..224f197 100644 --- a/src/index.ts +++ b/src/index.ts @@ -72,7 +72,7 @@ if (!config.token) { .setDescription("API test") .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})`, - 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 }))) .setTimestamp(); @@ -105,6 +105,7 @@ if (!config.token) { case "simulate": var electionname = (i.options as CommandInteractionOptionResolver).getSubcommand(); + var overlayimage = (i.options as CommandInteractionOptionResolver).getAttachment('image_overlay'); var election = structuredClone(Elections[electionname]); for (const candidate of election.candidates) { @@ -141,14 +142,24 @@ if (!config.token) { for (const state of Object.keys(election.states)) 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() .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: `${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 } })) diff --git a/src/predictor.ts b/src/predictor.ts index 28aa0f8..3ec6f10 100644 --- a/src/predictor.ts +++ b/src/predictor.ts @@ -5,7 +5,7 @@ import crypto from "crypto"; import Election from "./election.js"; import VoteType from './VoteType.js'; -export function MakePrediction(election : Election) : Promise { +export function MakePrediction(election : Election, overlay_image?: string) : Promise { return new Promise(async res => { const window = createSVGWindow(); registerWindow(window, window.document); @@ -41,6 +41,33 @@ export function MakePrediction(election : Election) : Promise { var s = sharp(Buffer.from(draw.svg())); 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) pred.winner = pred.candidates.sort((a : any, b : any) => b.electoralVotes - a.electoralVotes)[0].name; else if (election.voteType === VoteType.Popular)