Skip to main content

Builtin Functions

Botgami ships a large library of built-in functions for working with numbers, strings, arrays, objects, JSON, time, types, crypto, regex, ids, money, geo, random, and CSV. They're called in expressions, namespaced by category:

set { flow.name = str.lower(str.trim(user.name)) }
set { flow.count = util.len(user.todos) }
when array.includes(shared.admins, user.id) { send.text("Hi admin") }

Type signatures below use short type names: NUM (Number), STR (String), BOOL (Boolean), ANY, OBJECT, and ARRAY<T>. A ...name? parameter is optional/variadic.

High-value examples​

Arrays β€” the workhorse for ledgers and lists​

// Filter, find, reject (the upsert trio for cross-user data)
set { flow.admins = array.where(shared.users, "role", "admin") }
set { flow.me = array.find(shared.ledger, "uid", user.id) }
set { shared.ledger = array.reject(shared.ledger, "uid", user.id) }

// Sort + take a top-N leaderboard
set { flow.top10 = array.slice(array.sortBy(shared.scores, "points", true), 0, 10) }

Strings​

set { flow.parts = str.split(callback.data, ":") } // prefix-callback parsing
set { flow.slug = str.slugify("My Cool Title") } // "my-cool-title"
set { flow.safe = str.mask(user.card, 4) } // mask the middle

Crypto β€” signing, auth, and secure storage​

// Sign your own outgoing request (secret from global.*)
set { flow.sig = crypto.hmacSha256(flow.payload, global.api_secret) }
set { flow.hash = crypto.sha256(flow.token) }
set { flow.b64 = crypto.base64Encode(flow.raw) }

// Authenticate a Telegram Mini App before trusting its data
set { flow.ok = crypto.verifyInitData(web_app.data, global.bot_token) }

// Encrypt a user's API key at rest, decrypt on demand
set { user.enc_key = crypto.aesEncrypt(flow.api_key, global.enc_key) }
set { flow.api_key = crypto.aesDecrypt(user.enc_key, global.enc_key) }

Money β€” decimal-safe carts and splits​

set { flow.total = money.add(money.mul(9.99, flow.qty), flow.shipping) }
set { flow.label = money.format(flow.total, "USD") } // "$31.97"
set { flow.shares = money.allocate(100, [1, 1, 1]) } // [33.34, 33.33, 33.33]

Regex β€” validation and extraction​

when regex.test(flow.input, "^[^@]+@[^@]+\\.[^@]+$") {
send.text("Looks like a valid email.")
}
set { flow.numbers = regex.findAll(flow.text, "[0-9]+") }

Ids, time, and JSON​

set { flow.order_id = id.uuid() }
set { flow.today = time.date() } // "2026-06-08"
set { flow.until = time.format(time.unix(user.expires_ts), "YYYY-MM-DD") }
set { flow.data = json.parse(http_response_body) }
set { flow.value = json.path(flow.data, "data.user.id") }

Complete reference​

Every namespace and function:

math​

