Lua Templating
At AntiRaid, we prioritize flexibility and customization for our users. To this end, our bot supports advanced templating to allow for extensive personalization of embeds and messages. While many bots utilize proprietary languages or templating engines, we have chosen to leverage Lua—a renowned scripting language widely used in game development and other applications. This decision ensures that our users benefit from a powerful, well-documented, and versatile language, enhancing the capability and ease of customizing their AntiRaid experience.
Specifically, Anti Raid uses a variant of Lua called Luau. If you've ever used Roblox before, this is the same variant of Lua used there too (which is why Luau is also known as Roblox Lua in many places). You can check out the Luau docs for more information on the language itself. Unlike PUC Lua (the reference implementation), Luau is both faster and offers robust sandboxing capabilities allowing AntiRaid to run scripts in as safe an environment as possible.
Getting Started
Note that the remainder of these docs will cover AntiRaids Lua SDKs. To learn more about Lua itself, please checkout Lua's official tutorial for Lua 5.0 here. Other resources for Lua exist (Lua is very popular after all), including Roblox's tutorial (ignore the Studio bits), TutorialPoint and Codecademy.
Limitations
AntiRaid applies the following 3 global limits to all Lua templates. Note that we may provide increased limits as a Premium feature in the future:
#![allow(unused)] fn main() { pub const MAX_TEMPLATE_MEMORY_USAGE: usize = 1024 * 1024 * 3; // 3MB maximum memory pub const MAX_TEMPLATE_LIFETIME: std::time::Duration = std::time::Duration::from_secs(60 * 15); // 15 minutes maximum lifetime pub const MAX_TEMPLATES_EXECUTION_TIME: std::time::Duration = std::time::Duration::from_secs(30); // 30 seconds maximum execution time }
The above limits are in place to prevent abuse and ensure that the bot remains responsive. If you require increased limits, please contact support (once again, this may change in the future).
Some key notes
- Each guild is assigned a dedicated Lua VM. This VM is used to execute Lua code that is used in the templates.
- The total memory usage that a guild can use is limited to
MAX_TEMPLATE_MEMORY_USAGE
(currently 3MB). This is to prevent a single guild from using too much memory. - Execution of all scripts is timed out when the last executed script takes longer than
MAX_TEMPLATES_EXECUTION_TIME
(currently 30 seconds). - A lua VM will exist for a total of
MAX_TEMPLATE_LIFETIME
(currently 10 minutes) after the last access before being destroyed. This is to reduce memory+CPU usage. - The
__stack
table can be used to share data across templates safely while the VM is running. without affecting other templates. This is useful for sharing data between templates such as Audit Logs. Note that AntiRaid uses luau sandboxing meaning that_G
is readonly. - The standard
require
statement can be used to import AntiRaid modules. Note that the modules are read-only and cannot be monkey-patched etc. - Because Lua is a single-threaded language, only one template can be executed at a time
There are 2 valid syntax for a Luau template:
- Lua script syntax
local args, token = ...
-- Do something
return output
- Function expression syntax (not recommended for new code)
function(args, token)
-- Do something
return output
end
Note that option 1 is recommended as it is both more idiomatic and is also valid syntax for LSP's and Luau parsers. Note that option 2 is actually converted to option 1 internally through the below wrapper:
local args, token = ...
{function body here}
Interop
Many features of Lua don't work so well when calling functions within the AntiRaid SDK. For example, both arrays and maps are expressed as tables in Lua. However, AntiRaid, being written in Rust, doesn't know this and hance needs some help to convert certain types for FFI. This is where the @antiraid/interop
module comes in.
Arrays
To pass arrays to modules within the AntiRaid SDK, you need to set the metatable to @antiraid/interop#array_metatable
. This will allow the SDK to convert the array to a Rust Vec
internally.
local interop = require '@antiraid/interop'
setmetatable({a = 5}, interop.array_metatable)
Null
While the Lua nil
does work in many cases (and even when calling the SDK), its not the best choice. When querying AntiRaid SDK, the SDK will use the @antiraid/interop#null
value to represent a null value. Your Lua templates can also use this value if desired
local interop = require '@antiraid/interop'
local null = interop.null -- This is the null value
Memory Usage
While not strictly useful for interop, it is often desirable to know the memory usage of a Lua template as AntiRaid will kill your template if it exceeds the memory limit. For this, you can use the @antiraid/interop#memusage
function.
local interop = require '@antiraid/interop'
print(interop.memusage())
User Error vs Runtime Error
As Lua does not have a built-in way to distinguish between user errors and runtime errors, AntiRaid provides a way to do so. Simply return a table with the key __error
set, and the value set to the error message to create a user error. You can use the standard error
function for runtime errors. E.g.
-- User Error
return { __error = "You have reached the maximum number of tries in this 5 minute window." }
-- Runtime Error
error("Could not parse user ID for some reason")
Events
All Lua templates are invoked via events. As such, the first argument to the template is an Event
. Event
is a userdata
. The below will explain the most important fields exposed by Event
. Note that all fields, unless stated otherwise, are read-only:
- ``
Template Context
All Lua templates are passed both the Event
(denoted by args
) and a TemplateContext
userdata (denoted by token
). Note that like Event
, TemplateContext
is a userdata (not a table). As such, they cannot be manually constructed in templates themselves.
"Executors" and other sensistive APIs use the TemplateContext
to read template_data
including the pragma (note that template_data
is also exposed to templates as a read-only field). This is what allows AntiRaids capability system to correctly sandbox templates based on what capabilities they have been given.
Examples of executors include the @antiraid/actions
ActionExecutor
, which allows you to perform actions such as banning/kicking/timing out users and other Discord actions and @antiraid/kv
KvExecutor
which allow for persistent storage via a key-value interface.
TemplateContext
is guaranteed to be valid while accessible in the VM . This means that templates can choose to share their capabilities with other templates using the __stack
.
It is also guaranteed that the created executor is complete and does not rely on the token itself whatsoever after creation. This means that a template executor can be used after the template has finished executing (e.g. in a coroutine).
Example
local args, token = ...
print(token)