Skip to main content
Variables give you values. Operators let you do something with them. There are two shapes:
Pipes read left-to-right and are usually what you want for formatting. Operators are for logic.

Conditionals

{if: condition && then && else} — the else half is optional.
{unless: ...} is the same thing inverted. Aliases: ifnot, ifn, not.

Comparisons

Numbers are compared numerically when both sides look like numbers, and as text otherwise.

Combining conditions

and / & and or / | both work, and and binds tighter than or — the same as most languages. Prefix any single condition with ! to negate it.

Truthiness

A bare value with no comparison is checked for truthiness:
These all count as false: empty, 0, false, none, null, never, permanent, forever, no reason provided, n/a, nothing, nobody, nowhere. That last group exists so {if: {reason} && ...} behaves how you’d expect when no reason was given.

Text formatting

Numbers

plural also takes explicit words:

Arithmetic

Randomness

random aliases: rand, r, choice. rng aliases: randint, ri, random.randint. repeat aliases: mul, multiply. Randomness is evaluated each time the message is sent, so a welcome message with {random: ...} in it varies per member — which is the point.

Boolean helpers

Two pipes exist purely to feed {if:}:

Worked example

A level-up message that reads differently at milestones, formats numbers properly and picks a random opener:
Operators are resolved after variables. So {if: {user.level}>=10 && ...} works because {user.level} has already become a number by the time the if runs. If a variable doesn’t exist, its raw text is what gets compared — which is usually falsy, but worth knowing when a condition behaves oddly.

Nesting

Operators nest freely, and && is only treated as a separator at the top level — so an inner operator’s own && won’t break the outer one.
Keep it readable. If a script starts looking like code, it’s usually a sign the message wants splitting into two.