FunctionReturnsDescription
math.abs(number:NUM)NUMAbsolute value
math.avg(arr:ARRAY<ANY>)NUMMean of a number array; 0 for empty
math.ceil(number:NUM)NUMRound up to nearest integer
math.clamp(n:NUM,lo:NUM,hi:NUM)NUMClamp n to [lo, hi]
math.currency(n:NUM,...sym?:STR)STRFormat number as currency, e.g. math.currency(9.5, "$") = "$9.50"
math.factorial(n:NUM)NUMn! (0 for negative input)
math.floor(number:NUM)NUMRound down to nearest integer
math.format(n:NUM,decimals:NUM)STRFormat number to fixed decimals, e.g. math.format(3.14159, 2) = "3.14"
math.gcd(a:NUM,b:NUM)NUMGreatest common divisor
math.greaterThan(a:NUM,b:NUM)BOOLTrue if a > b
math.greaterThanOrEqual(a:NUM,b:NUM)BOOLTrue if a >= b
math.hypot(a:NUM,b:NUM)NUMHypotenuse sqrt(aΒ²+bΒ²)
math.lcm(a:NUM,b:NUM)NUMLeast common multiple
math.lessThan(a:NUM,b:NUM)BOOLTrue if a < b
math.lessThanOrEqual(a:NUM,b:NUM)BOOLTrue if a <= b
math.log(number:NUM)NUMNatural logarithm
math.log10(number:NUM)NUMBase-10 logarithm
math.log2(number:NUM)NUMBase-2 logarithm
math.max(a:NUM,b:NUM)NUMMaximum of two values
math.min(a:NUM,b:NUM)NUMMinimum of two values
math.mod(a:NUM,b:NUM)NUMModulo (a % b)
math.percentage(part:NUM,whole:NUM)NUMWhat percent part is of whole (0 when whole is 0)
math.pow(base:NUM,exp:NUM)NUMPower (base^exp) β€” uses math.Pow; correct for floats and negatives
math.random()NUMRandom float in [0.0, 1.0)
math.randomInt(min:NUM,max:NUM)NUMRandom int in [min, max] inclusive
math.round(number:NUM)NUMRound to nearest integer
math.roundTo(n:NUM,decimals:NUM)NUMRound to N decimal places
math.sign(number:NUM)NUMSign of n: -1, 0, or 1
math.sqrt(number:NUM)NUMSquare root
math.sum(arr:ARRAY<ANY>)NUMSum of a number array
math.truncate(number:NUM)NUMTruncate toward zero

str​

FunctionReturnsDescription
str.capitalize(text:STR)STRUppercase the first character only
str.charAt(text:STR,index:NUM)STRCharacter at index i ('' if out of range)
str.contains(text:STR,search:STR)BOOLTrue if text contains search substring
str.endsWith(text:STR,suffix:STR)BOOLTrue if text ends with suffix
str.escapeHtml(text:STR)STREscape &, <, > for Telegram HTML parse_mode
str.escapeMarkdown(text:STR,...v2?:BOOL)STREscape Telegram MarkdownV2 specials (pass false for legacy Markdown). Prevents parse errors in dynamic text.
str.extractHashtags(text:STR)ARRAYAll #hashtags in a string
str.extractMentions(text:STR)ARRAYAll @usernames in a string
str.extractUrls(text:STR)ARRAYAll http(s) URLs in a string
str.fields(text:STR)ARRAYSplit a string by whitespace into an array of words
str.includes(text:STR,search:STR)BOOLTrue if text contains the substring (alias of contains)
str.indexOf(text:STR,search:STR)NUMIndex of first occurrence of search in text, -1 if not found
str.isEmpty(text:STR)BOOLTrue if the string is empty or whitespace-only
str.isNumber(value:ANY)BOOLTrue if the value is numeric
str.join(array:ARRAY<ANY>,separator:STR)STRJoin an array of values into a string with separator
str.lastIndexOf(text:STR,search:STR)NUMIndex of the last occurrence of search, -1 if not found
str.len(value:STR)NUMUnicode character count of a string
str.levenshtein(a:STR,b:STR)NUMEdit distance between two strings (fuzzy match, near-dupe detection)
str.lower(text:STR)STRLowercase a string
str.mask(text:STR,visible:NUM)STRShow first/last visible chars, mask the middle with β€’ (e.g. card/secret display)
str.normalizeConfusables(text:STR)STRFold Cyrillic/Greek homoglyphs to Latin (anti-spam evasion), e.g. 'Ρ€Π°ypal' β†’ 'paypal'
str.padLeft(text:STR,length:NUM,...pad?:STR)STRLeft-pad a string to a minimum length
str.padRight(text:STR,length:NUM,...pad?:STR)STRRight-pad a string to a minimum length
str.printf(format:STR,...args:ANY)STRFormat a string with %-verbs (Go fmt.Sprintf)
str.repeat(text:STR,n:NUM)STRRepeat a string n times
str.replace(text:STR,old:STR,new:STR)STRReplace all occurrences of old with new
str.replaceFirst(text:STR,old:STR,new:STR)STRReplace the first occurrence of old with new
str.reverse(text:STR)STRReverse a string (Unicode-aware)
str.similarity(a:STR,b:STR)NUMSimilarity ratio 0–1 (1 = identical). Fuzzy command/answer matching.
str.slugify(text:STR)STRLowercase URL slug: keep [a-z0-9], spaces/underscores β†’ single dash
str.split(text:STR,separator:STR)ARRAYSplit a string by separator
str.startsWith(text:STR,prefix:STR)BOOLTrue if text starts with prefix
str.stripEmoji(text:STR)STRRemove emoji and pictographs (normalize display names)
str.substr(text:STR,start:NUM,length:NUM)STRExtract a substring by start index and length
str.template(tpl:STR,data:OBJECT)STRFill a template with {key} placeholders from an object β€” data-driven messages/broadcasts
str.titleCase(text:STR)STRCapitalize the first letter of every word
str.toNumber(text:STR)NUMParse a string to a number (0 if not numeric)
str.trim(text:STR)STRTrim leading and trailing whitespace
str.trimEnd(text:STR)STRTrim trailing whitespace
str.trimStart(text:STR)STRTrim leading whitespace
str.truncate(text:STR,n:NUM)STRTruncate to n chars, appending '…' when cut
str.upper(text:STR)STRUppercase a string
str.urlDecode(text:STR)STRDecode a percent-encoded URL string ('' on malformed input)
str.urlEncode(text:STR)STRPercent-encode a string for safe use in a URL query/path (RFC 3986)
str.wordWrap(text:STR,width:NUM)STRWrap text to a maximum line width on word boundaries

