Skip to main content

Control Flow

Control flow decides which steps run. Botgami gives you conditionals, multi-way branching, pattern matching, loops, and error handling.

when / else

The everyday conditional:

when user.balance >= 100 {
send.text("You can afford it!")
} else {
send.text("Not enough balance.")
}

The else is optional. You can nest when blocks freely.

when … if guard

Add an extra guard condition with if:

when user.role == "admin" if user.verified {
send.text("Verified admin access granted.")
}

The body runs only when both the main condition and the if guard are true.

branch

branch routes on a string value into named arms, with optional default, timeout, and error arms. It's the standard way to handle an ask.* result:

branch ask.text("Confirm? (yes/no)", timeout: "60s") {
"yes" {
send.text("Confirmed!")
}
"no" {
send.text("Cancelled.")
stop
}
"timeout" {
send.text("Timed out — no response.")
}
"error" {
send.text("Something went wrong.")
}
default {
send.text("Please reply yes or no.")
}
}
  • String arms ("yes", "no") match exact values.
  • default runs when nothing else matched.
  • timeout / error handle the failure paths of a blocking step like ask.*.

match

match is richer pattern routing — literal values, numeric ranges, a wildcard _, and optional if guards per arm:

match user.score {
0 {
send.text("No points yet.")
}
1..50 {
send.text("Getting started.")
}
51..100 if user.premium {
send.text("Pro player!")
}
_ {
send.text("Off the charts! 🚀")
}
}
  • A literal (0) matches that exact value.
  • A range (1..50) matches numbers in that inclusive span.
  • _ is the catch-all wildcard.
  • if guard further restricts an arm.

foreach

Iterate over an array. Optionally capture the index as a second name:

foreach item in user.cart {
send.text(`• ${item.name}`)
}

// With index (0-based)
foreach item, i in flow.ranked {
set { flow.pos = i + 1 }
send.text(`${flow.pos}. ${item.name}`)
}
note

There is no while loop in Botgami. Use foreach over an array. To iterate a fixed number of times, build a range with array.range(start, end) and foreach over it.

try / catch / finally

Catch runtime errors from a block:

try {
lk = chat.invite_link(chat_id: shared.group_chat_id, name: "Invite")
send.text(`Here's your link: ${lk}`)
} catch err {
send.text(`Couldn't create a link: ${err}`)
} finally {
set { shared.attempts += 1 }
}

catch binds the error to a name (err); finally always runs. For HTTP and await calls, prefer the inline on error { } handler — see Error handling.

stop

End the current flow immediately:

when user.banned {
send.text("🚫 You are banned.")
stop
}
send.text("Welcome!")

Next