Skip to content

Lua Commands

Daniel Jarski edited this page Feb 8, 2025 · 13 revisions

This Wiki page is under construction, and will receive frequent updates as features are added.

Lua commands are a powerful way to extend Administrator's own functionality into truly, fully custom commands for your server. You control everything - the name, description, options, and even what the command physically does!

Quick tips

  • Lua commands utilize a basic Lua installation ONLY. Other flavors or variants of Lua are not supported, though they may share some syntax with Lua.
  • The below libraries are enabled by default. If you have a good reason to request another standard library be enabled, feel free to ask.
    • Base
    • Math
    • String
    • Table
  • Non-native Lua methods provided for additional functionality are subject to strict rate-limits. To prevent abuse and spam, most external and/or Discord-related methods can only be used a limited number of times before command execution will fail due to exceeding the rate-limit.

Getting started

This feature is intended for power users who know what they are doing. Basic programming knowledge is a must, and knowledge of Lua itself will only make things easier. There are plenty of resources and tutorials out there, but you're always welcome to start with Lua's official site.

Command file structure

When you submit your file to /lua-command set, a certain file structure is expected for the sake of how Lua commands are parsed and handled. Your submitted file must have the .lua file extension, and must follow the below design, or an error will be returned when attempting to create it.

-- A 'metadata' object MUST be defined.
local metadata = {
    name = "echo",
    permissions = Permission.MANAGE_MESSAGES,
    description = "Repeat some text back to yourself.",
	options = {
		text = {
			description = "The text to echo."
		}
	}
}

-- The END METADATA comment must appear exactly like the below line to indicate an unambiguous metadata/command separation.

-- END METADATA --

-- After the END METADATA comment, you should begin the "execution" section of your command. This is the code that is actually called when a user uses your custom command.

return ctx.parameters.text

Metadata

Command metadata is defined and expressed via the metadata object. While this section is far from exhaustive, it can give you a good idea of what to expect when defining metadata for a command. Any options commented with (optional) are not required for command metadata. Again using our /echo example:

Metadata example
local metadata = {
    -- The name of the command. This is what is displayed in the command menu.
    -- This is absolutely required.
    name = "echo",

    -- (optional) The permissions required to see and execute this command.
    -- For a full list of permission names, see https://discord.com/developers/docs/topics/permissions#permissions-bitwise-permission-flags
    permissions = Permission.MANAGE_MESSAGES,

    -- (optional) The description of the command. This is displayed under the name in the command menu.
    description = "Repeat some text back to yourself.",
    
    -- (optional) The options for this command. These are displayed after the name in the command menu.
    options = {
    
        -- The name of the option. This is required, can only be lowercase characters, hyphens (-), and underscores (_), and must be unique per-command.
        text = {
            -- (optional) The description for this option.
            description = "The text to echo.",
            
            -- (optional) The type for this option. Defaults to "string".
            --[[
                Available options are:
                "string"
                "integer"
                "boolean"
                "user"
                "channel"
                "role"
                "mentionable"
                "number"
                "attachment"
                "subcommand"
                "subcommandgroup"
            --]]
            type = "string",
            
            -- (optional) Whether this option is required (true) or optional (false) for the command. Defaults to required (true).
            required = true,
            
            -- (optional) If type = "channel", an array of channel types this option accepts.
            --[[
                Available options are:
                "text"
                "direct"
                "voice"
                "group"
                "category"
                "news"
                "newsthread"
                "publicthread"
                "privatethread"
                "stage"
                "directory"
                "forum"
            --]]
            -- channelTypes = { "text", "publicthread" },
            
            -- (optional) The minimum value for an "integer" or "number" option, or the minimum length for a "string" option. Defaults to no minimum.
            -- minimum = 5,
            
            -- (optional) The maximum value for an "integer" or "number" option, or the maximum length for a "string" option. Defaults to no maximum.
            -- maximum = 500,
            
            -- (optional) If type = "subcommand" or "subcommandgroup", specifies a table of nested options below this option.
            -- More detailed documentation on this Discord feature can be found here: https://discord.com/developers/docs/interactions/application-commands#application-command-object-application-command-option-structure
            --[[
                This is used for more advanced command structure, such as the following:
                /something create <name>
                /something delete <name>
                /something modify <name>
            --]]
            -- options = { },
        }
    }
}

The ctx Table

Your command's "context" table, ctx, stores various data pertaining to the command, where it was executed, who executed it, and what information was passed to the command, if any. Several methods and properties are available to you.