array​

FunctionReturnsDescription
array.avg(arr:ARRAY<ANY>)NUMMean of a numeric array; 0 for empty
array.chunk(array:ARRAY<ANY>,size:NUM)ARRAYSplit into chunks of the given size
array.compact(arr:ARRAY<ANY>)ARRAYRemove nil, empty string, empty object, and empty array elements
array.concat(arr1:ARRAY<ANY>,arr2:ARRAY<ANY>)ARRAYConcatenate two arrays
array.count(arr:ARRAY<ANY>,key:STR,val:ANY)NUMCount objects whose field key == val
array.drop(array:ARRAY<ANY>,n:NUM)ARRAYAll elements after the first n
array.find(arr:ARRAY<ANY>,key:STR,val:ANY)ANYFirst object whose field key == val, else null
array.first(array:ARRAY<ANY>)ANYFirst element of an array, or nil if empty
array.flatDeep(array:ARRAY<ANY>)ARRAYRecursively flatten all nested arrays
array.flatten(array:ARRAY<ANY>)ARRAYFlatten one level deep
array.groupBy(array:ARRAY<ANY>,key:STR)OBJECTGroup objects into a map keyed by a field value
array.includes(arr:ARRAY<ANY>,item:ANY)BOOLTrue if array contains item
array.indexOf(array:ARRAY<ANY>,item:ANY)NUMIndex of the first element equal to item, -1 if absent
array.keyBy(array:ARRAY<ANY>,key:STR)OBJECTIndex objects into a map keyed by a field value (last wins)
array.last(array:ARRAY<ANY>)ANYLast element of an array, or nil if empty
array.len(array:ARRAY<ANY>)NUMNumber of elements in an array
array.max(array:ARRAY<ANY>)NUMMaximum of a numeric array (0 for empty)
array.min(array:ARRAY<ANY>)NUMMinimum of a numeric array (0 for empty)
array.pluck(arr:ARRAY<ANY>,key:STR)ARRAYExtract key field from each object in the array
array.range(start:NUM,end:NUM,...step?:NUM)ARRAYNumbers from start (incl) to end (excl), optional step
array.reject(arr:ARRAY<ANY>,key:STR,val:ANY)ARRAYAll objects whose field key != val (filter drop)
array.reverse(array:ARRAY<ANY>)ARRAYReverse an array
array.slice(array:ARRAY<ANY>,start:NUM,end:NUM)ARRAYExtract a slice from start to end (exclusive). Negative indices count from the end.
array.sortBy(arr:ARRAY<ANY>,keys:ANY,...desc?:BOOL)ARRAYSort array of objects by key(s). keys: string (single field/path) or [{key,desc?}] for multi-key.
array.sortNums(arr:ARRAY<ANY>,...desc?:BOOL)ARRAYSort a number array ascending; pass true for descending
array.sum(arr:ARRAY<ANY>)NUMSum of a numeric array
array.take(array:ARRAY<ANY>,n:NUM)ARRAYFirst n elements
array.unique(array:ARRAY<ANY>)ARRAYRemove duplicate values, preserving order
array.where(arr:ARRAY<ANY>,key:STR,val:ANY)ARRAYAll objects whose field key == val (filter keep)
array.zip(arr1:ARRAY<ANY>,arr2:ARRAY<ANY>)ARRAYPair elements of two arrays by index β†’ [[a0,b0],...]

