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. defaultruns when nothing else matched.timeout/errorhandle the failure paths of a blocking step likeask.*.
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 guardfurther 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}`)
}
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!")