Property Description
ctx.author A user object representing the user who used the command.
ctx.channel A channel object representing the channel this command was used in.
ctx.guild A guild object representing the guild (server) this command was used in.
ctx.parameters A table containing parameters passed to the command, as well as their values if they were provided.
ctx.path A table (array) containing each part of the command path.
Example: /command sub sub1{"command", "sub", "sub1"}

Users

Users have the following structure, with an example variable user:

Property/Method Description
user.id The unique ID of the user.
user.name The unique username of the user.
user.mention The mention of the user as it would appear in chat.
user.tag The user's tag.
user.discriminator The user's classic Discord discriminator, if set.
user.globalName The user's global name, if set.
user.avatar The user's global avatar URL.
user.bot Whether this user is a bot.
user.member Whether this user is a server member (the below properties will evaluate to nil if this is false)
user.roles An array of the user's role IDs, if any.
user.joined When the user joined the server (in UTC*).
user.nickname The user's nickname, or nil if they don't have a nickname.
user.muted Whether the user is server muted.
user.deafened Whether the user is server deafened.
user.boosted When the user first started boosting the server (in UTC*), or nil if they aren't boosting.
user.pending Whether the user is still pending membership screening/verification.
user.guildAvatar The user's server (guild) avatar, or their global avatar if they don't have one.
user.timedOutUntil When the user is timed out until (in UTC*), or nil if they aren't timed out.
user.permissions The user's total calculated server permissions. You can check individual permissions via hasPermission(permission) instead.
user:hasPermission(permission) Returns whether the user has a specific permission or permissions set.
user:setNickname(nickname) Updates the user's nickname.
user:kick(reason) Kicks the user out.
user:ban(reason) Bans the user.
user:grantRole(roleId) Grants (gives) the user a role.
user:revokeRole(roleId) Revokes (removes) a role from the user.

Channels

Channels have the following structure, with an example variable channel:

Property/Method Description
ctx.channel.id The unique ID for this channel.
ctx.channel.name The name for this channel.
ctx.channel.type The type of this channel. See channelTypes in the metadata section for a
ctx.channel.mention The mention for this channel as it would appear in chat.
ctx.channel.position The position of this channel, relative to other nearby channels.
ctx.channel:setName(name) Attempts to rename the channel.
ctx.channel:lastMessageId The most recent message's ID in the channel, or nil if it doesn't exist.
ctx.channel.tag The channel's tag.
ctx.channel.lastPin When a message was most recently pinned (in UTC*), or nil if it doesn't exist.
ctx.channel.ageRestricted Whether this channel is marked as age restricted (formerly NSFW).
ctx.channel.slowmode The slowmode on this channel, in seconds.
ctx.channel.topic The topic for this channel, or nil if it doesn't exist.
ctx.channel.categoryId The unique ID of the category this channel belongs to, or nil if it doesn't belong to one.
ctx.channel.news Whether this channel is a new channel.
ctx.channel.archiveThreadsAfter The span of time after which inactive threads are automatically archived, or nil if it doesn't exist.
ctx.channel:setTopic(topic) Attempts to set the channel topic.
ctx.channel:delete() Attempts to delete the channel.
ctx.channel:sendMessage(msg) Sends a message to the channel.
ctx.channel:getMessage(id) Returns a message with the specified ID in the channel, or nil if it doesn't exist.
ctx.channel.parentId If the channel is a thread, the ID of the channel the thread belongs to.
ctx.channel.creatorId If the channel is a thread, the ID of the user who created it.
ctx.channel.tags If the channel is a forum thread, an array of IDs representing the tags on it.
ctx.channel.memberLimit If the channel is a voice channel, the maximum number of members allowed to connect.

Guilds

Guilds have the following structure, with an example variable guild:

