Skip to main content

Utility Libraries

Helper libraries for everyday work — formatting, numbers, validation, and small no-key utilities. Most are pure (no network); a few (prices, shorten, fx_rates) call a free public API, so attach on error { }. Import and call like any library:

import format from "format"

let { result } = await format.money(amount: 9.5, symbol: "$") // "$9.50"

units — crypto unit conversion​

Pairs with apirone. Converts between display units and smallest units.

SubflowSignature → outputs
satoshi_to_btc(sats) → {btc, display}
btc_to_satoshi(btc) → {sats}
sun_to_trx(sun) → {trx, display}
trx_to_sun(trx) → {sun}
format_crypto(amount, decimals, symbol) → {result}
fee_pct(amount, pct) → {fee, net}
shorten_address(addr, edge) → {result}
import units from "units"

let { display } = await units.satoshi_to_btc(sats: 50000000)
send.text(`You received ${display} BTC`)

math_x — advanced numeric helpers​

Beyond the math.* builtins. Pure arithmetic.

SubflowSignature → outputs
percentage(part, whole) → {pct}
percent_of(value, pct) → {result}
round_to(value, decimals) → {result}
safe_divide(a, b) → {result} (0 when b is 0)
lerp(a, b, t) → {result}
ratio(a, b) → {result}
avg3 / sum3 / min3 / max3(a, b, c) → {result}
tip(amount, tip_pct) → {tip_amount, total}
discount(price, discount_pct) → {saved, final}
simple_interest(principal, rate_pct, years) → {interest, total}
compound_interest(principal, rate_pct, years) → {total, interest}
import math_x from "math_x"

let { tip_amount, total } = await math_x.tip(amount: 42, tip_pct: 18)
send.text(`Tip: ${tip_amount}, total: ${total}`)

validate — input validation​

Predicate helpers (best-effort, no regex engine). All pure.

SubflowSignature → outputs
is_empty(s) → {result}
is_number(s) → {result}
in_range(n, min, max) → {result}
length_between(s, min, max) → {result}
matches_prefix(s, prefix) → {result}
is_email(s) → {result}
is_url(s) → {result}
is_phone(s) → {result}
email_check(s) → {valid, reason}
import validate from "validate"

let { valid, reason } = await validate.email_check(s: flow.input)
when !valid { send.text(`Invalid email: ${reason}`) }
note

is_email, is_url, and is_phone are best-effort heuristics — they catch obvious mistakes but aren't RFC-compliant. For stricter checks use regex.test (see Security & validation).

format — display formatting​

SubflowSignature → outputs
money(amount, symbol) → {result} (e.g. "$9.50")
percent(ratio) → {result} (0–1 → "12.3%")
truncate(s, n) → {result}
pad_number(n, width) → {result} (e.g. "0007")
yes_no(b) → {result} ("Yes"/"No")
pluralize(n, singular, plural) → {result}
ordinal(n) → {result} ("1st", "22nd")
import format from "format"

let { result } = await format.pluralize(n: user.points, singular: "point", plural: "points")
send.text(`You have ${user.points} ${result}`)

text — string utilities​

SubflowSignature → outputs
slugify(input) → {result}
clamp(value, min, max) → {result}
capitalize(s) → {result}
strip_prefix(s, prefix) → {result}
ensure_suffix(s, suffix) → {result}
word_count(s) → {count}
mask_secret(s, visible) → {result}
initials(full_name) → {result}
title_case(s) → {result}

datetime — date/time helpers​

Named wrappers around time.*. Time values are opaque.

SubflowSignature → outputs
now_unix() → {ts}
format_date(t, fmt) → {result}
days_between(t1, t2) → {days}
add_duration(t, dur) → {result}
is_past(t) → {result}
age_from(birthdate) → {years}
humanize_seconds(sec) → {result}
import datetime from "datetime"

let { result } = await datetime.add_duration(t: time.unix(user.start_ts), dur: "30d")
let { days } = await datetime.days_between(t1: result, t2: time.now())
send.text(`${days} days left`)

antispam — spam heuristics​

Pure message-content checks (no network, no state). Score a single message; your bot owns the rate/flood state in shared.* vars.

SubflowSignature → outputs
check(text, max_links, max_mentions) → {spam, score, reason}
normalize(text) → {clean} (folds confusables, lowercases)
count_links(text) → {count}
matches_blocklist(text, pattern) → {blocked} (regex pattern)
looks_similar(a, b, threshold) → {similar, score} (fuzzy near-dupe)
import antispam from "antispam"

flow guard on message [text] {
let { spam, reason } = await antispam.check(text: message.text, max_links: 2, max_mentions: 5)
when spam {
delete(message)
send.text(`🚫 Removed (${reason})`, chat_id: global.mod_log_id)
}
}

Reference blueprint: group-antispam.botgami.

prices — crypto prices​

Live crypto prices via the CoinGecko public API (no key; rate-limited). coin is a CoinGecko id ("bitcoin", "ethereum"); vs is a fiat/crypto code ("usd", "eur", "btc").

SubflowSignature → outputs
get(coin, vs) → {price, change_24h, symbol, name}
simple(coin, vs) → {price}
import prices from "prices"

flow price on command "/price" {
let { price, change_24h, name } = await prices.get(coin: "bitcoin", vs: "usd") on error {
send.text("Couldn't fetch the price.")
stop
}
send.text(`${name}: $${price} (${change_24h}% 24h)`)
}

shorten — URL shortener​

Shorten links via is.gd (no key).

SubflowSignature → outputs
url(long) → {short}
custom(long, slug) → {short} (custom slug)
import shorten from "shorten"

flow short on command "/short" {
link = ask.text("Send a URL to shorten:")
let { short } = await shorten.url(long: link) on error {
send.text("Couldn't shorten that link.")
stop
}
send.text(`🔗 ${short}`)
}

qr — QR-code images​

Build QR-code image URLs (no key — returns a URL you send with send.photo).

SubflowSignature → outputs
image(data, size) → {url} (QR for arbitrary text, size×size px)
wifi(ssid, password, size) → {url} (QR that joins a Wi-Fi network when scanned)
import qr from "qr"

flow qr on command "/qr" {
let { url } = await qr.image(data: `https://t.me/${bot.username}`, size: 300)
send.photo(url, caption: "Scan to open the bot")
}

See also​