Skip to content

Commit f47145a

Browse files
authored
Update messages to pull emoji from env (#5)
* Update messages to pull emoji from env * Document new environment variable
1 parent 39a55d0 commit f47145a

File tree

11 files changed

+117
-88
lines changed

11 files changed

+117
-88
lines changed

.dev.vars.example

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ DISCORD_CLIENT_SECRET=<discord_client_secret>
55
# DISCORD_GUILD_ID=<discord_guild_id>
66
# DISCORD_SUMMARY_CHANNEL=<discord_channel_id>
77
# DISCORD_MILESTONE_CHANNEL=<discord_channel_id>
8-
# DISCORD_CAUSES_EMOJI=<discord_causes_emoji_object>
8+
# DISCORD_CAUSES_EMOJI={"Wallace & Gromit's Grand Appeal":"", ...}
9+
# DISCORD_REGULAR_EMOJI={"mascot":"","happy":"","hype":"","love":"","sad":""}
910
# WORKER_BASE_URL=<worker_base_url>
1011

1112
STATS_API_ENDPOINT=https://dashboard.jinglejam.co.uk/api/tiltify

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
2. Create your `.dev.vars` file.
99
- Copy `.dev.vars.example` and fill out the information from your Discord application, plus the ID of your test server/guild where you'll use the bot.
1010
- Optionally, `DISCORD_SUMMARY_CHANNEL` + `DISCORD_MILESTONE_CHANNEL` can be set to channel IDs for testing the scheduled messages, as well as the `DISCORD_BOT_TOKEN` to send the messages as.
11-
- Optionally, `DISCORD_CAUSES_EMOJI` can be set to a JSON object mapping the cause names to custom emoji Markdown (which can be [uploaded directly to the Discord application](https://discord.com/developers/docs/resources/emoji#emoji-object-applicationowned-emoji)).
11+
- Optionally, `DISCORD_CAUSES_EMOJI` + `DISCORD_REGULAR_EMOJI` can be set to a JSON object mapping the cause names + regular emoji (see [`const regular` in `src/util/emoji.ts`](src/util//emoji.ts)) to custom emoji Markdown (which can be [uploaded directly to the Discord application](https://discord.com/developers/docs/resources/emoji#emoji-object-applicationowned-emoji)).
1212
3. Authenticate with Wrangler by running `npx wrangler login`.
1313
4. Update `wrangler.toml` for your account.
1414
- Use `npx wrangler whoami` to get your account ID, update the value in `wrangler.toml` to match.
@@ -26,7 +26,7 @@ Ensure that the environment in `wrangler.toml` has been updated with your chosen
2626

2727
Ensure that the KV namespaces are created for staging/production environments and are configured in `wrangler.toml`. Use `npx wrangler kv:namespace create "STORE" -e <staging/production>`.
2828

29-
You'll also want to set `DISCORD_CLIENT_ID` + `DISCORD_PUBLIC_KEY` + `STATS_API_ENDPOINT` (optionally, `DISCORD_SUMMARY_CHANNEL` + `DISCORD_MILESTONE_CHANNEL` + `DISCORD_BOT_TOKEN` + `DISCORD_CAUSES_EMOJI` + `WORKER_BASE_URL`) as secrets for the worker, which you can do with `npx wrangler secret put <var name> -e <staging/production>` (the channel secrets can contain multiple IDs, separated by a comma).
29+
You'll also want to set `DISCORD_CLIENT_ID` + `DISCORD_PUBLIC_KEY` + `STATS_API_ENDPOINT` (optionally, `DISCORD_SUMMARY_CHANNEL` + `DISCORD_MILESTONE_CHANNEL` + `DISCORD_BOT_TOKEN` + `DISCORD_CAUSES_EMOJI` + `DISCORD_REGULAR_EMOJI` + `WORKER_BASE_URL`) as secrets for the worker, which you can do with `npx wrangler secret put <var name> -e <staging/production>` (the channel secrets can contain multiple IDs, separated by a comma).
3030

3131
If you're deploying for local, make sure that you've got the appropriate environment variables set for `DISCORD_CLIENT_ID`, `DISCORD_CLIENT_SECRET` + `DISCORD_GUILD_ID` (otherwise, they'll default to the values in `.dev.vars`).
3232

src/commands/causes.ts

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@ import getStats from "../util/stats";
88
import { error, loading, notStarted, thanks } from "../util/messages";
99
import getNow from "../util/now";
1010
import { bold, number } from "../util/format";
11-
import causesBreakdown, { parseEmoji } from "../util/causes";
11+
import causesBreakdown from "../util/causes";
1212
import type { CtxWithEnv } from "../env";
13+
import { emojiRegular } from "../util/emoji";
1314

1415
const causesCommand: Command<CtxWithEnv> = {
1516
name: "causes",
@@ -22,7 +23,7 @@ const causesCommand: Command<CtxWithEnv> = {
2223

2324
// Check if Jingle Jam is running
2425
const start = new Date(stats.event.start);
25-
const check = notStarted(start);
26+
const check = notStarted(start, context.env);
2627
if (check) return edit({ content: check });
2728

2829
// Check if Jingle Jam has finished
@@ -32,30 +33,27 @@ const causesCommand: Command<CtxWithEnv> = {
3233

3334
await edit({
3435
content: [
35-
`<:JingleJammy:1047503567981903894> Jingle Jam ${
36+
`${emojiRegular(context.env, "mascot")} Jingle Jam ${
3637
stats.event.year
3738
} ${ended ? "supported" : "is supporting"} ${bold(
3839
number(stats.causes.length),
3940
)} amazing causes:`,
4041
"",
41-
causesBreakdown(
42-
stats,
43-
parseEmoji(context.env.DISCORD_CAUSES_EMOJI),
44-
),
42+
causesBreakdown(stats, context.env),
4543
"",
46-
thanks(end, stats.event.year),
44+
thanks(end, stats.event.year, context.env),
4745
].join("\n"),
4846
});
4947
})().catch(async (err) => {
5048
console.error(err);
51-
await edit({ content: error() });
49+
await edit({ content: error(context.env) });
5250
}),
5351
);
5452

5553
return response({
5654
type: InteractionResponseType.ChannelMessageWithSource,
5755
data: {
58-
content: loading(),
56+
content: loading(context.env),
5957
flags: MessageFlags.Ephemeral,
6058
},
6159
});

src/commands/stats.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { error, loading, notStarted, thanks } from "../util/messages";
99
import getNow from "../util/now";
1010
import { bold, italic, money, number, timeSince } from "../util/format";
1111
import type { CtxWithEnv } from "../env";
12+
import { emojiRegular } from "../util/emoji";
1213

1314
const statsCommand: Command<CtxWithEnv> = {
1415
name: "stats",
@@ -21,7 +22,7 @@ const statsCommand: Command<CtxWithEnv> = {
2122

2223
// Check if Jingle Jam is running
2324
const start = new Date(stats.event.start);
24-
const check = notStarted(start);
25+
const check = notStarted(start, context.env);
2526
if (check) return edit({ content: check });
2627

2728
// Check if Jingle Jam has finished
@@ -111,7 +112,7 @@ const statsCommand: Command<CtxWithEnv> = {
111112
await edit({
112113
content: [
113114
bold(
114-
`<:JingleJammy:1047503567981903894> Jingle Jam ${stats.event.year} Stats`,
115+
`${emojiRegular(context.env, "mascot")} Jingle Jam ${stats.event.year} Stats`,
115116
),
116117
"",
117118
`:money_with_wings: ${
@@ -126,7 +127,7 @@ const statsCommand: Command<CtxWithEnv> = {
126127
ended ? " this year" : ""
127128
}, supporting the ${countCauses} amazing causes.`,
128129
"",
129-
`<:Jammy_HAPPY:1047503540475674634> This year, ${collections} Games Collections ${
130+
`${emojiRegular(context.env, "happy")} This year, ${collections} Games Collections ${
130131
ended ? "were" : "have already been"
131132
} redeemed, with the average donation being ${average}.`,
132133
`:black_small_square: That works out to an average of ${perHourCollections} collections claimed per hour, or ${perDayCollections} collections per day.`,
@@ -135,7 +136,7 @@ const statsCommand: Command<CtxWithEnv> = {
135136
`:scroll: Over the past ${historyYears} years, plus this year, we've raised a total of ${historyRaised} for charity!`,
136137
`:black_small_square: Since ${historyOldest}, we've received ${historyDonations} charitable donations as part of Jingle Jam.`,
137138
"",
138-
thanks(end, stats.event.year),
139+
thanks(end, stats.event.year, context.env),
139140
"",
140141
`:chart_with_upwards_trend: ${italic(
141142
"Explore more stats at <https://jinglejam.co.uk/tracker>",
@@ -144,14 +145,14 @@ const statsCommand: Command<CtxWithEnv> = {
144145
});
145146
})().catch(async (err) => {
146147
console.error(err);
147-
await edit({ content: error() });
148+
await edit({ content: error(context.env) });
148149
}),
149150
);
150151

151152
return response({
152153
type: InteractionResponseType.ChannelMessageWithSource,
153154
data: {
154-
content: loading(),
155+
content: loading(context.env),
155156
flags: MessageFlags.Ephemeral,
156157
},
157158
});

src/commands/total.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { error, loading, notStarted, thanks } from "../util/messages";
99
import getNow from "../util/now";
1010
import { bold, money, number } from "../util/format";
1111
import type { CtxWithEnv } from "../env";
12+
import { emojiRegular } from "../util/emoji";
1213

1314
const totalCommand: Command<CtxWithEnv> = {
1415
name: "total",
@@ -21,7 +22,7 @@ const totalCommand: Command<CtxWithEnv> = {
2122

2223
// Check if Jingle Jam is running
2324
const start = new Date(stats.event.start);
24-
const check = notStarted(start);
25+
const check = notStarted(start, context.env);
2526
if (check) return edit({ content: check });
2627

2728
// Check if Jingle Jam has finished
@@ -51,28 +52,28 @@ const totalCommand: Command<CtxWithEnv> = {
5152

5253
await edit({
5354
content: [
54-
`<:JingleJammy:1047503567981903894> ${totalRaised} ${
55+
`${emojiRegular(context.env, "mascot")} ${totalRaised} ${
5556
ended ? "was" : "has been"
5657
} raised for charity during Jingle Jam ${
5758
stats.event.year
5859
}${ended ? "!" : " so far!"} `,
59-
`<:Jammy_HAPPY:1047503540475674634> ${collections} Games Collections ${
60+
`${emojiRegular(context.env, "happy")} ${collections} Games Collections ${
6061
ended ? "were" : "have been"
6162
} redeemed, and over all the years, we've now raised ${historyRaised}!`,
6263
"",
63-
thanks(end, stats.event.year),
64+
thanks(end, stats.event.year, context.env),
6465
].join("\n"),
6566
});
6667
})().catch(async (err) => {
6768
console.error(err);
68-
await edit({ content: error() });
69+
await edit({ content: error(context.env) });
6970
}),
7071
);
7172

7273
return response({
7374
type: InteractionResponseType.ChannelMessageWithSource,
7475
data: {
75-
content: loading(),
76+
content: loading(context.env),
7677
flags: MessageFlags.Ephemeral,
7778
},
7879
});

src/env.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export interface Env {
66
DISCORD_SUMMARY_CHANNEL?: string;
77
DISCORD_MILESTONE_CHANNEL?: string;
88
DISCORD_CAUSES_EMOJI?: string;
9+
DISCORD_REGULAR_EMOJI?: string;
910
WORKER_BASE_URL?: string;
1011
STORE: KVNamespace;
1112
}

src/scheduled/milestone.ts

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { bold, money, number } from "../util/format";
33
import sendMessage from "../util/send";
44
import { thanks } from "../util/messages";
55
import type { Env } from "../env";
6+
import { emojiRegular } from "../util/emoji";
67

78
const milestones = [
89
100_000, 500_000, 1_000_000, 1_500_000, 2_000_000, 2_500_000, 3_000_000,
@@ -51,16 +52,12 @@ const milestoneScheduled = async (
5152

5253
// Send the messages, in the background, with errors logged to the console
5354
const content = [
54-
`# <:Jammy_HYPE:1047503542212108360> ${money(
55-
"£",
56-
recentMilestone,
57-
false,
58-
)}`,
55+
`# ${emojiRegular(env, "hype")} ${money("£", recentMilestone, false)}`,
5956
"",
60-
`<:JingleJammy:1047503567981903894> Jingle Jam ${stats.event.year} just hit a new milestone, with ${totalRaised} raised so far through the Yogscast and fundraisers.`,
57+
`${emojiRegular(env, "mascot")} Jingle Jam ${stats.event.year} just hit a new milestone, with ${totalRaised} raised so far through the Yogscast and fundraisers.`,
6158
`:black_small_square: There have already been ${collections} Games Collections claimed, and our ${countFundraisers} fundraisers have raised ${totalFundraisers}!`,
6259
"",
63-
thanks(new Date(stats.event.end), stats.event.year),
60+
thanks(new Date(stats.event.end), stats.event.year, env),
6461
].join("\n");
6562
ctx.waitUntil(
6663
Promise.all(

src/scheduled/summary.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@ import { notStarted, thanks } from "../util/messages";
33
import getStats from "../util/stats";
44
import sendMessage from "../util/send";
55
import { bold, italic, money, number, timeSince } from "../util/format";
6-
import causesBreakdown, { parseEmoji } from "../util/causes";
6+
import causesBreakdown from "../util/causes";
77
import type { Env } from "../env";
8+
import { emojiRegular } from "../util/emoji";
89

910
// Aim to post at 23:00 UTC every day
1011
const target = () => {
@@ -47,7 +48,7 @@ const summaryScheduled = async (
4748
// Get the stats, and check if Jingle Jam is running
4849
const stats = await getStats(env.STATS_API_ENDPOINT);
4950
const start = new Date(stats.event.start);
50-
if (notStarted(start)) return;
51+
if (notStarted(start, env)) return;
5152

5253
// Check the end, allowing for a final post after the end
5354
const end = new Date(stats.event.end);
@@ -68,9 +69,9 @@ const summaryScheduled = async (
6869

6970
// Send the webhooks, in the background, with errors logged to the console
7071
const content = [
71-
`# <:JingleJammy:1047503567981903894> Jingle Jam ${stats.event.year} Day ${daysSinceLaunch} Summary`,
72+
`# ${emojiRegular(env, "mascot")} Jingle Jam ${stats.event.year} Day ${daysSinceLaunch} Summary`,
7273
"",
73-
`<:Jammy_HAPPY:1047503540475674634> ${
74+
`${emojiRegular(env, "happy")} ${
7475
ended ? "We" : "We've"
7576
} raised a total of ${totalRaised} for charity over the ${timeElapsed} of Jingle Jam ${
7677
stats.event.year
@@ -81,9 +82,9 @@ const summaryScheduled = async (
8182
ended ? "joined" : "have joined"
8283
} to raise money for charity.`,
8384
"",
84-
causesBreakdown(stats, parseEmoji(env.DISCORD_CAUSES_EMOJI)),
85+
causesBreakdown(stats, env),
8586
"",
86-
thanks(end, stats.event.year),
87+
thanks(end, stats.event.year, env),
8788
].join("\n");
8889
ctx.waitUntil(
8990
Promise.all(

src/util/causes.ts

Lines changed: 4 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,13 @@
1+
import type { Env } from "../env";
2+
import { emojiCauses } from "./emoji";
13
import { bold, money } from "./format";
24
import type { Stats } from "./stats";
35

4-
export const parseEmoji = (env?: string): Record<string, string> => {
5-
try {
6-
const raw = JSON.parse(env || "{}");
7-
if (
8-
typeof raw !== "object" ||
9-
raw === null ||
10-
Object.entries(raw).some(
11-
([key, value]) =>
12-
typeof key !== "string" || typeof value !== "string",
13-
)
14-
)
15-
throw new Error("Invalid JSON");
16-
return raw;
17-
} catch (e) {
18-
return {};
19-
}
20-
};
21-
22-
const hash = (str: string) => {
23-
let hash = 0;
24-
for (let i = 0; i < str.length; i++) {
25-
hash = (hash << 5) - hash + str.charCodeAt(i);
26-
hash |= 0;
27-
}
28-
return hash;
29-
};
30-
31-
const hearts = [
32-
":heart:",
33-
":orange_heart:",
34-
":yellow_heart:",
35-
":green_heart:",
36-
":blue_heart:",
37-
":purple_heart:",
38-
];
39-
40-
const heart = (str: string) => hearts[Math.abs(hash(str)) % hearts.length];
41-
42-
const causesBreakdown = (stats: Stats, emoji: Record<string, string>) =>
6+
const causesBreakdown = (stats: Stats, env: Env) =>
437
stats.causes
448
.map((cause) => {
459
return bold(
46-
`${emoji[cause.name] || heart(cause.name)} [${cause.name}](${
10+
`${emojiCauses(env, cause.name)} [${cause.name}](${
4711
cause.url
4812
}): ${money(
4913
"£",

src/util/emoji.ts

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import type { Env } from "../env";
2+
3+
const parseEmoji = (env?: string): Record<string, string> => {
4+
try {
5+
const raw = JSON.parse(env || "{}");
6+
if (
7+
typeof raw !== "object" ||
8+
raw === null ||
9+
Object.entries(raw).some(
10+
([key, value]) =>
11+
typeof key !== "string" || typeof value !== "string",
12+
)
13+
)
14+
throw new Error("Invalid JSON");
15+
return raw;
16+
} catch (e) {
17+
return {};
18+
}
19+
};
20+
21+
const hash = (str: string) => {
22+
let hash = 0;
23+
for (let i = 0; i < str.length; i++) {
24+
hash = (hash << 5) - hash + str.charCodeAt(i);
25+
hash |= 0;
26+
}
27+
return hash;
28+
};
29+
30+
const hearts = [
31+
":heart:",
32+
":orange_heart:",
33+
":yellow_heart:",
34+
":green_heart:",
35+
":blue_heart:",
36+
":purple_heart:",
37+
];
38+
39+
let emojiCausesCache: Record<string, string> | null = null;
40+
export const emojiCauses = (env: Env, cause: string) => {
41+
const obj =
42+
emojiCausesCache ||
43+
(emojiCausesCache = parseEmoji(env.DISCORD_CAUSES_EMOJI));
44+
return obj[cause] || hearts[Math.abs(hash(cause)) % hearts.length];
45+
};
46+
47+
const regular = {
48+
mascot: ":snowflake:",
49+
happy: ":smile:",
50+
hype: ":partying_face:",
51+
love: ":heart_eyes:",
52+
sad: ":sob:",
53+
};
54+
55+
let emojiRegularCache: Record<string, string> | null = null;
56+
export const emojiRegular = (env: Env, type: keyof typeof regular) => {
57+
if (!(type in regular)) throw new Error(`Invalid emoji type: ${type}`);
58+
59+
const obj =
60+
emojiRegularCache ||
61+
(emojiRegularCache = parseEmoji(env.DISCORD_REGULAR_EMOJI));
62+
return obj[type] || regular[type as keyof typeof regular];
63+
};

0 commit comments

Comments
 (0)