Skip to content

feat(Customizable-Roll-Listener): Rewrite for Beacon sheets - #2363

Open
steverobertsuk wants to merge 35 commits into
Roll20:masterfrom
steverobertsuk:sr/crl-v2-0-0
Open

steverobertsuk wants to merge 35 commits into
Roll20:masterfrom
steverobertsuk:sr/crl-v2-0-0

Conversation

@steverobertsuk

@steverobertsuk steverobertsuk commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

[2.1.0] – 2026-11-01

Added

  • --fields filters on roll template fields, such as --fields subcategory=Attack && title~sword to trigger only on D&D 2024 attack cards for swords. = and != compare the whole value, ~ and !~ look for text in it (a regular expression with --regex on), and case is ignored. The inspector lists each message's fields and says when a field does not match. Asked for by Pak on Discord.
  • --exclude stops a listener when any of its pieces of text appears, such as --text sword --exclude dagger. It is searched like --text, and the inspector says when excluded text was found. Suggested by Pak on Discord.

Changed

  • The README and Help handout explain that success and fail need a target written into the roll, such as 1d20>15, so they never trigger on D&D sheet attacks, saves and checks (use --total for an AC or DC), and that --text also searches the raw content the inspector shows, which for a Beacon card is its HTML.
  • The inspector shortens embedded images in the raw content to their type. The weapon icon on a D&D 2024 attack card used up most of the 3,000 characters shown, hiding attributes such as data-rollSubcategory.

[2.0.1] – 2026-11-01

Fixed

  • Commands sent by other Mods, such as a ScriptCards --@crl| call, are no longer refused with "Only a GM can use Customizable Roll Listener". Roll20 sends them from the player "API", which is not a GM. Their replies are whispered to the GM.

Changed

  • The Help handout says that other Mods can send these commands, in all 24 languages.

[2.0.0] – 2026-10-29

A full rewrite by MidNiteShadow7 of Scott C.'s Customizable Roll Listener, which was reported as working on some chat output but not all, especially since Beacon sheets arrived, and is no longer supported.

