Skip to content

Write conditions

An automation has no separate condition form: the condition is an ordinary if in run(ctx). A module that reaches no action simply does nothing for that event.

If you write no condition, the automation acts on every occurrence of its trigger.

  1. Read what you need from the run context: ctx.event for the event that started the run, ctx.player for the subject player, ctx.input for button inputs, ctx.server for the server.
  2. Compare it with if, and return when the run should not act.
async run(ctx) {
if (ctx.event.channel !== 'ChatTeam') return
if (!ctx.event.message.includes('!voteban')) return
await ctx.note('Asked for a voteban')
}

The fields available on ctx.event depend on the event in the trigger; see Squad events.

Use && when all of several conditions must hold and || when any one is enough. For example, a message that contains !ban or !voteban, sent on the team channel:

const asksForBan = ctx.event.message.includes('!ban') || ctx.event.message.includes('!voteban')
if (ctx.event.channel === 'ChatTeam' && asksForBan) {
await ctx.note('Asked for a ban')
}

Because the condition is code, one run can take different actions on different branches, which a single filter could not express: warn below a limit and ban at it, as in the counter example below.

Some player facts are not part of the event; they are read on demand, with a call: await ctx.player.bansCount(), await ctx.player.country(). A read makes one query the first time it is awaited and is cached until the module performs an action, so a module that never calls it pays nothing. All values come back as strings: convert with Number(...) before comparing. The full list is in the Run context reference.

A counter is how you write “the N-th time in a window”. Declare it in the header counters — a name, an event to count and a window — and read it in run as ctx.counter.<name>:

counters: { tk: { event: 'teamkill', per: 'actor', window: '1h' } },
async run(ctx) {
if (ctx.counter.tk < 7) {
await ctx.rcon('AdminWarn "..." Stop teamkilling.')
return
}
await ctx.ban({ for: '1h', reason: 'Teamkilling', servers: 'all' })
},

The comparison is numeric, so <, <=, > and >= are the operators that make sense on a counter.

A counter keeps no state of its own — the number is recomputed from event history before the run — so it survives a restart, and no reset step exists or is needed. Declaring one is walked through step by step in Create an event automation.

Use a JavaScript RegExp: /^!(ban|kick)\b/i.test(ctx.event.message). Matching flags, including case-insensitivity, are whatever you write in the literal; heliana does not rewrite the pattern. A run that takes too long is stopped by the limits, so keep patterns simple.