Using MiScript to create custom commands

Written by Zephy, Gerard, xRappy, Veld

Sat Feb 22 2020

MiScript

MiScript is Miki's custom command language, specifically written to make easy-to-use custom commands for your own server.
Not only are Miki's custom commands powerful, but they will also become real slash commands in your server, and will appear when you type /.
In this guide, we will show you how to get started with MiScript.

Commands

Commands Description
/customcommand create [command name] [script] Used to create a custom command
/customcommand remove [command name] Used to delete an existing custom command
/customcommand get [command name] Used to get the code of an existing custom command
/customcommand getstorage Used to see the storage of custom commands
/customcommand list Used to see all custom commands in your server
/customcommand sync Used when something is wrong with your custom slash commands.
/customcommand setdescription [command name] [description] Used to set a description for your custom slash command.

Your first script

say("hello world!")

As you may have realized, this command simply makes Miki say "hello world". There's nothing else happening. You may add it to your server like this:

/customcommand create command-name:helloworld command-script:say("hello world!")

Variables

Variables let you store a value, and then use it later. You define them with var and give it any name you want. For example:

var mynumber = 5
mynumber = mynumber * 2
say(mynumber)

This will create a variable called mynumber with a value of 5. It will then multiply it by 2 and send it:

10

Message values

You can add a value to your text by putting it inside $[]. For example:

say("The number is $[mynumber]")

Also, Miki will provide a few global variables you can use, such as author, guild, channel and message. These have information about how the command was used. Consider this example:

say("Hello $[author]")

If the user Veld used this command, it would send:

Hello Veld

Conditionals

You are able to send different responses under different conditions. For example:

if author = "Veld" then
    say("Hello Veld!")
else
    say("Wait, you're not Veld!")
end

Arguments

The person using your command may include additional information. This is accessible through the global variable args.

Imagine we have a command /age, and its code is as follows:

say("You are $[args] years old")

If your user types /age 20, Miki will say You are 20 years old However, if the user doesn't enter any args, its value will be empty. You may want to check like this:

if args.length == 0 then
    say("No args!")
else
    say(args)
end

The length in this case is the amount of arguments (which were separated by spaces), as args is in fact a list. More on lists later.

Embeds

It is also possible to send embeds with MiScript. You may even use them in /levelupmessage. For example:

say(embed("Probably because you said something weird...")
    .title("$[author.name] ignores you")
    .image("https://media.giphy.com/media/H9jrEhsr1pUMo/giphy.gif"))

This would show:

image

There are more functions like .footer("Text") and .color("red") for the embed builder; check out the API reference for embeds.

Adding your command

In the "hello world" example, you saw how to add a command with a script to your server. However, sometimes your command consists of multiple lines, which currently can't be sent properly in a Discord slash command. In this case, you may chain lines together, and use ; between each statement. Or you may use the non-slash command, like this:

image

