aboutsummaryrefslogtreecommitdiffstats
path: root/server/daily.ts
diff options
context:
space:
mode:
authorPinapelz <yukais@pinapelz.com>2026-06-26 18:55:09 -0700
committerPinapelz <yukais@pinapelz.com>2026-06-26 18:55:09 -0700
commit84fbd4ee1e159b0426d664cc98a3dc7165d6381f (patch)
tree4e16adcb955339d44ae181633db65f6c956c98f9 /server/daily.ts
parente2446af8e273e412e0c1a1c7f46e43bbfcf732ae (diff)
game: music video frame routes
Diffstat (limited to 'server/daily.ts')
-rw-r--r--server/daily.ts100
1 files changed, 100 insertions, 0 deletions
diff --git a/server/daily.ts b/server/daily.ts
index cfc9d6a..f3eb9d8 100644
--- a/server/daily.ts
+++ b/server/daily.ts
@@ -11,6 +11,7 @@ import {
verifySessionToken,
verifyStateSignature,
getDailySong,
+ getDailyMusicVideo,
} from "./shared";
export const dailyRouter = Router();
@@ -42,6 +43,25 @@ dailyRouter.get("/today", (_req, res) => {
});
});
+dailyRouter.get("/todayMV", (_req, res) => {
+ const date = getUtcDate();
+ const mv = getDailyMusicVideo(date);
+
+ const initialState: SignedState = {
+ date,
+ currentTry: 0,
+ didGuess: false,
+ guesses: [],
+ };
+
+ res.json({
+ date,
+ data: obfuscateSong(mv, date),
+ sessionToken: buildSessionToken(date),
+ initialSig: signState(initialState),
+ });
+});
+
dailyRouter.post("/guess", guessLimiter, (req, res) => {
const today = getUtcDate();
const body = req.body as {
@@ -121,3 +141,83 @@ dailyRouter.post("/guess", guessLimiter, (req, res) => {
guessState: nextGuess.state,
});
});
+
+dailyRouter.post("/guessMV", guessLimiter, (req, res) => {
+ const today = getUtcDate();
+ const body = req.body as {
+ sessionToken?: unknown;
+ state?: unknown;
+ sig?: unknown;
+ guess?: unknown;
+ };
+
+ if (!verifySessionToken(body.sessionToken, today)) {
+ res.status(401).json({ error: "Invalid session token." });
+ return;
+ }
+
+ const state = normalizeSignedState(body.state);
+ if (!state) {
+ res.status(400).json({ error: "Invalid state payload." });
+ return;
+ }
+
+ if (state.date !== today) {
+ res.status(409).json({ error: "State date is not valid for today." });
+ return;
+ }
+
+ if (!verifyStateSignature(state, body.sig)) {
+ res.status(403).json({ error: "State signature mismatch." });
+ return;
+ }
+
+ if (state.didGuess || state.currentTry >= 6) {
+ res.status(409).json({ error: "Round already finished." });
+ return;
+ }
+
+ let nextGuess: GuessEntry;
+ const guess = body.guess as { artist?: unknown; name?: unknown } | null | undefined;
+
+ if (guess == null) {
+ nextGuess = {
+ state: "Skipped",
+ };
+ } else if (typeof guess.artist === "string" && typeof guess.name === "string") {
+ const solution = getDailyMusicVideo(today);
+
+ if (guess.artist === solution.artist && guess.name === solution.name) {
+ nextGuess = {
+ song: { artist: guess.artist, name: guess.name },
+ state: "Correct",
+ };
+ } else if (guess.artist === solution.artist) {
+ nextGuess = {
+ song: { artist: guess.artist, name: guess.name },
+ state: "PartiallyCorrect",
+ };
+ } else {
+ nextGuess = {
+ song: { artist: guess.artist, name: guess.name },
+ state: "Incorrect",
+ };
+ }
+ } else {
+ res.status(400).json({ error: "Invalid guess payload." });
+ return;
+ }
+
+ const nextState: SignedState = {
+ date: state.date,
+ currentTry: Math.min(6, state.currentTry + 1),
+ didGuess: state.didGuess || nextGuess.state === "Correct",
+ guesses: [...state.guesses, nextGuess],
+ };
+
+ res.json({
+ state: nextState,
+ sig: signState(nextState),
+ guessState: nextGuess.state,
+ });
+});
send patches to the email below
yukais@pinapelz.com
include the subject [PATCH repo_name]
pinapelz.com
homepage