Skip to content

Repository files navigation

Telegram Reminder Bot

A Telegram bot that creates reminders from natural language using LLM-based text extraction.

Features

  • Natural language processing: Send any text and the bot extracts task structure (title, description, date, time) using LLM
  • Smart follow-up: If information is missing, the bot asks follow-up questions
  • Deadline reminders: Sends notifications 1 minute before each deadline
  • Task management: Create, view, edit, and delete reminders
  • Restart-safe: All tasks persist in MySQL; reminders resume after restart
  • Idempotent: Duplicate messages are ignored
  • Operational alerts: Optional second Telegram bot sends warn/error logs; users subscribe via that bot (/start, /subscribe)

Architecture

src/
├── index.js                 # Entry point, startup logic
├── config.js                # Environment-based configuration
├── db.js                    # MySQL connection pool
├── models/
│   ├── task.js              # Task data access (CRUD)
│   ├── conversation.js      # Conversation state persistence
│   └── alert-subscriber.js  # Users subscribed to system log alerts
├── services/
│   ├── llm.service.js       # LLM API integration (OpenAI-compatible)
│   ├── task.service.js      # Task business logic
│   ├── conversation.service.js  # State management
│   └── reminder.service.js  # Reminder scheduler
├── handlers/
│   ├── command.handler.js   # Main bot commands (/new, /tasks, etc.)
│   ├── message.handler.js   # Free text + conversation flow
│   └── alert-bot.handler.js # Second bot: subscribe/unsubscribe to log alerts
├── utils/
│   ├── logger.js            # Winston logger
│   └── date.utils.js        # Date/time utilities (dayjs)
└── migrations/
    ├── init.sql             # Database schema
    └── run.js               # Standalone migration runner

LLM Usage

Model

Default: gpt-4o-mini (configurable via LLM_MODEL env var). Any OpenAI-compatible API can be used by changing LLM_BASE_URL.

Request Format

The bot sends a system prompt instructing the LLM to act as a task extraction assistant, followed by the user's message with current date/time context.

Response Format

The LLM returns a strict JSON object:

{
  "title": "Meeting with team",
  "description": "Discuss Q2 planning",
  "date": "2025-03-25",
  "time": "14:00"
}

Fields are set to null if not determinable from the text. The application validates the response format and retries up to 3 times on failure.

Bot Commands

Command Description
/start Welcome message
/new Create a new reminder
/tasks List active reminders
/task <id> View reminder details
/edit <id> Edit a reminder
/delete <id> Delete a reminder
/cancel Cancel current action
/help Show help

Alert bot (second bot) — log notifications

If TELEGRAM_ALERT_BOT_TOKEN is set, the app starts a second Telegram bot (long polling) only for subscriptions and for sending warn/error alerts.

Command Description
/start Subscribe to alerts (saves telegram_user_id in alert_subscribers)
/subscribe Subscribe again
/unsubscribe Unsubscribe
/help Short help

Use these commands in a private chat with the alert bot (not in a group).

Subscribing to log alerts (broadcast)

  1. Create a second bot in @BotFather for alerts only. Set TELEGRAM_ALERT_BOT_TOKEN to its token (must differ from the main bot token).
  2. In .env, set TELEGRAM_ALERT_BROADCAST_ALL_USERS=true to message everyone in alert_subscribers.
  3. Optionally set TELEGRAM_ALERT_CHAT_ID to also send every alert to one extra chat (e.g. admin DM or group).
  4. Each user who wants alerts must open the alert bot and send /start or /subscribe so they are stored in alert_subscribers and Telegram allows that bot to DM them.

If the user never talks to the alert bot, the API may reject DMs (403 / “bot can’t initiate conversation”).

Data Model

Task

Field Type Description
id INT (PK) Auto-increment
telegram_user_id BIGINT Telegram user ID
status ENUM DRAFT, ACTIVE, DONE, CANCELED, DELETED
title VARCHAR(255) Task title
description TEXT Task description
deadline_at DATETIME Deadline in UTC
notified TINYINT Whether reminder was sent
created_at DATETIME Creation timestamp
updated_at DATETIME Last update timestamp

ConversationState

Field Type Description
telegram_user_id BIGINT (PK) Telegram user ID
state VARCHAR(50) Current conversation state
payload JSON State-specific data
updated_at DATETIME Last update timestamp

AlertSubscriber

Used when TELEGRAM_ALERT_BROADCAST_ALL_USERS=true. Rows are created/removed by the alert bot (/start, /subscribe, /unsubscribe).

Field Type Description
telegram_user_id BIGINT (PK) Telegram user ID
created_at DATETIME First subscription time
updated_at DATETIME Last resubscribe time

Logging and alerting (end-to-end)

This section describes how logs flow from application code to the console and optionally to Telegram.

1. Logger creation

  • Module: src/utils/logger.js
  • Library: Winston
  • Level: LOG_LEVEL (default info). Messages below this level are ignored by Winston entirely (they never reach the console or Telegram).

2. Default metadata and format

  • Every log line includes default meta: { service: 'reminder-bot' }.
  • Console transport uses a human-readable format: timestamp (in TZ_DEFAULT / Europe/Moscow locale), level, message, and JSON for extra fields.
  • In Docker, these lines are what you see in docker compose logs -f bot.

3. Where logs are emitted

Throughout the app, code calls logger.info, logger.warn, logger.error, etc., for example:

  • Incoming Telegram messages (truncated text)
  • LLM request attempts and failures
  • Task CRUD and reminder sends
  • Database and startup lifecycle (Configuration validated, Reminder bot is running, …)

4. Telegram alert hook (warn / error only)