Notice the dark background behind the code. This is a code block, and you may use it by putting triple backticks ``` at the start and end of your code. It is optional, but it makes it look more organized.

Removing commands

To remove an existing command, you can use /customcommand remove [command name]. For example, if you want to remove the command we created above, you would use /customcommand remove orchid.

 

⚠ Programming Alert:

The following sections will go in depth on what MiScript can do. This can be overwhelming when you're new to programming. If you need help, feel free to join our Discord Server and ask for help in the #miscript channel.

Functions

When you want to execute code multiple times, you can create a function.

Creating functions

The following code creates a function in MiScript:

fun greet(name)
    say("Hello $[name]!")
end

Let's go through this script:

Code Description
fun Tell MiScript that we'll create a new function.
greet Function name. Use this name to call the function.
(name) Arguments that the function takes.
say("Hello $[name]!") The body of the function. This will be executed when the function is called.
end End of the function

Now that we defined the function "greet", we can call it:

greet("Veld")

This will return:

Hello Veld

Loops

Using functions, we can run code many times without having to copy and paste it. There is no "loop" statement in MiScript, but you may .map over a range of numbers or a list of values. For example:

(1..10).map(fun(e)
    say("I have said this $[e] times")
end)

Arrow functions

Sometimes you don't want to define a whole function, just run a single line of code. This is possible with arrow functions.
Consider this example that takes a list of strings (text), and then converts every value into uppercase:

["hello", "miki"].map(e => e.upper())

Let's go through the arrow function:

Code Description
e The parameter of the function. For more than one parameter, you can do (param1, param2)
=> Tell MiScript we're creating an arrow function.
e.upper() The body of the function, which is being directly returned. In this case, .upper() will uppercase every character.

When you execute the code, the result will be: ["HELLO", "MIKI"]. You will have to use say to send it though.

Lists

Lists are used to store multiple values in a single variable. Normally, you would try to do this:

var user1 = "Veld"
var user2 = "Gerard"
var user3 = "Zephy"

say("Cool dudes: $[user1], $[user2], $[user3]")

You can see how the amount of variables will pile up if we add more users, and there is no easy way to get all the users at once. The solution is to use a list.

A list starts with [ and ends with ]. So an empty list would be [].
To create an list with a string, you place the string between the brackets: [ "value" ].
To add multiple strings, you add them separated with commas within the brackets: [ "Veld", "Gerard", "Zephy"].

There are several useful functions for lists, such as .map() which we saw before.
We can join these users together with .join():

var users = ["Veld", "Gerard", "Zephy"]

say("Cool dudes: $[users.join(", ")]")

This will return:

Cool dudes: Veld, Gerard, Zephy

For more list functions check out the API reference for list.

Stop sequence

Since there is a message limit on Discord, using if and else over and over again is not a good idea for bigger commands. Using stop will stop the script and won't execute the rest of the code.

say("This message will be shown!")
stop
say("However, this message will be ignored!")

Storage API

When a script is finished, all the variables you have defined will be lost. To save a value, you can use the storage API.

Warning: The storage API is not infallible; it's rarely possible that your data will be lost. We will work to prevent this from happening.

Let's create a command that remembers how many times the command was executed. First, decide a unique key that you'll use in the storage, in this example we'll be using "count".

To store a value in the storage, you can use storage.set().

storage.set("count", 1)

To retrieve a value from the storage, you can use storage.get().
When a key doesn't exist in the storage, the result will be null.

var count = storage.get("count")

Example

Let's make a command that counts how many times it was executed.

First, we retrieve the value from the storage because it's possible that the command was executed before. When it doesn't exist, we set the count to 0.

var count = storage.get("count")

if not count then
    count = 0
end

Now we increment the value by 1, store it back to the storage and say the result:

count = count + 1
storage.set("count", count)

say("Count: $[count]")

When executing the command multiple times, you'll see that the counter increases:

image

View storage

To see what data is stored in the storage, you can use the command /customcommand getstorage:

image

Limits

To prevent abuse of the storage API, it has a key and value limit. To increase the amount of data, The owner of the Discord Server (where the command is being executed) can donate to get additional keys.

Default Donator
Amount of keys 1 25
Maximum value size 4 KB 4 KB

 

API Reference

The following section covers what the API can do in MiScript.

Globals

Function Description
args List with the arguments for the current message. Message: /command test Result: ["test"]
message Current message. See Message for more information.
author Author of the message. See User for more information.
guild Current guild. See Guild for more information.
channel Channel where the message was sent. See Channel for more information.
storage Storage API. See Storage for more information.
embed() Create a new embed. See Embed for more information.
say(content) Send a message or embed. Example: say(embed("Hello world"))

Message

Function Description
id Discord message ID. Example: message.id Result: 322845087467962368
content Content of the message. Example: message.content Result: "Hello!"

Guild

Function Description
id Discord guild ID. Example: guild.id Result: 160067691783127041
members Amount of members in the guild. Example: guild.members Result: 1337
icon Icon of the server. Example: guild.icon Result: "https://cdn.discordapp.com/icons/..."
owner Owner of the server. See User for more information.

Channel

Function Description
id Discord channel ID. Example: channel.id Result: 160105994217586689
name Name of the channel. Example: channel.name Result: "general"
nsfw True if the channel is NSFW. Example: channel.nsfw Result: false

User

Function Description
id Discord user ID. Example: user.id Result: 160067691783127041
bot True if the user is a bot. Example: user.bot Result: false
mention Create a mention to the user. Example: user.mention Result: "<@160105994217586689>"
discrim Discriminator of the user. Example: user.discrim Result: "6045"
name Name of the user. Example: user.name Result: "Miki"

String

Function Description
.length Length of the string. Example: "miki".length Result: 4
.upper() Converts a string to uppercase letters. Example: "miki".upper() Result: "MIKI"
.lower() Converts a string to lowercase letters. Example: "MIKI".lower() Result: "miki"
.has(str) Check if the string contains a substring. Example: "Hello Miki".has("Miki") Result: true
.startsWith(str) Check if the string starts with a substring. Example: "Hello Miki".startsWith("Hello") Result: true
.endsWith(str) Check if the string ends with a substring. Example: "Hello Miki".endsWith("Miki") Result: true
.replace(old, new) Replace every occurence of the old string with the new string. Example: "Hello Beld".replace("Beld", "Veld") Result: "Hello Veld"
.trim() Remove every whitespace character at the begin and end of the string. Example: " Hello Miki ".trim() Result: "Hello Miki"
.split() Splits the string into an list of substrings using every whitespace character. Example: "hello world".split() Result: ["hello", "world"]
.split(seperator) Splits the string into an alistrray of substrings using the given seperator. Example: "hello-world".split("-") Result: ["hello", "world"]
.indexOf(str) Get the position of the substring in the string. Example: "Hello Miki".indexOf("Miki") Result: 6
.remove(index, length) Remove a range in the string. Example: "Hello Miki".remove(0, 6) Result: "Miki"

Number

Function Description
.toFixed(length) Converts the number into a string with the fixed amount of decimals. Example: (1.23456).toFixed(2) Result: "1.23"

List

Function Description
.length Amount of items in the list. Example: ["hello", "world"].length Result: 2
.has(item) Check if the list contains the item. Example: ["hello", "world"].has("hello") Result: true
.add(item) Add a new item to the list. Example: ["hello"].add("world") Result: ["hello", "world"]
.del(item) Removes am item from the list. Example: ["hello", "world"].del("world") Result: ["hello"]
.join(seperator) Combine all the values into a single string with a separator. Example: ["hello", "world"].join(" ") Result: "hello world"
.map(function) Create a new list and map all the values by calling the function. Example: ["hello", "world"].map(i => i.upper()) Result: ["HELLO", "WORLD"]
.filter(function) Create a new list and add the values that pass the function filter. Example: ["hello", "world"].filter(i => i = "hello") Result: ["hello"]
.take(amount) Create a new list and insert the first X amount of values. Example: ["hello", "world"].take(1) Result: ["hello"]
.skip(amount) Create a new list and remove the first X amount of values. Example: ["hello", "world"].skip(1) Result: ["world"]
.reverse() Create a new list and reverse the order of the items. Example: ["hello", "world"].reverse() Result: ["world", "hello"]
.random() Get a random value from the list. Example: ["hello", "world"].random() Result: "hello" or "world"

Object

Function Description
.keys All the keys in the object. Example: {name: "Miki", discrim: "6045"}.keys Result: ["name", "discrim"]
.values All the values in the object. Example: {name: "Miki", discrim: "6045"}.values Result: ["Miki", "6045"]
.has(key) True if the key exists in the object. Example: {name: "Miki", discrim: "6045"}.has("name") Result: true
.del(key) Removes the key from the object. Example: {name: "Miki", discrim: "6045"}.del("name") Result: {discrim: "6045"}

Range

Function Description
.from Start position of the range. Example: (1..10).from Result: 1
.to End position of the range. Example: (1..10).to Result: 10
.toArray() Create an list with every number in the range. Example: (1..10).toArray() Result: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
.random() Get a random number from the range. Example: (1..10).random() Result: A number between 1 and 10
.map(function) Create a new list and map every number in the range. Example: (1..10).map(i => i.upper()) Result: ["HELLO", "WORLD"]

Function

Function Description
.name Get the function name. Example: (fun test() end).name Result: "test"

Embed

Function Description
.title(value) Sets the title of the embed. Example: embed().title("New title")
.image(url) Sets the image of the embed. Example: embed().image("https://media.giphy.com/media/H9jrEhsr1pUMo/giphy.gif")
.color(value) Sets the color of the embed. The value can either be a hex color (#FFFFFF) or a HTML color (red). Example: embed().color("red")
.author(name, image, url) Sets the author of the embed. The image and url parameters are optional. Example: embed().author("Miki")
.footer(content, url) Sets the footer content of the embed. The url parameter is optional. Example: embed().footer("Miki")
.field(title, content) Adds a new field to the embed. Example: embed().field("Title", "Content")
.inlineField(tile, content) Adds a new inline field to the embed. Example: embed().inlineField("Title", "Content")

Storage

Function Description
.keys All the keys in the storage. Example: storage.keys Result: ["name"]
.set(key, value) Changes the value in the storage. Example: storage.set("name", "Miki")
.get(key) Gets the value from the storage. Example: storage.get("name") Result: "Miki"
.del(key) Remove the value from the storage. Example: storage.del("name")
.clear() Clears the storage. Example: storage.clear()

© Veld Technologies, 2022, All Rights Reserved

Crowdin | Agile localization for tech companies