Skip to content

RonenMars/groceries-bot

Repository files navigation

🛒 Groceries Bot

A shared groceries list you control from Telegram and a mobile-friendly web UI, backed by Neon Postgres.

  • Telegram bot via webhook (no polling)
  • Next.js App Router + TypeScript + Tailwind CSS
  • Drizzle ORM over Neon Postgres
  • A single shared service layer drives both the bot and the web UI — no duplicated business logic
  • One Telegram chat (private or group) = one shared workspace

How it works

Each Telegram chat_id maps to a workspace. Everyone in a group shares the same lists. The bot replies to /start with a private web link (/w/<token>) so you can manage lists in the browser too. The web link uses an unguessable token instead of a numeric id — there is no login in this MVP.

Tech stack

Concern Choice
Framework Next.js (App Router)
Language TypeScript
Package manager pnpm
Styling Tailwind CSS v4
Database Neon Postgres
ORM Drizzle ORM + drizzle-kit
Telegram Bot API webhook (sendMessage)
Tests Vitest

Local setup

1. Install dependencies

pnpm install

2. Create a Neon database

  1. Sign up at neon.tech and create a project.
  2. Copy the connection string (use the pooled/serverless URL).
  3. Put it in .env as DATABASE_URL.

3. Create a Telegram bot

  1. In Telegram, talk to @BotFather and run /newbot.
  2. Copy the bot token into .env as TELEGRAM_BOT_TOKEN.
  3. Choose any long random string for TELEGRAM_WEBHOOK_SECRET.

4. Environment variables

Copy the example file and fill it in:

cp .env.example .env
DATABASE_URL=postgresql://...           # Neon connection string
TELEGRAM_BOT_TOKEN=123456:ABC-DEF...    # from @BotFather
TELEGRAM_WEBHOOK_SECRET=long-random-str # you choose this
NEXT_PUBLIC_APP_URL=https://your-app... # public base URL (no trailing slash)

5. Run migrations

Generate is already committed in lib/drizzle/. To apply the schema to your database:

pnpm db:migrate     # apply committed migrations
# or, for quick local iteration:
pnpm db:push        # push schema directly (no migration files)

To regenerate migrations after changing lib/db/schema.ts:

pnpm db:generate

6. Run the dev server

pnpm dev

App runs at http://localhost:3000.

Telegram webhook registration

Telegram must reach your app over HTTPS. Deploy the app (e.g. to Vercel) or expose it locally with a tunnel, then register the webhook.

Using the helper script

With TELEGRAM_BOT_TOKEN, TELEGRAM_WEBHOOK_SECRET, and NEXT_PUBLIC_APP_URL set in your environment:

pnpm set-webhook

Or manually

https://api.telegram.org/bot<token>/setWebhook?url=<app-url>/api/telegram/webhook&secret_token=<secret>

The webhook route validates the X-Telegram-Bot-Api-Secret-Token header against TELEGRAM_WEBHOOK_SECRET on every request.

Local webhook testing with a tunnel

Telegram cannot call localhost. Use a tunnel such as ngrok:

ngrok http 3000
# set NEXT_PUBLIC_APP_URL to the https URL ngrok prints, then:
pnpm set-webhook

Telegram usage

/start                      create workspace + default list, get web link
/help                       list all commands
/add milk, eggs, bread      add items (comma-separated => multiple items)
/list                       show the current list, grouped by category
/done milk                  mark bought  (also accepts the item id, e.g. /done 12)
/undone milk                mark pending
/urgent eggs                mark urgent 🔥
/noncritical napkins        mark non-critical ▫️
/normal eggs                reset priority
/remove bread               delete an item
/lists                      show all lists
/newlist Weekly shop        create + switch to a new list
/use Weekly shop            switch current list (id or name)
/close                      close (archive) the current list
/baselists                  show templates
/savebase Staples           save the current list as a reusable template
/recreate Staples           start a new list from a template

Items resolve by id first, then by name (case-insensitive). If a name is ambiguous, the bot shows the matching ids instead of guessing.

Web UI

  • / — landing page (explains Telegram control)
  • /w/<token> — workspace: see lists, create, switch current, close
  • /w/<token>/lists/<listId> — items grouped by category; check/uncheck, edit, delete, change priority and category. Closed lists are read-only.
  • /w/<token>/base-lists — manage templates; recreate a list from a template

Within a category items are ordered urgent → normal → non-critical → bought. Bought items are de-emphasized; urgent highlighted; non-critical softened.

Project structure

app/
  page.tsx                          landing
  not-found.tsx
  actions.ts                        server actions -> service layer
  api/telegram/webhook/route.ts     webhook: validate secret -> dispatch -> 200
  w/[token]/                        web UI (workspace, lists, base-lists)
lib/
  db/{index.ts, schema.ts}          Drizzle + neon-http
  drizzle/                          generated migrations
  grocery/{service.ts, repository.ts, types.ts}   shared business logic
  telegram/{client.ts, commands.ts, handle-update.ts, render.ts, types.ts}
  env.ts                            validated env
scripts/set-webhook.ts             webhook registration helper
tests/                             vitest unit tests

Scripts

pnpm dev          # run the dev server
pnpm build        # production build
pnpm start        # run the production build
pnpm lint         # eslint
pnpm typecheck    # tsc --noEmit
pnpm test         # vitest run
pnpm db:generate  # generate a migration from the schema
pnpm db:migrate   # apply migrations
pnpm db:push      # push schema directly (dev)
pnpm set-webhook  # register the Telegram webhook

Phase 2 (not implemented)

The data model and service layer are designed so AI features can be added later without restructuring: automatic category detection, item-name normalization, Hebrew/English grocery mapping, and image-based recognition from photos. For now category assignment is manual and editable.

About

Telegram-controlled shared groceries list bot with a Next.js web UI

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages