Skip to content
← Projects

LeetCode Daily Check-in Bot

A dependency-free Node.js script and GitHub Actions workflow that checks, via LeetCode's GraphQL API, who has solved the daily challenge and posts a status summary to Telegram at a precise time — with retries, honest 'could not verify' handling and a design that works around late scheduled runs.

Built by

GitHub
Node.jsGitHub ActionsGraphQLTelegram Bot APIAutomationCron

Overview

A small automation I built to keep a study streak going: every evening it checks which of a configured list of LeetCode accounts have solved the daily challenge and posts a summary — completed, still pending, and "couldn't verify" — to a Telegram chat.

It is one 195-line script with no npm dependencies (Node 20's built-in fetch) plus a GitHub Actions workflow that runs it. It is small on purpose, and I like it as an example of the unglamorous parts of automation: time zones, flaky APIs and not drawing a false conclusion from missing data.

What It Does

  • Queries LeetCode's public GraphQL endpoint for the active daily challenge and each user's recent accepted submissions
  • Marks a user completed only if there is an accepted submission for that problem on or after the challenge's UTC start of day
  • Posts a formatted message to Telegram through the Bot API
  • Distinguishes three states — completed, pending, and unavailable — so a hidden profile is never reported as "pending"
  • Supports a DRY_RUN mode that prints the message instead of sending it
  • Can also trigger a separate reminder service on a schedule (see the workflow)

Engineering Notes

  • Retries with backoffWaiting progressively longer between retries after failures — often exponentially, with random jitter — so a struggling service isn't hammered.: each GraphQL call is attempted up to three times with an increasing delay and a 15-second timeout.
  • Empty is not the same as no: LeetCode sometimes returns an empty list for a public profile, so an empty result is retried before the user is marked unavailable.
  • Explicit time boundary: the challenge date is parsed as YYYY-MM-DDT00:00:00Z, so the "did they solve it today" check doesn't depend on the runner's time zone.
  • Concurrent checks: users are checked in parallel with Promise.all, with each failure isolated to that user.
  • Scheduling around GitHub's delays: scheduled workflows can start hours late, so the workflow starts early, sleeps until the exact send time, and uses backup cron entries with a concurrency group so duplicates are skipped.

Tech Stack

Node.js 20 (no dependencies), GitHub Actions (scheduled and manual dispatch), LeetCode GraphQL, Telegram Bot API

Notes

It relies on an undocumented public endpoint, so it can break without notice. Usernames and tokens are supplied through environment variables and repository secrets; none are stored in the code.

Related projects