KursusOtomasi WhatsApp untuk Bisnis
ModulMembangun bot
Durasi12 min
Progress0/7
Materi Pelajaran0/7 · 0%
  1. 1Apa yang kita bangun, dan setup proyeknya8 min
  2. 2Menyambung ke WhatsApp Cloud API12 min
  3. 3Session dan state machine alur pesanan12 min
  4. 4Mini CRM: kontak, order, dan label10 min
  5. 5Pembayaran dalam chat dengan sandbox Midtrans12 min
  6. 6Hardening: dedup, retry, dan pengambilalihan manusia10 min
  7. 7Deploy dengan Docker, lepas dari laptop selamanya12 min

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));
}
Buat folder data/ dulu. Satu upsert dan satu select adalah seluruh lapisan memori si bot.

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 };
  }
}
Setiap state memvalidasi inputnya, bertanya ulang saat input salah, dan 'admin' jadi jalan keluar dari mana pun. Action PAYMENT disambungkan di lesson 5.

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);
});
Muat, langkahkan, simpan, balas: loop di jantung setiap chatbot yang akan pernah Anda bangun.

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.

Layanan Etzal terkaitChatbot WhatsApp AIMau dikerjakan untuk Anda? Lihat caranya.
Session dan state machine alur pesanan - Otomasi WhatsApp untuk Bisnis | Etzal Academy | Etzal Group