Skip to content

Wizard

Wizard membimbing pengguna melalui percakapan multi-langkah (form, registrasi, onboarding). Setiap langkah adalah fungsi yang dijalankan secara berurutan.

Memulai Cepat

typescript
import { Bot, session, Wizard } from 'vibegram';

const bot = new Bot(process.env.BOT_TOKEN!);
bot.use(session());

const wizardRegistrasi = new Wizard('daftar', [
    async (ctx) => {
        await ctx.reply('Langkah 1/3: Siapa nama Anda?');
        ctx.wizard?.next();
    },
    async (ctx) => {
        ctx.wizard!.state.nama = ctx.message?.text;
        await ctx.reply(`Halo ${ctx.wizard!.state.nama}! Langkah 2/3: Email Anda?`);
        ctx.wizard?.next();
    },
    async (ctx) => {
        ctx.wizard!.state.email = ctx.message?.text;
        const { nama, email } = ctx.wizard!.state;
        await ctx.reply(`✅ Pendaftaran selesai!\nNama: ${nama}\nEmail: ${email}`);
        ctx.wizard?.leave();
    }
]);

bot.use(wizardRegistrasi.middleware());
bot.command('daftar', ctx => wizardRegistrasi.enter(ctx));

API Wizard

MetodeDeskripsi
wizard.enter(ctx)Mulai wizard dari langkah 0
ctx.wizard?.next()Maju ke langkah berikutnya
ctx.wizard?.back()Mundur ke langkah sebelumnya
ctx.wizard?.leave()Keluar dari wizard
ctx.wizard?.stateObjek state bersama antar semua langkah
ctx.wizard?.cursorIndeks langkah saat ini (0-based)

State Wizard

Objek ctx.wizard.state persisten di semua langkah dalam satu sesi wizard:

typescript
const wizardPesanan = new Wizard('pesan', [
    async (ctx) => {
        await ctx.reply('📦 Produk apa yang ingin Anda pesan?');
        ctx.wizard?.next();
    },
    async (ctx) => {
        ctx.wizard!.state.produk = ctx.message?.text;
        await ctx.reply('🔢 Berapa jumlahnya?');
        ctx.wizard?.next();
    },
    async (ctx) => {
        ctx.wizard!.state.jumlah = parseInt(ctx.message?.text || '1');
        await ctx.reply('📍 Alamat pengiriman?');
        ctx.wizard?.next();
    },
    async (ctx) => {
        const { produk, jumlah } = ctx.wizard!.state;
        const alamat = ctx.message?.text;
        await ctx.reply(
            `✅ Pesanan dikonfirmasi!\n` +
            `Produk: ${produk}\nJumlah: ${jumlah}\nAlamat: ${alamat}`
        );
        ctx.wizard?.leave();
    }
]);

Validasi Input

Jangan panggil next() untuk tetap di langkah yang sama — sempurna untuk validasi:

typescript
const wizardUsia = new Wizard('usia', [
    async (ctx) => {
        await ctx.reply('Masukkan usia Anda (hanya angka):');
        ctx.wizard?.next();
    },
    async (ctx) => {
        const usia = parseInt(ctx.message?.text || '');

        if (isNaN(usia) || usia < 1 || usia > 150) {
            // Tidak memanggil next() — pengguna tetap di langkah ini
            return ctx.reply('❌ Usia tidak valid. Masukkan angka antara 1-150:');
        }

        ctx.wizard!.state.usia = usia;
        await ctx.reply(`✅ Usia ${usia} tahun tercatat.`);
        ctx.wizard?.leave();
    }
]);

Tombol Navigasi

Izinkan pengguna kembali ke langkah sebelumnya:

typescript
const wizard = new Wizard('form', [
    async (ctx) => {
        await ctx.reply('Nama Anda?', {
            reply_markup: Markup.keyboard([
                [Markup.replyButton.text('❌ Batal')]
            ])
        });
        ctx.wizard?.next();
    },
    async (ctx) => {
        if (ctx.message?.text === '❌ Batal') {
            await ctx.reply('Form dibatalkan.', { reply_markup: Markup.removeKeyboard() });
            return ctx.wizard?.leave();
        }
        ctx.wizard!.state.nama = ctx.message?.text;
        await ctx.reply(`Nama: ${ctx.wizard!.state.nama}\nEmail?`);
        ctx.wizard?.next();
    },
    async (ctx) => {
        ctx.wizard!.state.email = ctx.message?.text;
        await ctx.reply('✅ Selesai!', { reply_markup: Markup.removeKeyboard() });
        ctx.wizard?.leave();
    }
]);

Validasi Step

Jika tidak memanggil ctx.wizard?.next(), pengguna tetap di langkah saat ini — ideal untuk loop validasi input.

Released under the ISC License.