Overview
Two modules insrc/cache/ provide the persistence layer for the bot:
snapshotCache.ts— stores the last successful AI-curated deal list for the current calendar day. Allows/dealsrequests and repeated cron ticks to be served from disk without re-calling CheapShark or GPT.deduplication.ts— records which games have been notified recently so they are not broadcast again within the configured deduplication window.
data/ directory and use write-file-atomic for crash-safe persistence.
SnapshotCache
File:src/cache/snapshotCache.tsData file:
data/snapshot.json
loadSnapshot
data/snapshot.json and returns the parsed DailySnapshot, or null if the file does not exist or cannot be parsed.
saveSnapshot
data/snapshot.json using an atomic write. Creates the data/ directory if it does not exist.
The snapshot to persist. Contains
deals, candidatesHash, and createdAt (ISO string).isSnapshotFresh
true if the snapshot’s createdAt date matches today’s date in the America/Bogota timezone.
America/Bogota. Using the server’s local time would cause incorrect freshness checks when the host is in a different timezone.
clearStaleSnapshot
data/snapshot.json if it exists and is not from today. Called once at process startup to prevent the bot from serving stale deal data after downtime spanning midnight.
hashCandidates
steamAppID before hashing to ensure the result is independent of the order returned by CheapShark.
The hash covers both the fields GPT uses to decide (title, metacriticScore, steamRatingText) and the fields visible to users (salePrice, normalPrice, savings, dealID). If any of these change — even if the same game is still on sale — the hash differs and GPT is re-consulted.
Deduplication
File:src/cache/deduplication.tsData file:
data/notified_games.json
getNotifiedIds
data/notified_games.json and returns a Set<string> of steamAppID values that were notified within the last config.dedup.days days. Entries older than the cutoff are excluded from the returned set (but not yet removed from disk — cleanup happens in markAsNotified).
dealsService calls this once per pipeline run and passes the result to applyHardFilters, keeping the rules filter module free of any I/O.
markAsNotified
- Loads the existing log.
- Removes entries older than the deduplication window.
- Appends new entries for games not already present in the cleaned list.
- Writes the result atomically.
Games to mark as notified. Only
steamAppID is required — titles are intentionally not stored.data/notified_games.json stores only steamAppID and notifiedAt (ISO timestamp). Game titles are not persisted — they are not needed for deduplication and omitting them keeps the file compact and avoids storing display data that may change.