Skip to content

Commit

Permalink
Automated daily summary announcements
Browse files Browse the repository at this point in the history
  • Loading branch information
MattIPv4 committed Nov 19, 2023
1 parent 3377cf6 commit d4b1f18
Show file tree
Hide file tree
Showing 11 changed files with 218 additions and 129 deletions.
93 changes: 6 additions & 87 deletions src/commands/causes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,76 +6,11 @@ import type { Command } from "workers-discord";

import getStats from "../util/stats";
import checkDate from "../util/check";
import { bold, italic, money, number } from "../util/format";
import getNow from "../util/now";
import { bold, italic, number } from "../util/format";
import causesBreakdown from "../util/causes";
import type { CtxWithEnv } from "../env";

const sluggify = (str: string) =>
str
// Remove anything that isn't a word character or space
.replace(/[^\w\s]+/g, "")
// Convert spaces to case changes for camel case
.replace(/\s+\S/g, (match) => match.trim().toUpperCase())
// Ensure first character is lowercase for camel case
.replace(/^./, (match) => match.toLowerCase());

const hash = (str: string) => {
let hash = 0;
for (let i = 0; i < str.length; i++) {
hash = (hash << 5) - hash + str.charCodeAt(i);
hash |= 0;
}
return hash;
};

const hearts = [
":blue_heart:",
":green_heart:",
":purple_heart:",
":yellow_heart:",
":heart:",
];

const heart = (str: string) => hearts[Math.abs(hash(str)) % hearts.length];

const data: Record<string, { name?: string; emote?: string }> = {
autistica: {
emote: "<:Autistica:1159097680753066035>",
},
campaignAgainstLivingMiserablyCALM: {
emote: "<:CALM:1159097678957920286>",
},
comicRelief: {
emote: "<:ComicRelief:1159097684750250044>",
},
coppafeel: {
emote: "<:CoppaFeel:1160890771273162832>",
},
galop: {
emote: "<:Galop:1160890850075742309>",
},
movember: {
emote: "<:Movember:1160891203986927627>",
},
helloWorld: {
emote: "<:HelloWorld:1160884733283147866>",
},
justdiggit: {
emote: "<:Justdiggit:1160889813461913632>",
},
royalNationalInstituteOfBlindPeopleRNIB: {
emote: "<:RNIB:1160891327635017820>",
},
warChild: {
emote: "<:WarChild:1159097686394425385>",
},
wallaceGromitsGrandAppeal: {
emote: "<:GrandAppeal:1160891082972868608>",
},
whaleAndDolphinConservation: {
emote: "<:WDC:1159097675304669214>",
},
};

