PexoBot · Documentation

Custom code

Write your own commands and automations in Lua or JavaScript. A script receives an event and asks the bot to perform actions through the API.

How it works

  1. Open the Custom code module in your server dashboard and add a script.
  2. Pick a trigger (e.g. the /ping command) and a language. Lua is recommended.
  3. Write the code. The event variable describes what happened; functions like reply() queue actions.
  4. Click Run to test (no actions are performed), then Save. The command appears on the server within seconds.

Scripts run in an isolated sandbox: no internet, files or other servers. The bot performs actions after the script finishes, and only on your server.

Languages

Lua

Lua recommended

Lua 5.4 (WebAssembly) with a LuaJIT / Lua 5.1 compatibility layer: bit.band/bor/bxor/lshift/rshift/bnot, unpack, table.getn, math.pow. Fastest start and lowest memory use. No io, require, load, debug; os keeps time/clock/date.

JS

JavaScript

Modern JavaScript (QuickJS, ES2023). Code runs inside a function, so you can use return — the returned value becomes the reply. No fetch, require, timers or async I/O.

Triggers

Triggerevent.typeWhenreply()
Slash commandcommandSomeone uses the command. Optional text goes to event.input.command response
MessagemessageA message starts with the prefix (e.g. !points). The rest → event.input. Empty = every message.reply to the message
Join / leavejoin / leaveA member joins or leaves the server.channel from script settings
TimertimerEvery N minutes (Free: min 10 min, PexoBot+: 1 min). event.user = nil.channel from script settings

The event

FieldTypeDescription
event.typestringcommand, message, join, leave or timer
event.commandstringcommand name (for command)
event.inputstringtext after the command or prefix, otherwise ""
event.user.id / .name / .tagstringID, display name and username
event.user.botboolis a bot
event.user.avatarstringavatar URL
event.member.roleslist of IDsmember roles (without @everyone)
event.member.nickstringserver nickname
event.member.adminbool“Manage Server” permission
event.member.joinedAtnumberjoin time, ms
event.channel.id / .namestringevent channel
event.guild.id / .name / .members / .ownerIdstring / numberserver
event.message.id / .contentstringmessage (for message)

Fields missing for an event are nil (Lua) / null (JS).

API

The same functions in Lua and JavaScript. Every action counts toward the per-run action limit.

FunctionDescription
reply(msg)Replies to the command / message, or posts to the configured channel (join, leave, timer).
send(channelId, msg)Sends a message to a channel on this server.
dm(userId, msg)Direct message to a server member.
react(emoji)Reacts to the triggering message (e.g. "👍" or "<:name:id>").
addRole(userId, roleId) / removeRole(…)Adds / removes a role. The role must be below the bot’s role and can’t have Administrator.
log(...) / print(...)Writes to the log — visible in the dashboard test (not sent to Discord).
kv.get(k) / kv.set(k, v) / kv.del(k)Persistent server data store (text). See below.
kv.num(k, def) / kv.add(k, n)Numbers: read with default and increment (returns the new value).
kv.keys()List of keys.
random(a, b)Random integer from a to b (inclusive).
now()Current time in milliseconds.
return msgLua & JS: the returned value = reply(), unless the script already replied.
reply() accepts a string, an object { content, embed }, or an array of strings (joined by lines). Empty text / null sends nothing.

Messages & embeds

msg is text (up to 2000 chars) or a table/object:

Lua
reply({
  content = "Text above the embed",
  embed = {
    title = "Title", description = "Description with **markdown**", color = "#22c55e",
    fields = { { name = "Field", value = "Value", inline = true } },
    image = "https://…/image.png", thumbnail = "https://…/mini.png", footer = "Footer",
  }
})

User mentions work (<@ID>), but @everyone, @here and roles never ping.

kv store

Shared by all scripts on the server, persistent between runs. Key up to 100 chars, value up to 2000 chars (stored as text — read numbers with kv.num). Browse and clear it in the History & data tab. Dashboard tests don’t save changes.

Lua
kv.set("motd", "Have a nice day!")
local visits = kv.add("visits:" .. event.user.id)   -- 1, 2, 3…
if kv.get("motd") then reply(kv.get("motd") .. " (" .. visits .. ")") end
kv.del("old")

Limits

FreePexoBot+
Run time (Lua/JS)150 ms600 ms
Memory16 MB64 MB
Actions per run520
kv keys1001000
Runs per minute (server)30120
Scripts325
Timer every10 min1 min

The same person can run the same script at most once every 2 seconds.

Examples

Random roll (/los command)

Lua
-- /los — roll a number from 1 to the given maximum (default 100)
local max = tonumber(event.input) or 100
reply("🎲 " .. event.user.name .. " rolled **" .. random(1, max) .. "**")
JS
// /los — roll a number from 1 to the given maximum (default 100)
const max = Number(event.input) || 100;
return `🎲 ${event.user.name} rolled **${random(1, max)}**`;

Points with a role reward (“!punkty” message)

Lua
-- Message starting with "!punkty": activity points in kv
local key = "pkt:" .. event.user.id
if event.input == "" then
  reply("You have **" .. kv.num(key) .. "** points.")
else
  local n = kv.add(key, 1)
  if n == 50 then addRole(event.user.id, "123456789012345678") end  -- Discord role ID
  react("⭐")
end

Welcome with a counter (join)

Lua
-- Member join: welcome in the configured channel with a counter
local nr = kv.add("dolaczenia")
reply({
  content = "<@" .. event.user.id .. ">",
  embed = {
    title = "👋 Welcome to " .. event.guild.name .. "!",
    description = "You are visitor **" .. nr .. ".** since the bot started.",
    color = "#8a3ffc",
    fields = { { name = "Members", value = tostring(event.guild.members), inline = true } },
  }
})

Rotating tips (every 60 min)

JS
// Every 60 minutes: reminder with rotating texts
const tips = ['Remember the rules 📜', 'Vote for the server on Top.gg ⭐', 'Have an idea? /suggest 💡'];
const i = kv.add('tip') % tips.length;
reply({ embed: { title: '💡 Tip', description: tips[i], color: '#eab308' } });

Errors

Recent runs (with errors) are in the module’s History & data tab.