object​

FunctionReturnsDescription
object.entries(object:OBJECT)ARRAYArray of {key, value} pairs
object.fromEntries(entries:ARRAY<ANY>)OBJECTBuild an object from an array of {key, value} pairs
object.get(object:OBJECT,key:STR,...default?:ANY)ANYRead a key with an optional default
object.hasKey(object:OBJECT,key:STR)BOOLTrue if key exists in object
object.isEmpty(object:OBJECT)BOOLTrue if the object has no keys
object.keys(object:OBJECT)ARRAYReturn the keys of an object
object.len(object:OBJECT)NUMNumber of keys in an object
object.merge(obj1:OBJECT,obj2:OBJECT)OBJECTMerge two objects; obj2 overrides obj1
object.omit(object:OBJECT,...keys:STR)OBJECTNew object removing the listed keys
object.pick(object:OBJECT,...keys:STR)OBJECTNew object keeping only the listed keys
object.values(object:OBJECT)ARRAYReturn the values of an object

json​

FunctionReturnsDescription
json.isValid(json_string:STR)BOOLTrue if the string is valid JSON
json.parse(json_string:STR)ANYParse a JSON string into a value
json.path(object:ANY,path:STR)ANYRead a dotted path into a parsed value (e.g. "data.user.id")
json.stringify(value:ANY)STRConvert any value to a JSON string

time​

FunctionReturnsDescription
time.add(t:ANY,dur:STR)ANYAdd a duration to a Carbon. Supports "24h", "7d", "3w", "2month", "1year".
time.after(t1:ANY,t2:ANY)BOOLTrue if t1 is chronologically after t2
time.before(t1:ANY,t2:ANY)BOOLTrue if t1 is chronologically before t2
time.carbon(...t?:ANY)ANYAlias for time.from(). Also callable as top-level carbon().
time.date(...format?:STR)STRCurrent date formatted as string (default: YYYY-MM-DD)
time.diff(t1:ANY,t2:ANY,...unit?:STR)NUMDifference t1 βˆ’ t2 in the given unit (s/m/h/d/w/month/year)
time.endOf(t:ANY,unit:STR)ANYEnd of the given unit (second/minute/hour/day/week/month/quarter/year)
time.format(t:ANY,format:STR)STRFormat a Carbon/time as a string. Supports Carbon layouts, and aliases (RFC3339, YYYY-MM-DD).
time.formatDuration(seconds:NUM)STRFormat total seconds into a human-readable string, e.g. "2d 3h 14m 5s"
time.from(t:ANY)ANYConvert any time-like value (timestamp, string, time.Time) to a Carbon.
time.humanize(t:ANY)STRHuman-friendly relative time, e.g. "2 hours ago" / "in 3 days"
time.now()ANYCurrent date/time as a chainable Carbon. See: https://github.com/golang-module/carbon
time.parse(str:STR,...fmt?:STR)ANYParse a date/time string into a Carbon. Returns zero Carbon on failure β€” never silently falls back to now().
time.startOf(t:ANY,unit:STR)ANYStart of the given unit (second/minute/hour/day/week/month/quarter/year)
time.sub(t:ANY,dur:STR)ANYSubtract a duration from a Carbon. Supports "24h", "7d", "3w", "2month", "1year".
time.timestamp()NUMCurrent Unix timestamp (seconds)
time.timezone(t:ANY,tz:STR)ANYReturn a copy of the Carbon shifted to the given IANA timezone (e.g. "Asia/Kolkata")
time.unix(epoch:NUM)ANYCreate a Carbon from a Unix timestamp (seconds)

