Tworzenie MVP bota dla Max.ru: obsługa feedbacku przez Long Polling i SQLite
Platforma Max.ru umożliwia developerom dostęp do Bot API, aby tworzyć narzędzia interakcji. Do MVP bota z feedbackiem wykorzystuje się Node.js w połączeniu z biblioteką @maxhub/max-bot-api oraz SQLite. Mechanizm Long Polling pozwala uruchomić bota lokalnie bez konfiguracji webhooków, serwera czy HTTPS.
Głównym wyzwaniem jest identyfikacja klienta podczas odpowiedzi administratora. Wiadomość typu reply zawiera link z mid oryginalnej wiadomości, co umożliwia powiązanie odpowiedzi z klientem poprzez bazę danych.
Wybór technologii i przygotowanie projektu
Biblioteka @maxhub/max-bot-api wspiera Long Polling od razu — nie trzeba używać ngrok ani Cloudflare Tunnel podczas rozwoju.
SQLite idealnie nadaje się do MVP: baza danych plikowa, bez oddzielnego serwera, minimalne opóźnienia, sekwencyjne przetwarzanie zapytań.
Instalacja zależności:
npm init -y
npm install @maxhub/max-bot-api sqlite sqlite3 dotenv
Plik .env:
BOT_TOKEN=twój_tekst_tutaj
OWNER_ID=12345678
Token generuje się w business.max.ru, a OWNER_ID to indywidualny identyfikator użytkownika w Max.
Inicjalizacja SQLite i struktura bazy danych
Połączenie asynchroniczne z utworzeniem tabeli reply_map do mapowania:
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('Baza danych gotowa');
})();
Tabela przechowuje relację owner_msg_mid → client_user_id.
Przetwarzanie wiadomości od klienta
Zdarzenie message_created sprawdza nadawcę. Wiadomości od klientów są przesyłane do administratora w formacie Markdown:
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 = `📩 **Wiadomość od ${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;
}
// logika odpowiedzi
});
mid wysłanej wiadomości zostaje zapisane w tabeli reply_map.
Logika odpowiedzi administratora
Wiadomość typu reply zawiera pole link.type = 'reply' oraz link.message.mid — ID oryginalnej wiadomości. Wyszukiwanie klienta poprzez reply_map:
- Jeśli link.type === 'reply': wyodrębnij repliedMsgMid, wykonaj zapytanie do bazy.
- Wyślij odpowiedź klientowi na podstawie client_user_id.
- Potwierdź administratorowi.
Struktura JSON reply:
{
"body": { "text": "odpowiedź", ... },
"sender": { ... },
"link": {
"type": "reply",
"message": {
"mid": "mid.oryginalnej_wiadomości..."
}
}
}
Zalety architektury
- Uruchomienie lokalne: npm start bez portów i SSL.
- Interfejs Reply: administrator korzysta z standardowej funkcji odpowiedzi.
- Prosta baza danych: pojedynczy plik database.sqlite do backupu.
- Skalowalność: migracja do PostgreSQL przy wzroście obciążenia.
Co warto pamiętać
- Long Polling upraszcza rozwój MVP, eliminując potrzebę webhooków.
- Mapowanie reply w SQLite zapewnia precyzyjną dystrybucję odpowiedzi.
- Format Markdown w przesyłaniu dodaje kontekst (imię, ID klienta).
- Fallback dla tekstowych odpowiedzi administratora — ostatni aktywny klient.
— Editorial Team
Brak komentarzy.