After the Winston logger is created, logger.warn and logger.error are wrapped:

  1. The original Winston method runs first → message always appears in the console (if level allows).
  2. Then sendTelegramAlert(level, message, meta) runs asynchronously for warn and error only.

5. When Telegram alerts are sent

Alerts are sent only if all of the following hold:

  • TELEGRAM_ALERT_BOT_TOKEN is set (the alert bot, not the main bot). The same token is used for outgoing sendMessage calls and, when set, for a second long-polling bot instance that handles /start, /subscribe, and /unsubscribe (alert-bot.handler.js).
  • Either TELEGRAM_ALERT_CHAT_ID is set or TELEGRAM_ALERT_BROADCAST_ALL_USERS=true.
  • The log level is at least as severe as TELEGRAM_ALERT_LEVEL (default warn: both warn and error are sent; if set to error, only error is sent).

6. Recipients

  • Single chat: if TELEGRAM_ALERT_BROADCAST_ALL_USERS is false, only TELEGRAM_ALERT_CHAT_ID receives messages.
  • Broadcast: if TELEGRAM_ALERT_BROADCAST_ALL_USERS=true, recipients are loaded from MySQL table alert_subscribers (up to TELEGRAM_ALERT_MAX_RECIPIENTS, newest updated_at first). If TELEGRAM_ALERT_CHAT_ID is also set, it is included in addition to subscribers.

7. HTTP call and payload

  • The alert bot calls https://api.telegram.org/bot<token>/sendMessage with JSON body: chat_id, text, disable_notification (false for error, true for warn).
  • text includes level, message, and serialized meta (JSON). Long text is truncated (Telegram limit).
  • Failures are logged at debug level on the main logger (to avoid alert loops).

8. Concurrency guard

A boolean isSendingAlert ensures that alert sending does not re-enter while a previous batch is still in progress (reduces risk of overlapping broadcasts).

9. Viewing logs

  • Local / Docker: docker compose logs -f bot or stdout of npm start.
  • Telegram: only for wrapped warn/error when alert env is configured and recipients are valid.

Quick Start

Prerequisites

  • Node.js 18+
  • MySQL 8.0+
  • Telegram Bot Token (from @BotFather)
  • OpenAI API key (or compatible LLM API)

Local Setup

  1. Clone the repository:

    git clone <repo-url>
    cd telegram-reminder-bot
  2. Install dependencies:

    npm install
  3. Create .env file from the example:

    cp .env.example .env
  4. Fill in your secrets in .env:

    • TELEGRAM_BOT_TOKEN — your bot token
    • LLM_API_KEY — your OpenAI API key
  5. Ensure MySQL is running and create the database:

    npm run migrate
  6. Start the bot:

    npm start

Docker Setup

  1. Create .env file from the example:

    cp .env.example .env
  2. Fill in your secrets in .env

  3. Start with Docker Compose:

    docker compose up -d

    This starts both the MySQL database and the bot. The bot waits for MySQL to be healthy before starting.

  4. View logs:

    docker compose logs -f bot
  5. Stop:

    docker compose down

Environment Variables

Variable Required Default Description
TELEGRAM_BOT_TOKEN Yes Telegram Bot API token
TELEGRAM_ALERT_BOT_TOKEN No (empty) Separate bot token for warning/error alerts
TELEGRAM_ALERT_CHAT_ID No (empty) Telegram chat ID where alerts are sent
TELEGRAM_ALERT_LEVEL No warn Minimum level for alerts (warn or error)
TELEGRAM_ALERT_BROADCAST_ALL_USERS No false If true, send alerts to every telegram_user_id in alert_subscribers (subscribe via alert bot)
TELEGRAM_ALERT_MAX_RECIPIENTS No 500 Max subscribers to message per alert batch
LLM_API_KEY Yes LLM API key
LLM_MODEL No gpt-4o-mini LLM model name
LLM_BASE_URL No https://api.openai.com/v1 OpenAI-compatible API base URL. For ProxyAPI, use https://api.proxyapi.ru/openai/v1 with a ProxyAPI key.
LLM_MAX_RETRIES No 3 Max LLM retry attempts
DB_HOST No localhost MySQL host
DB_PORT No 3306 MySQL port
DB_USER No root MySQL user
DB_PASSWORD No (empty) MySQL password
DB_NAME No reminder_bot MySQL database name
TZ_DEFAULT No Europe/Moscow Default timezone
REMINDER_CHECK_INTERVAL_MS No 15000 Reminder check interval (ms)
LOG_LEVEL No info Log level (error, warn, info, debug)

Autodeploy (Bonus)

For automated deployment on a local VM, a GitHub Actions workflow or a simple Git hook can be used:

Git Hook Approach

  1. On the VM, set up a bare Git repository:

    mkdir -p /opt/reminder-bot.git
    cd /opt/reminder-bot.git
    git init --bare
  2. Create a post-receive hook (/opt/reminder-bot.git/hooks/post-receive):

    #!/bin/bash
    TARGET="/opt/reminder-bot/app"
    GIT_DIR="/opt/reminder-bot.git"
    
    git --work-tree=$TARGET --git-dir=$GIT_DIR checkout -f main
    cd $TARGET
    npm ci --omit=dev
    docker compose down
    docker compose up -d --build
  3. Make it executable:

    chmod +x /opt/reminder-bot.git/hooks/post-receive
  4. Add the VM as a remote on your development machine:

    git remote add deploy ssh://user@vm-ip/opt/reminder-bot.git
  5. Push to deploy:

    git push deploy main

No manual intervention required after the initial setup.

License

MIT

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages