Conditions and timing
Any step of a FlaviBot autoresponder can carry conditions that decide whether it runs, and the flow actions can pause a rule, for a few seconds or for up to 30 days. This page covers both.
Conditions on a step
Any step can carry up to 10 conditions. Choose whether all of them or any of them must hold, and what happens when they do not: skip just this step, or stop the workflow.
Available checks:
| Check | Use it for |
|---|---|
| has role / missing role | staff-only branches |
| in channel / not in channel | narrow a rule further |
| is member / is not member | one person, or everyone but them |
| variable comparison | =, ≠, contains, exists, and >, ≥, <, ≤ |
| random chance | run a step only N% of the time |
| group | several checks with their own all or any |
A group lets one step say "has the Staff role and (is in #mod-log or the strike count is at least 3)": the group counts as one condition of the step, and holds up to 10 of its own. Groups go three levels deep, counting the step's own conditions as the first, and one step holds 25 checks in all.
The role, channel and member checks take a variable as well as a fixed pick.
{steps.1.role_id} checks the role an earlier step created, and
{vars.log_channel} a channel a data step read back. A variable that turns
out empty, or holds something that is not a role, channel or member, makes the
check fail both ways: "missing role" and "not in channel" never let everyone
through because a value was missing.
The numeric comparisons are what make counters useful:
2. Add 1 to the strike counter → {steps.2.value}
3. Warn the member
4. Time them out only if {steps.2.value} ≥ 3
5. Reset the counter only if {steps.2.value} ≥ 3
Both halves of a comparison accept variables, not just the left one. So you can check a value against another value instead of against a constant:
2. Read the member's warning count → {vars.strikes}
3. Read the server's warning limit → {vars.limit}
4. Time them out only if {vars.strikes} ≥ {vars.limit}
A token on the right that does not resolve is left as written rather than treated as empty, so a typo shows up as a comparison that never matches instead of one that quietly matches everything. "Contains" with an empty right side matches nothing, for the same reason.
To choose between several paths, add a branch: an if, up to nine else ifs and an else, each with its own steps. The first test that passes wins and only its steps run. A test holds conditions and groups like a step does. Any path of a branch can hold one more branch of its own, for "if staff, then: if the strike count is at least 3 … otherwise …".
A condition that does not hold is a normal outcome, not an error: the run is still a success.
Waiting
Three flow actions:
- Wait: pause up to 60 seconds inside the run
- Wait, cancellable: park everything below it for up to 30 days under a key you choose
- Cancel: cancel whatever is parked under that key
The cancellable wait is durable: the schedule lives in the database, not in memory, so a restart in the middle changes nothing.
The key is what makes it safe. Use honeypot:{user.id} and each member's
timer is separate: cancelling only ever affects your own.
That pattern is the whole "second chance" idea:
1. Delete the message
2. Post a card with an "it was a mistake" button
3. Wait 5 minutes, cancellable, key honeypot:{user.id}
4. Ban ← only reached if nobody cancelled
A second rule on that button cancels the same key. Click in time and step 4 never happens.
What survives the wait
A cancellable wait ends the run and starts a fresh one when the timer fires, so
what the second half can see is decided at the moment you park it. Carried
over: your trigger variables ({user.mention}, {server.name}, …), anything
you stored in a {vars.…} variable, the user, channel and message the rule
fired on, and the {steps.N.field} results of the earlier steps that your
parked steps actually read. So this works:
1. Send message → {steps.1.message_id}
2. Wait 5 minutes, cancellable
3. Edit message {steps.1.message_id} ← still resolves
Only what the steps below the wait mention is kept, so nothing you do not use
is stored. If those results add up to more than 16 KB the step refuses to park
with deferred_state_too_large instead of quietly dropping half of them; the
realistic way to hit that is a large HTTP response, and the fix is to pull out
the one field you need first:
1. Call HTTP endpoint into {vars.api}
2. Set variable id = {vars.api.data.id}
3. Wait 1 hour, cancellable
4. Send message using {vars.id}
Two things are deliberately not carried. The interaction token, because Discord expires it after 15 minutes, so a reply to the click that started the rule cannot work an hour later. And the member's role list, because a snapshot taken an hour ago could make a moderation step act on roles they no longer have.
Steps after the wait hand values to each other normally: they all run in the same second pass.