types​

FunctionReturnsDescription
types.bool(value:ANY)BOOLConvert to boolean
types.float(value:ANY)NUMConvert to float
types.int(value:ANY)NUMConvert to integer
types.isArray(value:ANY)BOOLTrue if value is an array
types.isBool(value:ANY)BOOLTrue if value is a boolean
types.isNil(value:ANY)BOOLTrue if value is nil
types.isNumber(value:ANY)BOOLTrue if value is numeric
types.isObject(value:ANY)BOOLTrue if value is an object/map
types.isString(value:ANY)BOOLTrue if value is a string
types.string(value:ANY)STRConvert to string
types.typeOf(value:ANY)STRRuntime type name: nil/bool/number/string/array/object

util​

FunctionReturnsDescription
util.coalesce(...values:ANY)ANYReturn the first non-nil, non-empty argument
util.default(value:ANY,fallback:ANY)ANYReturn fallback if value is nil or empty string
util.eval(expression:STR)NUMAlias for util.evaluate
util.evaluate(expression:STR)NUMSafely evaluate an arithmetic expression string (sandboxed: numbers + math only, no variables)
util.inEnum(value:ANY,...allowed:ANY)BOOLAlias for util.isEnum
util.isEnum(value:ANY,...allowed:ANY)BOOLTrue if value equals one of the allowed values
util.len(value:ANY)NUMUnified length: string (Unicode chars), array (elements), object (keys)

crypto​

FunctionReturnsDescription
crypto.base64Decode(input:STR)STRDecode a standard base64 string ('' on invalid input)
crypto.base64Encode(input:STR)STRBase64-encode a string (standard encoding)
crypto.base64UrlDecode(input:STR)STRDecode an unpadded base64url string ('' on invalid input)
crypto.base64UrlEncode(input:STR)STRBase64url-encode (unpadded) β€” safe for URLs/JWT segments
crypto.hexDecode(input:STR)STRDecode a hex string ('' on invalid input)
crypto.hexEncode(input:STR)STRHex-encode a string
crypto.hmacSha256(message:STR,key:STR)STRHMAC-SHA256 of message with key, hex-encoded. Use to sign/verify webhooks & API requests.
crypto.hmacSha256Base64(message:STR,key:STR)STRHMAC-SHA256 of message with key, base64-encoded (some APIs require base64 signatures)
crypto.hmacSha512(message:STR,key:STR)STRHMAC-SHA512 of message with key, hex-encoded
crypto.md5(input:STR)STRMD5 hash of a string, hex-encoded (non-cryptographic; for checksums/etags only)
crypto.randomToken(bytes:NUM)STRCryptographically-random hex token of n bytes (default 16, max 256). Use for secrets/nonces.
crypto.sha256(input:STR)STRSHA-256 hash of a string, hex-encoded
crypto.sha512(input:STR)STRSHA-512 hash of a string, hex-encoded
crypto.aesEncrypt(text:STR,key:STR)STRAES-256-GCM encrypt (key = sha256(passphrase)); base64 output. Store secrets in user.*/shared.* at rest.
crypto.aesDecrypt(text:STR,key:STR)STRDecrypt an AES-256-GCM base64 blob ('' on failure/tamper)
crypto.totp(secret:STR,...period?:NUM)STRCurrent TOTP 2FA code (RFC 6238, 6 digits) for a base32 secret. Optional period (default 30s).
crypto.totpVerify(secret:STR,code:STR,...window?:NUM)BOOLVerify a user-entered TOTP code, allowing Β±window steps (default 1)
crypto.jwtSign(claims:OBJECT,secret:STR,...alg?:STR)STRSign a JWT (HS256 default, or HS512). Add an exp claim (unix seconds) for expiry.
crypto.jwtVerify(token:STR,secret:STR)OBJECTVerify a JWT signature + expiry. Returns { valid: Boolean, claims: Object }.
crypto.verifyInitData(initData:STR,botToken:STR)BOOLVerify a Telegram Mini App initData string against the bot token. Gate every Mini App login with this.
crypto.constantTimeEqual(a:STR,b:STR)BOOLConstant-time string compare (timing-attack-safe) for secrets/tokens

