All projects

LearnGPT

A platform to learn with AI flashcards, a Pomodoro timer, a daily ML research-paper feed with an in-app reader, and a Telegram bot for on-the-go studying

Year
May 2025
Field
Education
Built with
Next.js, MongoDB, TailwindCSS, Telegram Bot API, Generative AI, GitHub Actions, pdf.js
LearnGPT: Sign in
LearnGPT: Library
LearnGPT: Research papers
LearnGPT: Paper reader

Overview

A full-stack study platform that combines AI-generated flashcards, a Pomodoro timer, and a feature-rich Telegram bot — letting users manage notes, tasks, and study sessions directly from Telegram as well as the web. It also curates a daily feed of new ML research papers (kernel optimisation, pre-training and post-training) that can be read in the app or browsed from the bot.

Technical Challenges

Telegram Bot Integration

The bot communicates through a webhook endpoint (/api/telegram/webhook) secured with a secret_token header. Updates are routed through grammY to command handlers covering notes (/notes, /addnote, /flashcards, /regencard, /editcard, /addcard, /deletenote), spaced revision (/revise), tasks (/tasks, /addtask, /donetask, /deletetask), the Pomodoro timer (/pomo, /stats), AI summarisation (/summarise — accepts a PDF, PPTX, DOCX, photo or URL), research papers (/papers) and community search (/search, plus inline @bot <query>). Each webhook call can land on a fresh serverless instance, so state for multi-step flows (e.g., waiting for a file after /summarise) is persisted per chat in a MongoDB telegram_sessions collection rather than in memory, and cleared on /cancel.

Flashcard Generation Pipeline

User-pasted text or an uploaded file (PDF, PPTX, DOCX, image or URL) is sent to Gemini 2.5 Flash-Lite (via LangChain) through a Next.js API route. The prompt asks for a strict JSON shape — a title, a subtopic and 5–10 question/answer pairs — and the response is streamed back so cards appear incrementally rather than after a full round-trip. To keep LLM costs down, outputs are cached in MongoDB for an hour under a hash of the source document, and the document is registered with Gemini's context cache so repeat requests on the same material don't pay to resend it.

Pomodoro Timer Shared Across Web and Telegram

The timer is server-authoritative so it can be driven from both the web app and the bot's /pomo command. Each user's runtime lives in MongoDB as a mode (focus, short break or long break) plus an absolute endsAt timestamp rather than a ticking counter, so no server process has to run the clock. Any read lazily settles an expired session with a conditional update that matches isActive and the exact endsAt, so concurrent web and bot requests can't double-count it: only the request whose update succeeds logs the pomodoro with $inc and sends a Telegram completion alert. The web app stays in sync over a Server-Sent Events stream, with polling as a fallback.

Persistence & Schema Design

Notes, flashcard decks, and tasks are stored in MongoDB. Cards embed directly inside their parent note document (sub-document array) since they are always accessed together, avoiding unnecessary round-trips. Task completion and pomodoro counters are updated with atomic $set / $inc operations to prevent race conditions from concurrent web and bot access.

The bot registers an inline_query handler so users can type @bot <query> in any Telegram chat for live, as-you-type community note search without opening a dedicated chat. Results are a case-insensitive title match, newest first, returned as InlineQueryResultArticle objects whose message previews each note's flashcards, trimmed to fit Telegram's message limit. cache_time: 0 stops Telegram caching results, so every keystroke gets a fresh query.

Daily Research Paper Feed

A GitHub Actions cron job runs a TypeScript script every morning that queries the arXiv API for new papers on ML kernel optimisation, pre-training and post-training. arXiv's search stems words ("pretraining" also matches "pretrained"), so each topic pairs a broad arXiv query with a local regex filter to keep the results on-topic. Only papers submitted in the last 7 days are considered, and each one is upserted by its unique arXiv ID ($setOnInsert for the paper, $addToSet for its topics), so re-runs never create duplicates. A TTL index deletes papers 30 days after they were stored; the lookback window stays well under that, so expired papers are never re-added.

In-App Paper Reader

Papers open in a full-screen reader with two views. Reader fetches arXiv's LaTeXML-generated HTML, sanitizes it with DOMPurify (keeping MathML and SVG), and restyles it in the app's own typography: wide equations and tables scroll on their own, and footnotes become focusable popovers. PDF draws the original pages onto canvases with pdf.js, so it also works on phones, whose browsers can't show PDFs inline. Pages render lazily via IntersectionObserver, release their canvases once off-screen, and stay under iOS Safari's 16.7M-pixel canvas limit.

Telegram Topic Filters

/papers replies with the latest papers and inline topic buttons showing live counts. Tapping one sends a callback_query that edits the same message in place. Telegram can't make a command with an argument (/papers kernel) tappable, so buttons are the only way to filter with one tap.

System architecture

  • Client
  • Service
  • External / AI
  • Storage
  • Infrastructure
  • Live

Dual-interface app: a Next.js web frontend and a webhook-driven Telegram bot share the same API routes and MongoDB backend. The bot exposes notes, flashcard, task, and AI-summarise commands via Telegram's Bot API; the Pomodoro timer is stored server-side as an end timestamp, so the web app (kept in sync over SSE) and the bot's /pomo command share one clock. An LLM API route handles both flashcard generation and document summarisation. A daily GitHub Actions job pulls new arXiv papers into MongoDB, where a TTL index expires them after 30 days, and the in-app reader loads each paper's HTML or PDF straight from arXiv.

Next project · ProductivityMeetifyA platform to connect students and educators