Back to Home

MVP bot for Max.ru: Long Polling and Reply mapping

The article describes the creation of an MVP feedback bot for Max.ru on Node.js using Long Polling and SQLite. Reply mapping logic is implemented for routing administrator responses. Full code for DB initialization and event handling is provided.

Bot for Max.ru: from installation to Reply logic
Advertisement 728x90

Building an MVP Feedback Bot for Max.ru Using Long Polling and SQLite

Max.ru provides developers with access to its Bot API for building interactive tools. For this MVP feedback bot, we use Node.js with the @maxhub/max-bot-api library and SQLite. Long Polling enables local development without requiring webhooks, servers, or HTTPS.

The key challenge is identifying the client when the admin replies. Reply messages include a link with the original message's mid, allowing us to map responses back to users via the database.

Choosing the Tech Stack and Setting Up the Project

The @maxhub/max-bot-api library supports Long Polling out of the box, eliminating the need for ngrok or Cloudflare Tunnel during development.

Google AdInline article slot

SQLite is ideal for MVP: it’s a file-based database with no separate server, minimal latency, and consistent request handling.

Install dependencies:

npm init -y
npm install @maxhub/max-bot-api sqlite sqlite3 dotenv

Create a .env file:

Google AdInline article slot
BOT_TOKEN=your_token_here
OWNER_ID=12345678

The token is generated in business.max.ru, and OWNER_ID is your personal Max user ID.

Initializing SQLite and Database Structure

Use async connection and create the reply_map table for mapping:

import { open } from 'sqlite';
import sqlite3 from 'sqlite3';

let db;
(async () => {
  db = await open({ filename: './database.sqlite', driver: sqlite3.Database });
  
  await db.exec(`
    CREATE TABLE IF NOT EXISTS reply_map (
      owner_msg_mid TEXT PRIMARY KEY,
      client_user_id INTEGER
    )
  `);
  console.log('Database ready');
})();

This table stores the mapping from owner_msg_mid to client_user_id.

Google AdInline article slot

Handling Messages from Clients

The message_created event checks the sender. Client messages are forwarded to the admin with Markdown formatting:

const ownerId = Number(process.env.OWNER_ID);

bot.on('message_created', async (ctx) => {
  const msg = ctx.message;
  const senderId = msg.sender.user_id;
  const text = msg.body.text;

  if (senderId !== ownerId) {
    
    const forwardText = `📩 **Message from ${msg.sender.first_name}** (ID: ${senderId}):

${text}`;
    
    const sentMsg = await bot.api.sendMessageToUser(ownerId, forwardText, { format: 'markdown' });

    if (sentMsg && sentMsg.body && sentMsg.body.mid) {
        await db.run('INSERT OR REPLACE INTO reply_map (owner_msg_mid, client_user_id) VALUES (?, ?)', 
          [sentMsg.body.mid, senderId]);
    }
    return;
  }
  // reply logic
});

The mid of the sent message is stored in reply_map for later routing.

Admin Response Logic

Reply messages contain link.type = 'reply' and link.message.mid — the original message ID. To find the client:

  • If link.type === 'reply': extract repliedMsgMid, query the DB.
  • Send the response back to the client using client_user_id.
  • Confirm receipt to the admin.

Sample Reply JSON structure:

{
  "body": { "text": "answer", ... },
  "sender": { ... },
  "link": {
    "type": "reply",
    "message": {
      "mid": "mid.original_message..."
    }
  }
}

Benefits of This Architecture

  • Local Development: Run with npm start—no ports or SSL setup needed.
  • Reply Interface: Admins use the standard Reply function seamlessly.
  • Simple Database: One file (database.sqlite) for easy backups.
  • Scalability: Easy migration to PostgreSQL as traffic grows.

Key Takeaways

  • Long Polling simplifies MVP development by removing the need for webhooks.
  • The SQLite reply_map ensures accurate response routing.
  • Markdown in forwarded messages adds context (client name, ID).
  • Fallback for admin replies: last active client is used if no match found.

— Editorial Team

Advertisement 728x90

Read Next