Lesson 3 / 7 · 12 min
Session dan state machine alur pesanan
Session SQLite dan alur pesanan multi-langkah sungguhan. Checkpoint: bot menerima pesanan lengkap, langkah demi langkah.
Echo bot melupakan Anda seketika. Untuk menerima pesanan, bot butuh memori (pelanggan ini di langkah mana) dan aturan (apa yang ditanyakan berikutnya). Memori adalah tabel session; aturan adalah state machine. Keduanya muat di dua file kecil.
Langkah 1: database-nya (db.js)
import Database from "better-sqlite3";
export const db = new Database("data/bot.db");
db.exec(
"CREATE TABLE IF NOT EXISTS sessions(" +
" phone TEXT PRIMARY KEY," +
" state TEXT NOT NULL DEFAULT 'START'," +
" data TEXT NOT NULL DEFAULT '{}'," +
" updated_at INTEGER);" +
"CREATE TABLE IF NOT EXISTS processed(" +
" message_id TEXT PRIMARY KEY);"
);
export function getSession(phone) {
const row = db
.prepare("SELECT * FROM sessions WHERE phone = ?")
.get(phone);
if (!row) return { phone, state: "START", data: {} };
return { phone, state: row.state, data: JSON.parse(row.data) };
}
export function saveSession(s) {
db.prepare(
"INSERT INTO sessions(phone, state, data, updated_at) " +
"VALUES (?, ?, ?, unixepoch()) " +
"ON CONFLICT(phone) DO UPDATE SET " +
"state = excluded.state, data = excluded.data, " +
"updated_at = excluded.updated_at",
).run(s.phone, s.state, JSON.stringify(s.data));
}Langkah 2: state machine-nya (flow.js)
const MENU =
"Halo! Balas dengan angka ya:\n" +
"1. Menu & harga\n2. Buat pesanan\n3. Chat admin";
const PRICES = { A: 85000, B: 120000 };
export function step(session, text) {
const t = text.trim().toLowerCase();
if (t === "admin") return go("HUMAN", "Oke, admin akan membalas di sini ya.");
switch (session.state) {
case "START":
return go("MENU", MENU);
case "MENU":
if (t === "1") return go("MENU", "Paket A Rp 85.000, Paket B Rp 120.000. Balas 2 untuk pesan.");
if (t === "2") return go("ITEM", "Mau Paket A atau Paket B?");
if (t === "3") return go("HUMAN", "Oke, admin akan membalas di sini ya.");
return go("MENU", MENU);
case "ITEM": {
const item = t === "a" || t === "paket a" ? "A"
: t === "b" || t === "paket b" ? "B" : null;
if (!item) return go("ITEM", "Balas A atau B ya.");
session.data.item = item;
return go("QTY", "Berapa box?");
}
case "QTY": {
const qty = parseInt(t, 10);
if (!qty || qty < 1 || qty > 500)
return go("QTY", "Tulis angka saja ya, contoh: 2");
session.data.qty = qty;
session.data.total = qty * PRICES[session.data.item];
return go("ADDRESS", "Alamat pengirimannya ke mana?");
}
case "ADDRESS":
session.data.address = text.trim();
return go(
"CONFIRM",
session.data.qty + " box Paket " + session.data.item +
" ke " + session.data.address +
". Total Rp " + session.data.total.toLocaleString("id-ID") +
". Balas YA untuk lanjut bayar, atau UBAH."
);
case "CONFIRM":
if (t === "ya") return go("PAYMENT", null, "create_invoice");
if (t === "ubah") return go("MENU", MENU);
return go("CONFIRM", "Balas YA atau UBAH ya.");
default:
return go("MENU", MENU);
}
function go(state, reply, action) {
session.state = state;
return { session, reply, action };
}
}Langkah 3: sambungkan ke webhook
// server.js
import { getSession, saveSession } from "./db.js";
import { step } from "./flow.js";
import { sendText } from "./wa.js";
app.post("/webhook", async (req, res) => {
res.sendStatus(200);
const msg = req.body?.entry?.[0]?.changes?.[0]?.value?.messages?.[0];
if (!msg || msg.type !== "text") return;
const session = getSession(msg.from);
if (session.state === "HUMAN") return; // a person owns this chat now
const { reply } = step(session, msg.text.body);
saveSession(session);
if (reply) await sendText(msg.from, reply);
});Checkpoint: jalani alur penuh dari ponsel Anda, menu, item, jumlah, alamat, konfirmasi, termasuk input salah (huruf di tempat angka). Bot harus bertanya ulang dengan sopan dan tidak pernah crash. Ketahanan itulah beda demo dan produk.
Yang Anda dapatkan
- Session di SQLite memberi bot memori antar pesan, berkunci nomor telepon
- State machine memvalidasi tiap input dan bertanya ulang alih-alih crash
- 'admin' keluar ke manusia dari state mana pun; state HUMAN membungkam bot
- Muat, langkahkan, simpan, balas adalah loop chatbot yang universal
Sumber terkurasi
Referensi pilihan untuk mendalami lebih jauh, direview oleh tim kami.