Personal tool · Telegram bot + web app · Next.js on Vercel

A reminder bot that speaks my language

Write it a plain sentence - "call mom tomorrow at 10:00" - and it pings you at the right second. Recurring schedules, snooze buttons, a web UI for the complex stuff. Built for myself, in daily use.

Reminders Bot
lakiza-reminders · Next.js · Telegram Bot API · Vercel

Try it yourself: message @etchnow_bot on Telegram ↗

Skeddy died on me, so I rebuilt it

I ran my life on a reminder bot: taxes, calls, "check the pie in the oven". When it got unreliable, I spent an evening on the sofa and shipped my own - natural-language reminders in English and Ukrainian, no forms, no date pickers. You just type.

If the text has a recognisable time, it becomes a reminder. If it doesn't, it's saved as a note - nothing you send it is ever lost.

Type

Personal tool

Built with

Next.js 16 · Telegram Bot API

Data

Vercel Postgres · Drizzle

Delivery

Upstash QStash

From a sentence to a ping

The hard part isn't parsing - it's firing at the exact second on a serverless platform that can't sleep. A webhook parses the message, and QStash calls back at the due moment.

01

You type

"taxes at 11:30", "drink water every day at 8:00"

02

Webhook parses

chrono-node (uk + en) finds the date; keywords map to RRULE

03

Saved

reminder → Postgres; no time found → note

04

QStash fires

callback at the due second - serverless can't sleep, so it doesn't

05

You get pinged

with snooze buttons; recurring ones reschedule themselves

No LLM anywhere in the pipeline. chrono-node plus a small keyword layer handles every phrase I actually use - parsing is instant, free, and deterministic.

Snooze from the notification

Every reminder arrives with inline buttons - +10 min / +1 hr / +3 hrs / tomorrow / turn off - so rescheduling is one tap, not a new message.

Bot chat: fired reminder 'buy a Bread' with the snooze button row below it

A fired reminder in the chat - snooze or dismiss without typing a word.

A web UI you log into from the chat

Complex schedules are easier on a big screen. Send the bot /web and it mints a one-time 15-second token - the link logs you straight in, no password, no signup. From there you create, edit, toggle and delete anything.

Web interface: new reminder form with date picker and recurrence select, active and completed reminder lists

The web UI - quick-add form on top, active and finished reminders below.

What it does

01

Natural language, two languages

"call mom tomorrow at 10am", "pick up the parcel in 2 days" - English and Ukrainian, parsed the same way.

02

Recurring schedules

"every day at 8:00", "on the 1st of every month", "every Sunday at 13:00", "every 10 minutes" - keywords compile to RRULE under the hood.

03

Notes by default

Text without a recognisable time is saved as a note instead of being rejected - the bot never answers "I don't understand".

04

Snooze on every ping

+10 min / +1 hr / +3 hrs / tomorrow / turn off - inline buttons on each fired reminder.

05

One-time-token web login

/web in the chat → 15-second magic link → JWT session. No password ever existed.

06

Exact-second delivery

No cron polling every minute - QStash fires a signed callback at the due moment, and recurring reminders reschedule the next occurrence.

07

Talk instead of type

Send a voice note - it's transcribed and parsed like any typed reminder. Auto-detects the language.

08

Send it a file

Drop in photos or files - stored for free by Telegram file reference and shown in the web dashboard.

One deploy, zero servers to babysit

Next.js App Router gives the bot webhook, the delivery callback and the web UI a single Vercel deploy. Postgres (Neon) via Drizzle holds the reminders; every incoming request is verified - Telegram's secret header on the webhook, QStash's signature on delivery.

Same story as the rest of this site: designed and built by me, with Claude doing the typing.

Two additions since: Groq Whisper handles voice transcription, a new attachments table in Postgres stores each file's Telegram file_id and kind, and /api/media/[id] is an auth-gated proxy that resolves an id back to a fresh Telegram URL and streams the bytes - no file ever touches disk on my end.

Next.js 16 Telegram Bot API Vercel Postgres Drizzle ORM chrono-node rrule Upstash QStash JWT · jose Groq Whisper Voice transcription File attachments
# the whole UX: you ▸ pay for internet on the 1st of every month bot ▸ ✓ recurring reminder set - next: Aug 1, 09:00 you ▸ /web bot ▸ your login link (valid 15s): https://…/login?token=…

Now it also listens, and catches whatever you throw at it

Voice input. Send the bot a voice note and it's transcribed with Groq's Whisper (whisper-large-v3), then run through the exact same natural-language parser as typed text - a voice message becomes a reminder or a note automatically, hands-free. The bot echoes back what it heard so you can catch a bad transcription before it turns into the wrong reminder. Language is auto-detected per message - Ukrainian, English, or mixed - no language lock.

Telegram chat: a voice note sent, the bot's 'Розпізнав: Buy a bread in 30 minutes' transcription confirmation, then the reminder it set for 15:20

Voice note in, transcription echoed back, reminder set - three messages, no typing.

File attachments. Send a photo, document, audio or video and it's saved as a note - any caption becomes the note text - and shows up in the web dashboard: photos render inline, video and audio get players, documents appear as download links.

Both ride on a free tier: Groq Whisper for transcription, and for files there's no blob store at all - only the permanent Telegram file_id gets saved, re-served on demand through an auth-gated proxy that a user can only ever use for their own files. The trade-off: durability rides on Telegram's file retention - reliable in practice, no formal SLA.

Built for me - but I like talking shop