What you'll build

In this part you'll add a session system so the bot remembers where each customer is in the conversation, and a full menu flow so they can browse items and build a cart.

By the end of this part customers can:

Define the menu

BlocWeave prompt

Create src/bot/menu.ts for the Quick Bites bot. Define and export: - MenuItem: { id: string; name: string; description: string; price: number; category: string } - MENU: MenuItem[] with at least 8 items across 3 categories: Category "Burgers": { id: "b1", name: "Classic Burger", description: "Beef patty, lettuce, tomato, special sauce", price: 35 }, { id: "b2", name: "Spicy Chicken Burger", description: "Grilled chicken, jalapeños, coleslaw", price: 38 }, { id: "b3", name: "Veggie Burger", description: "Black bean patty, avocado, tomato", price: 32 } Category "Sides": { id: "s1", name: "Fries", description: "Crispy salted fries", price: 15 }, { id: "s2", name: "Coleslaw", description: "Creamy homemade coleslaw", price: 12 }, { id: "s3", name: "Onion Rings", description: "Beer-battered onion rings", price: 18 } Category "Drinks": { id: "d1", name: "Fresh Juice", description: "Pineapple, orange, or watermelon", price: 20 }, { id: "d2", name: "Chilled Water", description: "500ml", price: 5 } - CATEGORIES: string[] — ["Burgers", "Sides", "Drinks"] - getItemsByCategory(category: string): MenuItem[] - getItemById(id: string): MenuItem | undefined - formatMainMenu(): string — returns "Welcome to Quick Bites! 🍔\n\nWhat would you like?\n1. Burgers\n2. Sides\n3. Drinks\n\nReply with a number, or type *cart* to view your order." - formatCategoryMenu(category: string): string — returns "category name items with numbered list of name + price" - formatItemDetail(item: MenuItem): string — returns "name\ndescription\nGHS price\n\nReply *a* to add to cart or *b* to go back."

Build the session manager

BlocWeave prompt

Create src/bot/session.ts for the Quick Bites bot. Define: - CartItem: { item: MenuItem; quantity: number } - SessionState: { step: "main" | "category" | "item"; selectedCategory?: string; selectedItem?: MenuItem; cart: CartItem[] } Export: - sessions: Map (module-level) - getSession(from: string): SessionState — gets or creates a session for the sender number. Default: { step: "main", cart: [] } - setSession(from: string, state: SessionState): void - addToCart(from: string, item: MenuItem): void — if item already in cart, increment quantity; otherwise push { item, quantity: 1 } - clearCart(from: string): void - formatCart(from: string): string — if cart is empty return "Your cart is empty. Type *menu* to browse."; otherwise list each item as "name x quantity — GHS total" with a grand total line and "Reply *pay* to checkout or *clear* to empty your cart." - cartTotal(from: string): number

Update the webhook handler

BlocWeave prompt

Rewrite src/bot/handler.ts to use the session and menu modules. Command routing (check these first regardless of session state): - "hi" | "hello" | "start" → reset session to main, reply with formatMainMenu() - "menu" → set step to "main", reply with formatMainMenu() - "cart" → reply with formatCart(from) - "clear" → clearCart(from), reply "Cart cleared. Type *menu* to start again." Session-based routing: step === "main": - "1" → setSession step="category", selectedCategory="Burgers", reply formatCategoryMenu("Burgers") - "2" → step="category", selectedCategory="Sides", reply formatCategoryMenu("Sides") - "3" → step="category", selectedCategory="Drinks", reply formatCategoryMenu("Drinks") - anything else → reply formatMainMenu() step === "category": - "b" | "0" → step="main", reply formatMainMenu() - a digit 1-N → get the item by index from getItemsByCategory(selectedCategory)[n-1]; if valid, step="item", selectedItem=item, reply formatItemDetail(item); if invalid, reply formatCategoryMenu(selectedCategory) again step === "item": - "a" → addToCart(from, selectedItem), step="main", reply "✅ [item.name] added to your cart!\n\nType *cart* to view your order, or *menu* to keep browsing." - "b" | "0" → step="category", reply formatCategoryMenu(selectedCategory) - anything else → reply formatItemDetail(selectedItem) Default fallback: reply "Type *menu* to see our menu or *hi* to start over."

Test the full menu flow

On your phone:

  1. Send menu → you should see the main menu with 3 categories
  2. Reply 1 → you should see the Burgers category with numbered items
  3. Reply 1 → you should see the Classic Burger detail page
  4. Reply a → you should see "Classic Burger added to your cart!"
  5. Send cart → you should see your cart with the item and total
  6. Send menu, navigate to Drinks, add Fresh Juice
  7. Send cart again → you should see both items with a combined total

Project structure after Part 2

src/
  bot/
    handler.ts   ← session-aware message routing
    menu.ts      ← menu data and formatters
    session.ts   ← session state and cart management
  index.ts

What's next

In Part 3 we add checkout — when the customer types pay, the bot generates a real Paystack payment link and sends it in the WhatsApp chat. After payment, we send a confirmation message.