Fixed

  • Listeners with a roll result (roll=critical and so on) no longer fail with "CRL ERROR ||| Ability: … was not parseable". Version 1 crashed on any roll containing a modifier, a group or a comment, and on text without ##, then skipped the listener and logged the error on every chat message.
  • A roll result without text now checks the rolls. Version 1 skipped the check, so such listeners triggered on every message using the template.
  • ## finds inline rolls at any position. Version 1 only found the first ten ($[[0]] to $[[9]]), so long roll templates were missed, and only converted the first ## in each piece of text.
  • Text is plain text unless a listener asks for a regular expression. Version 1 used it as a regular expression, so text containing (, +, ?, . and similar characters silently matched something else.
  • Dropped dice (advantage, disadvantage, kh/kl) and rerolled dice no longer count as crits or fumbles.
  • Damage dice rolling their highest face are no longer crits when a d20 was rolled (configurable per listener).
  • A roll exactly on its target is a success and not also a failure.
  • Rollable table results can be matched as text.
  • /roll results (rollresult and gmrollresult messages) are read.
  • Rolls from Beacon sheets such as D&D 2024 are read: they arrive as advancedroll roll cards with no inline rolls or template name, so CRL reads the template, character, title and each roll from the card.
  • Inline rolls and roll templates typed into a !crl command are kept in the listener's commands instead of being rolled once and lost.
  • Multi-line commands are sent one message per line, as a macro would be, instead of as one message.
  • Malformed commands, missing listeners and commands from other mods no longer crash the Mod sandbox; problems are whispered to the GM with a hint.
  • CRL no longer reacts to its own menus, and a listener that keeps triggering itself is switched off instead of looping.
  • Listener names may contain commas, and any field can be cleared.
  • GM cards never spill into public chat. Roll20 whispers only the first line of a message and posts the rest publicly, so listener cards with multi-line commands and the inspector's extra message data used to leak; cards are now kept on one line.
  • Menus no longer go missing: text shown in chat has @{…}, %{…}, [[…]] and ?{…} encoded, because Roll20 drops a mod's whole message when it cannot resolve them.
  • The help no longer says that \\config deletes a listener.
  • Upgrading no longer wipes the mod's state when the schema changes.

Added

  • Chat menus with a card per listener and buttons to change every setting, and a Command Deck handout.
  • Help handout, and documentation (README, TESTING, DEVELOPERS, MOD_FILE_MAP), including how to find a sheet's roll template names, the templates of common sheets and links to Roll20's roll template articles.
  • New !crl <command> --option value syntax. Listeners can be named by name or ID.
  • Filters for message types, speakers / characters (including the character in the roll template) and players.
  • Roll checks for totals and natural die results (>=15, <=2, 19-20), and a choice of which dice to check.
  • Placeholders in commands: ${who}, ${character}, ${player}, ${template}, ${total}, ${natural}, ${result}, ${expression}, ${table}, ${listener} and ${field:name}.
  • Commands can be sent as CRL or as the character who rolled, per listener or as a setting.
  • Per-listener on/off switch, cooldown, ordering and "stop after this one".
  • !crl inspect: explains the next chat message (type, template and fields, every roll with its dice, the text listeners search) and why each listener did or did not trigger. Useful for Beacon sheets such as D&D 2024.
  • !crl test: runs a listener's commands with sample values.
  • Option to ignore chat sent by other mods.
  • Listener rules are mirrored into each ability's description, so listeners survive copying the game.
  • Public API for other mods: CRL.analyse, CRL.matches, CRL.onMatch.
  • Translations into 23 languages, chosen with a language setting.

Changed

  • Text matching ignores upper and lower case.
  • fail means a target roll with no successes.
  • Version 1 listeners are converted automatically; the version 1 \\create, \\edit, \\delete, \\config and \\set commands still work.
  • Source split into ES modules under src/, bundled with Rollup and tested with the Node test runner.

Signed-off-by: Steve Roberts steve@shadowcomputers.co.uk

Version 2 is built from ES modules under src/ in the same way as
Hexploration and TokenController: Rollup bundles them into CRL.js and the
2.0.0/ snapshot, Prettier formats the output, and the scripts handle
version bumps and locale synchronisation.

Signed-off-by: Steve Roberts <steve@shadowcomputers.co.uk>
…n chat

Version 1.1 only matched some chat output. Its "not parseable" errors
were swallowed crashes on rolls with modifiers or text without "##"; its
text was used as a raw regular expression; "##" only found the first ten
inline rolls; dropped dice counted as crits; and Beacon sheets such as
D&D 2024, which post HTML roll cards with their dice in msg.rolls, were
never matched at all.

The rewrite analyses every message (roll templates, plain chat, /roll
results, rollable tables and Beacon advancedroll cards) and matches
listeners on templates, message types, plain or regex text, speakers,
players, crit/fumble/success/fail, totals and natural dice. Commands
support placeholders, can be sent as CRL or the roller, and have
cooldowns, ordering, stop-after and loop protection. An inspector
explains what a message contains and why each listener did or did not
fire.

Commands stay as abilities on the existing store character, and rules
are mirrored into each ability's description so listeners survive a game
copy. Version 1 state, listeners and \create-style commands are
upgraded automatically. Chat text encodes Roll20 syntax, because
sendChat drops a whole message it cannot resolve. Menus, a Help handout
and a Command Deck are translatable; locales other than en-US fall back
to English until they are generated.

Credits Scott C. as the original author and The Aaron for the 1.1 fixes.

Signed-off-by: Steve Roberts <steve@shadowcomputers.co.uk>
…nd-to-end tests

The suites cover message analysis and matching (crits, fumbles, dropped
dice, targets, text, tables, filters), command and version 1 parsing,
placeholders and translation keys, and D&D 2024 checks, attacks, damage
and advantage built from cards captured in a live Jumpgate game. The
end-to-end suite drives the built bundle in a stubbed Roll20 sandbox,
including the version 1 upgrade, game copies and loop protection.

Signed-off-by: Steve Roberts <steve@shadowcomputers.co.uk>
Adds a README for GMs, a changelog of what version 2 fixes and adds, a
developer guide to the data model and matching, a file map, and a
testing guide with the manual checklist and the results from the live
D&D 2024 test game.

Signed-off-by: Steve Roberts <steve@shadowcomputers.co.uk>
Adds the bundled CRL.js for the One-Click library and its identical
2.0.0/ snapshot.

Signed-off-by: Steve Roberts <steve@shadowcomputers.co.uk>
…ames

A user reading the README could not find which roll template names to
use with --templates. The README and the Help handout now explain what a
template name is and how to find one (the inspector, a sheet's macro or
the sheet's source), list the templates of the built-in default, D&D 5E
by Roll20 (2014) and D&D 2024 sheets, and link to Roll20's roll template
articles. The templates prompt and option description point to them.

Signed-off-by: Steve Roberts <steve@shadowcomputers.co.uk>
Each version heading now shows its release date, taken from the
repository history, in the same style as the other mods' changelogs.

Signed-off-by: Steve Roberts <steve@shadowcomputers.co.uk>
…s a table

The supported languages were one long comma-separated paragraph, which was
hard to scan. They are now a table with the code to use in
!crl config language, the language's English name and its native name.

Signed-off-by: Steve Roberts <steve@shadowcomputers.co.uk>
…nguages table

Each language row now starts with its flag, taken from the locale
metadata, so a language is quicker to spot. The flag's name is shown on
hover.

Signed-off-by: Steve Roberts <steve@shadowcomputers.co.uk>
…images

Roll20 does not draw flag emoji in handouts (Windows shows them as
letters), so the flags were missing. Each flag is now its Twemoji SVG
image, as ConditionTracker does, with the flag's name as alt text and
title.

Signed-off-by: Steve Roberts <steve@shadowcomputers.co.uk>
Roll20 whispers only the first line of a message and posts every later
line publicly. A listener card whose commands span several lines, and
the inspector's extra message data (pretty-printed JSON), therefore
leaked to the players; found while testing a real version 1 game with
MetaScriptToolbox installed. Code blocks now use <br> for line breaks,
and every card is kept on one line before it is sent.

Signed-off-by: Steve Roberts <steve@shadowcomputers.co.uk>
Tested in a Legacy game holding 47 real version 1 listeners: the upgrade
kept every listener's templates, text, roll checks and commands, and the
D&D 5E 2014 sheet checks passed. The sheet's NPC attacks use the
npcfullatk template, which Roll20's help article does not list, so it is
added to the template tables. The README and Help handout now explain
that the sheet rolls advantage and disadvantage as two separate d20s,
both of which CRL checks.

Signed-off-by: Steve Roberts <steve@shadowcomputers.co.uk>
Every locale now holds all 280 strings. Nine files had only their
handout translated, or none of it, and the rest were machine
translations with literal errors ("die" as dying, "roll" as bread
rolls, "speaker" as a loudspeaker) and with commands, options and
typed values translated, so pasted examples would not work. Each file
was translated or reviewed in full, with !crl commands, --options,
placeholders and values such as crit and on kept in English, and the
missing languages-table headings and 2014 sheet advantage note added.
The README, changelog and developer notes describe the translations.

Signed-off-by: Steve Roberts <steve@shadowcomputers.co.uk>
Commands that another Mod sends with sendChat, such as a ScriptCards
--@ call, reach the Mod from the player "API". playerIsGM() is false for
that player, so every such command was refused with "Only a GM".

Messages from the API player are now accepted, and their replies are
whispered to the GM.

Signed-off-by: Steve Roberts <steve@shadowcomputers.co.uk>
Bumps the version to 2.0.1 for accepting commands sent by other Mods.
It goes into the same pull request as 2.0.0, so 2.0.0 joins the
previous versions and its snapshot folder is left as it was.

The rebuilt script is copied to the new 2.0.1 folder, and the
changelog, README, testing checklist, developer notes and the startup
test carry the new version. The testing checklist gains a check for a
command sent by another Mod.

Signed-off-by: Steve Roberts <steve@shadowcomputers.co.uk>
…Mods can send commands

The README gained this in the 2.0.1 fix, but the handout still said
every command is GM only. The sentence has its own key so the existing
translations of the intro stay as they are.

Signed-off-by: Steve Roberts <steve@shadowcomputers.co.uk>
… sentence into 23 languages

Only the new commands.mods sentence is added. sync-locales also re-sent
strings that are meant to stay as they are, such as CRL, the example
commands and the seconds unit, so those changes were dropped. The
machine translations were checked: Spanish, Ukrainian and Chinese made
the GM a director or general manager, Hebrew used the word for bicycles,
and Japanese, Korean and Turkish dropped the s from ScriptCards; these
were corrected by hand. Note the handout change in the changelog.

Signed-off-by: Steve Roberts <steve@shadowcomputers.co.uk>
…t's other-Mods sentence

Signed-off-by: Steve Roberts <steve@shadowcomputers.co.uk>
… locales to sync-locales

sync-locales used to re-send every string still identical to English on
each run. Many are meant to stay English, so Hex (V) and Hex (H) turned
into hexadecimal and CRL into a certificate revocation list. It also
needed hand-editing all 23 locale files before a reworded English string
would be translated again, and sent one request every 2 seconds.

- Only strings missing from a locale are translated by default.
  --untranslated brings back the old behaviour.
- --keys a.b,c.d translates the named keys again; unknown keys are an
  error.
- --dry-run lists what would be translated, per locale and key.
- --concurrency=N translates N locales at once (default 4); 429s and
  server errors are retried with back-off.
- Placeholder-only strings are not sent, a translation whose
  placeholders did not survive keeps the English, mask tokens are
  matched in any case, and files are formatted with the Mod's Prettier
  settings so unchanged lines stay unchanged.
- The estimate before confirming counts the strings to send.

The script is now the same file in every Mod. DEVELOPERS.md lists the
options.

Signed-off-by: Steve Roberts <steve@shadowcomputers.co.uk>
…ields

Pak asked on Discord for listeners that trigger only on, for example,
D&D 2024 attack cards for swords. --text cannot do this reliably: it
also searches the raw content, which for a Beacon card is its HTML,
and a weapon name can appear anywhere in the card.

- Add --fields (alias --field): conditions separated by &&, each a
  field name, an operator and a value. = and != compare the whole
  value, ~ and !~ look for text in it (a regular expression with
  --regex on), and case is ignored. A missing field counts as empty.
- Read fields with their inline rolls replaced by the results.
- Check fields after players and before text; the inspector reports
  "template field does not match".
- Show the conditions on the listener card with a Change button, and
  list --fields in the help, hints and Help handout.
- Whisper an error for a condition without an operator or with a bad
  regular expression, changing nothing.
- Test Beacon and roll template fields, and editing, triggering and
  clearing them by command. The startup test expects version 2.1.0.

Signed-off-by: Steve Roberts <steve@shadowcomputers.co.uk>
…23 languages

Adds the prompt, card label, inspector reason, errors, hint, Help
handout option and matching note for --fields to every locale, and
lists --fields in each locale's options. Field names, operators and
examples stay literal.

Signed-off-by: Steve Roberts <steve@shadowcomputers.co.uk>
- README: a Template Fields section and a row in the filter table,
  explaining that --text also searches a Beacon card's HTML.
- CHANGELOG: a 2.1.0 entry.
- DEVELOPERS: the fields rule and where it sits in the matching order.
- TESTING: a live check for a 2024 sword attack.
- Version 2.1.0 in the README, developer notes and testing checklist.

Signed-off-by: Steve Roberts <steve@shadowcomputers.co.uk>
Bumps the version to 2.1.0 for the template field filter. It goes into
the same pull request as 2.0.0, so 2.0.1 joins the previous versions
and its snapshot folder is left as it was. The rebuilt script is
copied to the new 2.1.0 folder, and the One-Click description lists
template fields among the filters.

Signed-off-by: Steve Roberts <steve@shadowcomputers.co.uk>
…ntent

Pak asked on Discord what Success and Failure look for, and how
data-rollSubcategory="Attack" could match when the inspector's text
does not show it.

- README: a Success and Failure section saying that they need a target
  written into the roll (1d20>15, dice pools, roll-under), so D&D sheet
  attacks, saves and checks never trigger them; use --total for an AC
  or DC. An Inspector section saying that --text also searches the raw
  content, which for a Beacon card is its HTML.
- Help handout (English): the same points in Roll Checks, the
  --results option, the Text filter and The Inspector.
- CHANGELOG: a Changed entry under 2.1.0.

Signed-off-by: Steve Roberts <steve@shadowcomputers.co.uk>
… raw content texts

Brings the Help handout's Roll Checks, --results option, Text filter
and Inspector texts in the 23 other languages into line with English,
including the new point about D&D rolls. Roll expressions, AC and DC
and the typed values stay literal.

Signed-off-by: Steve Roberts <steve@shadowcomputers.co.uk>
…p handout

Rebuilds CRL.js and the 2.1.0 snapshot, which stay identical.

Signed-off-by: Steve Roberts <steve@shadowcomputers.co.uk>
The --fields parser split on "s*&&s*" instead of "\s*&&\s*", so it
dropped the letters s next to &&: "title~Attacks&&mode=Normal" became
"title~Attack" and "mode=Normal".

Signed-off-by: Steve Roberts <steve@shadowcomputers.co.uk>
… the inspector

--exclude (or --not) lists text that stops a listener triggering, such
as --text sword --exclude dagger. It is searched like --text, in the raw
content and the readable text, and the inspector reports "excluded text
found". The listener card shows it as Excluded text.

The inspector now shows embedded data: images in Raw content as just
their type, so Beacon cards' weapon icons no longer fill the 3,000
characters it shows. Listeners still search the full message.

Signed-off-by: Steve Roberts <steve@shadowcomputers.co.uk>
…tor texts

Signed-off-by: Steve Roberts <steve@shadowcomputers.co.uk>
…ages

Signed-off-by: Steve Roberts <steve@shadowcomputers.co.uk>
Signed-off-by: Steve Roberts <steve@shadowcomputers.co.uk>
… icon on its handouts in the Journal

The Help and Command Deck handouts now use the plain Customizable Roll Listener icon from Roll20's file store as their journal icon (the handout avatar). The icon is set when a handout is created and added to handouts from earlier versions, so existing games get it too.

Roll20 stores the avatar as max.webp with a number, so a handout counts as having the icon when its avatar contains the image's path. Only the webp files exist (thumb.png is a 404), so the URL uses thumb.webp. A test covers new handouts and handouts made before.

Signed-off-by: Steve Roberts <steve@shadowcomputers.co.uk>
…rnal icon

The root script and the 2.1.0 snapshot are rebuilt without a version bump and remain identical.

Signed-off-by: Steve Roberts <steve@shadowcomputers.co.uk>
…nger fills the handout

Roll20 always stores a handout's journal icon as the full-size file, whatever size the Mod asks for, and shows that file at its natural size at the top of an open handout. The Customizable-Roll-Listener icon was a 1024 pixel upload, so it filled the handout above the text.

The icon now points at a 100 pixel copy of the same artwork. Handouts from earlier builds are matched on the image path, so they change to the small icon the next time the Mod starts.

Signed-off-by: Steve Roberts <steve@shadowcomputers.co.uk>
…ll journal icon

The built script and its 2.1.0 copy carry the new icon path. The version number is unchanged.

Signed-off-by: Steve Roberts <steve@shadowcomputers.co.uk>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants