Getting Started
WardenX allows you to write custom Lua scripts to handle Discord events and commands. Scripts are sandboxed for security and have access to specific Discord objects.
Global Variables
These variables are automatically available in your scripts depending on the context.
interactionDiscord: Available in command, button, and modal scripts.guildDiscord: The current server object.memberDiscord: The member who triggered the event.webhookWardenX: Available in the WardenX webhook event.storageWardenX: Persistent data storage object for your server.wardenxWardenX: Utility object for advanced script interactions.
Script Folders WardenX
Scripts are organized by their purpose. The dashboard handles the placement automatically when you create them in specific sections.
--[[ @wardenx ... ]] metadata block at the top to declare options.wardenx.run("filename") to keep repeated logic in one place.Command Metadata
--[[ @wardenx
description = "Greet a user"
options = {
{ name = "user", description = "The user to greet", type = "user", required = true }
}
--]]
Supported Events Discord
Discord IDs should always be treated as strings in Lua to prevent precision loss.
member or user object.message object.member, channel, and oldChannel.role object.member and roles (array of role objects). Also provides role as the first role when at least one role is present in the event payload.channel object.guild and a webhook table containing method, body, lowercase headers, query parameters, and parsed json when the content type is JSON. Repeated header or query values are represented as arrays.-- events/WardenXWebhookEvent.lua
print(webhook.body)
if webhook.json ~= nil then
print(webhook.json.name)
end
Webhook URLs use a private 256-bit token. Keep the URL secret and regenerate or revoke it immediately if it is exposed. Requests are limited to 1 MiB and 30 requests per minute per server.
WardenX Utility WardenX
The wardenx global provides core functionality for script interaction.
LuaEmbedBuilder instance.LuaButton instance.lib/ or commands/. Use it to compose scripts, share utilities, and call specific helper functions without duplicating code.Usage Patterns
-- lib/moderation.lua
function canModerate(targetRoleLevel, actorRoleLevel)
return actorRoleLevel > targetRoleLevel
end
function formatCase(caseId)
return "CASE-" .. tostring(caseId)
end
-- commands/warn.lua
local allowed = wardenx.run("moderation", "canModerate", 3, 7)
if not allowed then
interaction:replyEphemeral("You cannot moderate this target.")
return
end
local caseLabel = wardenx.run("moderation", "formatCase", 1234)
interaction:reply("Created " .. caseLabel)
-- Injected globals example
wardenx.run("audit", {
action = "warn",
targetId = "123456789012345678"
})
Tip: Use lib/ for shared modules and keep command/event files focused on orchestration (inputs, permission checks, replies).
HTTP Requests WardenX
Call external HTTP APIs synchronously from a script. Use pcall when you want to handle connection, timeout, validation, or response-size errors without stopping the script.
options may contain method, headers, and body.
Supported methods are GET, POST, PUT, PATCH, DELETE, HEAD, and OPTIONS.
Response
The returned table contains status (number), ok (true for status 200–299), body (string), url (the final URL after redirects), and headers. Header names are lowercase and each value is an array to preserve repeated headers.
local response = wardenx.request("https://api.example.com/items", {
method = "POST",
headers = {
["Authorization"] = "Bearer token",
["Content-Type"] = "application/json"
},
body = '{"name":"example"}'
})
if response.ok then
print(response.body)
else
print("API returned status " .. response.status)
end
Persistent Storage WardenX
Data saved in storage persists across restarts. Supports strings, numbers, booleans, and nested tables.
nil if not found.Moderation Logs WardenX
Created via guild:newModlog(). Automatically handles formatting and sending logs to the server's log channel.
Interactions Discord
The interaction object is used specifically for slash commands, button clicks, and modal submissions.
SLASH_COMMAND, BUTTON, or MODAL.components can be a single button or table of buttons.LuaMember or LuaUser who triggered the interaction.Servers (Guilds) Discord
LuaMember representing the bot.LuaMember objects.LuaMember for the given ID.LuaMember.LuaRole objects.LuaRole by ID (use everyone for public role).LuaChannel for the given ID.LuaModlog instance.LuaCategory as parentCategory to create it under that category.{time, timescale, reason}. Supported timescales: HOURS, DAYS.Members & Users Discord
LuaMember objects represent a user within a specific server and inherit all LuaUser methods.
LuaGuild the member belongs to.LuaUser
Represents a global Discord user.
Roles Discord
Channels & Categories Discord
LuaCategory.nil to remove the channel from its category.LuaMember or LuaRole. allowed/denied are arrays of permission names.{ allowed = {...}, denied = {...} }, or nil if no override exists.LuaMember or LuaRole.LuaCategory
LuaMember or LuaRole.{ allowed = {...}, denied = {...} }, or nil.LuaMember or LuaRole.Messages Discord
Embeds Discord
Use wardenx.newEmbed() to create a builder.
LuaMessageEmbed.LuaMessageEmbed
Read-only representation of a sent or received embed.
LuaEmbedBuilder pre-filled with this embed's data for easy editing.Advanced Features WardenX
Type Casting
Use type hints in the editor for better autocomplete.
local m: member = guild:getMember("ID")
m:getNickname() -- Editor now knows 'm' is a member
Security & Limits WardenX
- Sandboxing:
os,io,debug,package, andluajavalibraries are disabled. - Action limits: Discord actions, storage mutations, and HTTP calls count toward the per-script action budget.
- HTTP timeouts: Requests have short connection and total-call deadlines so unavailable APIs cannot block a script indefinitely.
- HTTP size: Request and response bodies are limited to 1 MiB.
- Network access: Private, loopback, link-local, multicast, and reserved network destinations are blocked, including redirect targets.
Code Examples
Map users roles to global storage
function saveRoles(m: member)
local roles = m:getRoles() or {}
if #roles == 0 then return end
local roleIds = {}
for _,role in ipairs(roles) do
print("saving " .. role:getName() .. " with role id: " .. tostring(role:getId()))
table.insert(roleIds, tostring(role:getId()))
end
local existingData = storage:get("roleStores") or {}
local userId = m:getId()
existingData[userId] = roleIds
storage:set("roleStores", existingData)
end
Make your own custom welcome messages with fallback functionality, using the Member Joined event
local embed: embedBuilder = wardenx.newEmbed()
embed:setTitle(string.format("User %s has joined the server!", member:getEffectiveName()))
--[[
you can either
- hardcode the welcome channel
local welcomeChannel = guild:getChannel("channelid")
- id needs to be in a string as its too long of a number, thank lua for that
or
- get it dynamically from server storage
- this means you can change the target channel via command, instead of needing to edit the script
local welcomeChannel = storage:get("welcomeChannel")
--]]
local welcomeChannel = storage:get("welcomeChannel")
if welcomeChannel == nil then
-- send server owner message
guild:getOwner():sendMessage(string.format(
"hey, welcomeChannel is nil, so i cant send a welcome message for %s",
member:getEffectiveName()))
-- create a modlog
local modlog = guild:newModlog()
-- configure it
modlog:setAction("failed welcome message")
modlog:setDetails("welcomeChannel is nil")
modlog:setModerator(guild:getSelf())
-- send it
modlog:send()
else
welcomeChannel:sendMessage("", embed:build())
end