-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathchallenges.ts
108 lines (90 loc) · 2.62 KB
/
challenges.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
import { isAfter, addDays, isFuture } from 'date-fns'
import { UTCDate } from '@date-fns/utc'
import { dev } from '$app/environment'
export interface Challenge {
title: string
day: number
body: string
discordLink: string
unlockDate: Date
image: string | null
locked: boolean
}
interface ChallengeImport {
default: any
metadata?: {
title: string
discordLink: string | null
image: string | null
}
}
const DEFAULT_DISCORD_LINK =
'https://discord.com/channels/457912077277855764/1158027748699279430'
const LOCKED_BODY =
'Svelte Bot commends you for trying, but there are no spoilers in Advent of Svelte'
function createUnlockDate(year: number, day: number) {
return new Date(`${year}-12-${day < 10 ? '0' : ''}${day}T00:00:00Z`)
}
const CHALLENGE_FILES = {
2023: () =>
import.meta.glob<ChallengeImport>('./2023/day-*.svx', { eager: true }),
}
type Year = keyof typeof CHALLENGE_FILES
export async function getChallenges(year: Year) {
const challengeFiles = CHALLENGE_FILES[year]()
const NOW_UTC = new UTCDate()
const challenges = await Promise.all(
Object.entries(challengeFiles)
// Parse the files to something useful
.map<Challenge>(([path, mod]) => {
const day = Number(path.match(/day-(\d+)/)![1])
const unlockDate = createUnlockDate(year, day)
const locked = dev ? false : isFuture(unlockDate)
if (!mod.metadata?.title?.length) {
throw new Error(`Missing title for day ${day}`)
}
return {
day,
locked,
title: locked ? 'Spoilers' : mod.metadata.title,
body: locked ? LOCKED_BODY : mod.default.render().html,
discordLink:
mod.metadata?.discordLink || DEFAULT_DISCORD_LINK,
image: locked ? mod.metadata?.image || null : null,
unlockDate,
}
})
// Sort it by day
.sort((a, b) => a.day - b.day)
// Only show the available challenges, and the next day (locked)
.filter((challenge) =>
dev ? true : isAfter(addDays(NOW_UTC, 1), challenge.unlockDate),
)
.map(async (challenge) => {
let image: string | null = null
if (challenge.image) {
const mod = await import(`./images/${challenge.image}.png`)
image = mod.default as string
}
return {
...challenge,
image,
}
}),
)
// Push a empty locked challenge if the next day hasn't been written yet
// purely for UI purposes
if (!dev && !challenges.some((c) => c.locked) && challenges.length < 24) {
const day = challenges.length + 1
challenges.push({
day,
title: 'Spoilers',
locked: true,
body: LOCKED_BODY,
discordLink: DEFAULT_DISCORD_LINK,
image: null,
unlockDate: createUnlockDate(year, day),
})
}
return challenges
}