regex​

FunctionReturnsDescription
regex.count(input:STR,pattern:STR)NUMNumber of non-overlapping matches
regex.findAll(input:STR,pattern:STR)ARRAYAll substrings matching the pattern
regex.groups(input:STR,pattern:STR)ARRAYSubmatch groups of the first match (index 0 = whole match)
regex.match(input:STR,pattern:STR)STRFirst substring matching the pattern, or '' if none/invalid
regex.replace(input:STR,pattern:STR,replacement:STR)STRReplace all matches; replacement supports $1, ${name} backrefs
regex.split(input:STR,pattern:STR)ARRAYSplit a string by a regex pattern
regex.test(input:STR,pattern:STR)BOOLTrue if the string matches the pattern (RE2 syntax). Invalid pattern β†’ false.

id​

FunctionReturnsDescription
id.nanoid(...size?:NUM)STRURL-safe random id (default length 21)
id.short()STRShort URL-safe random id (8 chars)
id.uuid()STRRandom UUID v4 string

money​

Decimal-safe money math β€” no float drift. Use for carts, balances, and bill-splitting.

FunctionReturnsDescription
money.add(a:NUM,b:NUM)NUMAdd two money amounts without float drift (cent-precise)
money.sub(a:NUM,b:NUM)NUMSubtract money amounts (cent-precise)
money.mul(amount:NUM,factor:NUM)NUMMultiply a money amount by a factor (rounded to cents)
money.div(amount:NUM,divisor:NUM)NUMDivide a money amount (rounded to cents; 0 on divide-by-zero)
money.format(amount:NUM,currency:STR)STRFormat with a currency symbol, e.g. money.format(9.5, "USD") β†’ "$9.50"
money.parse(str:STR,currency:STR)NUMParse a money string to a number, stripping symbols/separators
money.allocate(total:NUM,ratios:ARRAY<NUM>)ARRAYSplit a total into parts by ratio so the parts sum EXACTLY to the total (no lost cents)

geo​

FunctionReturnsDescription
geo.distance(lat1:NUM,lon1:NUM,lat2:NUM,lon2:NUM)NUMGreat-circle distance in km between two lat/lon points (haversine). Nearest-store / radius checks.
geo.bearing(lat1:NUM,lon1:NUM,lat2:NUM,lon2:NUM)NUMInitial compass bearing in degrees (0–360) from point 1 to point 2

random​

For games, giveaways, and raffles.

FunctionReturnsDescription
random.pick(array:ARRAY<ANY>)ANYRandom element from an array (nil if empty)
random.shuffle(array:ARRAY<ANY>)ARRAYA shuffled copy of an array
random.sample(array:ARRAY<ANY>,n:NUM)ARRAYn random elements without replacement (raffle winners)
random.weighted(items:ARRAY<ANY>,weights:ARRAY<NUM>)ANYRandom element chosen by weights (loot tables, odds)

csv​

FunctionReturnsDescription
csv.parse(text:STR,...delimiter?:STR)ARRAYParse CSV text (first row = header) into an array of objects. Optional delimiter.
csv.stringify(rows:ARRAY<OBJECT>)STRSerialize an array of objects to CSV text (sorted header from the first row)
note

Many builtins also have a flat alias (e.g. len() for util.len(), round() for math.round()). The namespaced form shown above is the recommended, unambiguous style.

Next​