Property/Method Description
ctx.guild.id The unique ID for this guild.
ctx.guild.name The name for this guild.
ctx.guild.icon The icon URL for this guild, or nil if it doesn't exist.
ctx.guild.splash The splash image URL for this guild, or nil if it doesn't exist.
ctx.guild.discoverySplash The discovery splash image URL for this guild, or nil if it doesn't exist.
ctx.guild.ownerId The ID of the owner of this guild.
ctx.guild.verificationLevel The verification level of this guild.
ctx.guild.notificationLevel The notification level of this guild.
ctx.guild.contentFilterLevel The content filter level of this guild.
ctx.guild.roles An array of roles belonging to this guild.
ctx.guild.emojis An array of emojis belonging to this guild.
ctx.guild.features An array of strings representing features enabled on this guild.
ctx.guild.systemChannelId The ID of the guild's system channel, or nil if it doesn't exist.
ctx.guild.rulesChannelId The ID of the guild's rules channel, or nil if it doesn't exist.
ctx.guild.vanityInviteCode The invite code for this guild's vanity invite, or nil if it doesn't exist.
ctx.guild.description The guild's description, or nil if it doesn't exist.
ctx.guild.banner The banner image URL for this guild, or nil if it doesn't exist.
ctx.guild.boostTier The boost tier for this guild.
ctx.guild.boosters The number of users boosting this guild.
ctx.guild.updateChannelId The ID of the guild's public update channel, or nil if it doesn't exist.
ctx.guild.nsfwLevel The NSFW (age restriction) level of this guild.
ctx.guild.safetyChannelId The ID of the guild's safety alert channel, or nil if it doesn't exist.
ctx.guild:banUser(id, reason, pruneDays) Bans a user from the server with a reason and a number of days' worth of messages to prune (nil or 0-7)
ctx.guild:sendMessage(id, msg) Sends a message to a specified channel ID.

Roles

Roles have the following structure, with an example variable role:

Property/Method Description
role.id: The unique ID of this role.
role.name: The name of this role.
role.mention The mention for this role as it would appear in chat.
role.color The HTML hex code for this role.
role.icon If a default emoji, the raw emoji string. If an icon image, the URL to that image, or nil if it doesn't exist.
role.hoisted Whether this role is "hoisted" or shown separately in the member list.
role.position The position of this role, relative to other roles.
role.permissions The role's total calculated permissions.
role.managed Whether this role is managed by an integration, like a bot or third-party service.
role.mentionable Whether this role can be directly mentioned in chat to notify its members.

Emojis

Emojis have the following structure, with an example variable emoji:

Property/Method Description
emoji.id The unique ID of this emoji.
emoji.tag The tag of this emoji.
emoji.animated Whether this emoji animates in chat.
emoji.name The name of this emoji.
emoji.roleIds An array of role IDs allowed to use this emoji.
emoji.creator A user object representing the user that created this emoji, or nil if it can't be found.
emoji.managed Whether this emoji is managed by an integration, like a bot or third-party service.

ctx.parameters

The parameters passed to the command with their values, if any.

  • ctx.parameters.[NAME]: The [NAME] parameter. In the /echo example, you could access the value ctx.parameters.text.

Utility

On top of the ctx table, there are also several other features available to assist with making your Lua commands more flexible and further increasing their utility.

Permission Enum

Discord permissions are stored in a handy Permission Lua table mimicking an enumeration. A full list of Discord permission names can be found on the official API documentation. For example, the "Manage Messages" permission can be expressed as Permission.MANAGE_MESSAGES.

Emoji Enum

Emojis can help spice up a command response and make it feel less boring. Similar to permissions, most to all emojis can be easily accessed via the Emoji Lua table. For most emojis, you can access them via Emoji.FULL_EMOJI_NAME (using the name as it appears in Discord), but for any emoji such as 🎱 (8BALL) which starts with a non-numeric character, you can index the table as usual, in this case Emoji["8BALL"].

Persistence

To allow Lua commands to store data between executions for long-term storage, an extremely basic persistence library is provided. At the moment, only a single string value can be stored and retrieved, but what you can use the string to store is entirely up to you.

Method Description
persistence.get() Returns the current value of this command's persistent data as a string, or nil if no data has been set before.
persistence.set(value) Updates the persistent data with the supplied string value, returning a boolean value indicating if the save was successful.

HTTP Requests

To allow Lua commands to, for example, query a remote web API (such as for displaying website data to users), a very basic REST request library is provided. This library is subject to extremely strict rate-limits, only allowing one request per method per command to prevent abuse.

Method Description
http.get(url) Returns the response content located at url as a string, or nil if the request fails.

JSON

To further extend both the Persistence and HTTP libraries, a JSON serialization and deserialization library is provided. This can allow remote web data returned as JSON to be more easily consumed, or to store complex persistent data in a more convenient format.

Method Description
json.serialize(value) Returns the provided object, array, or value converted into a JSON string.
json.deserialize(json) Converts the provided JSON string into a Lua table, or nil if the deserialization fails.

Random

Lua provides a helpful math.random() method for obtaining a random number. However, for security concerns, os.time() is not available in Lua commands, so you can utilize the helper method now() for seeding Lua's random number generator like so:

math.randomseed(now())
-- Now, calls to math.random() will be...random!

Clone this wiki locally