const causesCommand: Command<CtxWithEnv> = {
name: "causes",
description:
Expand All @@ -88,17 +23,14 @@ const causesCommand: Command<CtxWithEnv> = {
// Check if Jingle Jam is running
const start = new Date(stats.event.start);
const check = checkDate(start);
// TODO: Re-enable this check once testing is done
// if (check) return edit({ content: check });
if (check) return edit({ content: check });

// Check if Jingle Jam has finished
const end = new Date(stats.event.end);
if (isNaN(+end)) throw new Error("Invalid end date");

// Time since launch
// TODO: Switch back to using the current time once testing is done
// const now = new Date();
const now = end;
const now = getNow();
const ended = now >= end;

await edit({
Expand All @@ -107,20 +39,7 @@ const causesCommand: Command<CtxWithEnv> = {
ended ? "supported" : "is supporting"
} ${bold(number(stats.causes.length))} amazing causes:`,
"",
...stats.causes.map((cause) => {
const slug = sluggify(cause.name);
const { name, emote } = data[slug] ?? {};

return bold(
`[${emote || heart(cause.name)} ${
name || cause.name
}](${cause.url}): ${money(
"£",
cause.raised.yogscast +
cause.raised.fundraisers,
)}`,
);
}),
causesBreakdown(stats),
"",
`:heart: Thank you for supporting some wonderful causes! ${
ended
Expand Down
8 changes: 3 additions & 5 deletions src/commands/stats.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import type { Command } from "workers-discord";

import getStats from "../util/stats";
import checkDate from "../util/check";
import getNow from "../util/now";
import { bold, date, italic, money, number } from "../util/format";
import type { CtxWithEnv } from "../env";

Expand All @@ -21,17 +22,14 @@ const statsCommand: Command<CtxWithEnv> = {
// Check if Jingle Jam is running
const start = new Date(stats.event.start);
const check = checkDate(start);
// TODO: Re-enable this check once testing is done
// if (check) return edit({ content: check });
if (check) return edit({ content: check });

// Check if Jingle Jam has finished
const end = new Date(stats.event.end);
if (isNaN(+end)) throw new Error("Invalid end date");

// Time since launch
// TODO: Switch back to using the current time once testing is done
// const now = new Date();
const now = end;
const now = getNow();
const ended = now >= end;
const timeSinceLaunch = Math.min(
now.getTime() - start.getTime(),
Expand Down
8 changes: 3 additions & 5 deletions src/commands/total.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import type { Command } from "workers-discord";

import getStats from "../util/stats";
import checkDate from "../util/check";
import getNow from "../util/now";
import { bold, italic, money, number } from "../util/format";
import type { CtxWithEnv } from "../env";

Expand All @@ -21,17 +22,14 @@ const totalCommand: Command<CtxWithEnv> = {
// Check if Jingle Jam is running
const start = new Date(stats.event.start);
const check = checkDate(start);
// TODO: Re-enable this check once testing is done
// if (check) return edit({ content: check });
if (check) return edit({ content: check });

// Check if Jingle Jam has finished
const end = new Date(stats.event.end);
if (isNaN(+end)) throw new Error("Invalid end date");

// Time since launch
// TODO: Switch back to using the current time once testing is done
// const now = new Date();
const now = end;
const now = getNow();
const ended = now >= end;

// Format some stats
Expand Down
2 changes: 1 addition & 1 deletion src/scheduled/milestone.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ const milestoneScheduled = async (
`# :tada: ${money("£", recentMilestone, false)}`,
"",
`Jingle Jam ${stats.event.year} just hit a new milestone, with ${totalRaised} raised so far through the Yogscast and fundraisers.`,
` There have already been ${collections} games collections claimed, and our ${countFundraisers} fundraisers have raised ${totalFundraisers}!`,
"",
`There have already been ${collections} games collections claimed, and our ${countFundraisers} fundraisers have raised ${totalFundraisers}!`,
":heart: Thank you for supporting some wonderful causes! Get involved and grab the collection at <https://jinglejam.tiltify.com>",
].join("\n");
ctx.waitUntil(
Expand Down
86 changes: 59 additions & 27 deletions src/scheduled/summary.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,25 @@
import getNow from "../util/now";
import checkDate from "../util/check";
import getStats from "../util/stats";
import sendWebhook from "../util/webhook";
import { bold, italic, money, number, timeSince } from "../util/format";
import causesBreakdown from "../util/causes";
import type { Env } from "../env";

// Aim to post at 23:00 UTC every day
const target = () => {
// TODO: Switch back to using the current time once testing is done
// const now = new Date();
const now = new Date("2023-12-02T23:15:00.000Z");
const now = getNow();
now.setUTCHours(23, 0, 0, 0);
return now;
};

// Check the end, but allow for posting a final summary within 12 hours of the end
const checkEnd = (end: Date) => {
const offset = new Date(end);
offset.setHours(offset.getHours() + 12);
return getNow() > offset;
};

const summaryScheduled = async (
event: ScheduledController,
env: Env,
Expand All @@ -25,46 +33,70 @@ const summaryScheduled = async (

// Get the target, and short-circuit if we're not there yet
const targetSummary = target();
// TODO: Switch back to using the current time once testing is done
// const now = new Date();
const now = new Date("2023-12-02T23:15:00.000Z");
console.log(now, targetSummary);
if (now.getTime() < targetSummary.getTime()) return;
const now = getNow();
if (now < targetSummary) return;

// Check when we last posted a summary, and don't post if it was after the current target
const lastSummary = new Date((await env.STORE.get("lastSummary")) || 0);
if (lastSummary.getTime() >= targetSummary.getTime()) return;
if (lastSummary >= targetSummary) return;

// Get the stats, and check if Jingle Jam is running
const stats = await getStats(env.STATS_API_ENDPOINT);
const start = new Date(stats.event.start);
// TODO: Re-enable this check once testing is done
// if (checkDate(start)) return;
if (checkDate(start)) return;

// Check the end, but less precisely, to allow for posting a final summary after the event
// Check the end, allowing for a final post after the end
const end = new Date(stats.event.end);
if (isNaN(+end)) throw new Error("Invalid end date");
end.setMinutes(end.getMinutes() + 23 * 60 + 59);
if (now.getTime() > end.getTime()) return;
if (checkEnd(end)) return;

// Format some stats
const daysSinceLaunch = Math.ceil(
Math.max((now.getTime() - start.getTime()) / 1000 / 60 / 60 / 24, 1),
);
const ended = now >= end;
const timeElapsed = italic(timeSince(start, ended ? end : now));
const timeRemaining = ended ? null : italic(timeSince(now, end));
const totalRaised = bold(
money("£", stats.raised.yogscast + stats.raised.fundraisers),
);
const collections = bold(number(stats.collections.redeemed));
const fundraisers = bold(number(stats.campaigns.count - 1));

// TODO: Send the webhooks, in the background, with errors logged to the console
// const content = [
// bold(`:snowflake: Jinlge Jam ${stats.event.year} Day ${daysSinceLaunch} Summary`),
// "",
// ":heart: Thank you for supporting some wonderful causes! Get involved at <https://jinglejam.tiltify.com>",
// ].join("\n");
// ctx.waitUntil(
// Promise.all(
// webhooks.map((webhook) =>
// sendWebhook(webhook, { content }).catch(console.error),
// ),
// ),
// );
// Send the webhooks, in the background, with errors logged to the console
const content = [
`# :snowflake: Jingle Jam ${stats.event.year} Day ${daysSinceLaunch} Summary`,
"",
`:money_with_wings: ${
ended ? "We" : "We've"
} raised a total of ${totalRaised} for charity over the last ${timeElapsed} during Jingle Jam ${
stats.event.year
}${ended ? "!" : " so far!"}`,
`:package: There ${
ended ? "were" : "have already been"
} ${collections} games collections redeemed, and ${fundraisers} fundraisers ${
ended ? "joined" : "have joined"
} to raise money for charity.`,
"",
causesBreakdown(stats),
"",
`:heart: Thank you for supporting some wonderful causes! ${
timeRemaining
? `\n:arrow_right: There ${
/^\D*1 /.test(timeRemaining) ? "is" : "are"
} still ${timeRemaining} remaining to get involved and grab the collection at <https://jinglejam.tiltify.com>`
: `We look forward to seeing you again for Jingle Jam ${
stats.event.year + 1
}.`
}`,
].join("\n");
ctx.waitUntil(
Promise.all(
webhooks.map((webhook) =>
sendWebhook(webhook, { content }).catch(console.error),
),
),
);

// Update the last summary we posted
await env.STORE.put("lastSummary", now.toISOString());
Expand Down
Loading

0 comments on commit d4b1f18

Please sign in to comment.