User Scripting

The scripts user command allows creation of user scripts in JavaScript directly from Discord via a web-based editor using Monaco, the editor that powers VS Code.

A repository has been created - shared-user-scripts - for anyone to share their useful scripts. It also provides a number of helpful examples in EdFletcher’s folder.

User scripts will be linted (via eslint) upon attempting to save and the save will be blocked if there are any lint errors. This is an important protection as errant user scripts are capable of creating significant trouble. However, linting along cannot catch every bug so be sure to practice defensive programming, catching and handling errors instead of letting them bubble up.

New scripts will be disabled by default and will need to be explicitly enabled with ;scripts enableScript <scriptName>. Scripts can be globally disabled with ;scripts disable. Any user script that fails enough times consecutively will be automatically disabled by DRC.

These scripts are executed in a sandboxed environment that provides a number of extra top-level globals implementing various useful DRC-related functionality. They will be executed in an async context by default so top-level await is available.

User scripts are executed on receipt of every IPC message and so should run as quickly as possible, and be as quiet with logging/emissions as possible. Even without any additional IPC traffic, the IRC daemon will publish a heartbeat frequently which therefore acts as the slowest “clock” for user script execution. Of course, on an active installation, traffic of many others types will be more frequent.

User script executions are queued to be serialized and so may back up (the “queue high watermark” denotes the largest extent of this queue in a 1-minute time period). Accordingly, do not rely on the time at execution as representative of the time of the event being handled: instead, use the timestamp in the event data passed to your script.

Script state can be persisted across calls (into Redis) with the .state property of the DRCUserState global. To ensure immediate consistency, state.get() and state.set() must be awaited! Immediate consistency is especially important for any script that emits on some multiple of events seen, such as the userFlux example.