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