Adding prosodyctl shell commands
Prosody allows modules to easily expose commands to its interactive shell. These commands allow server admins to interact with your module while Prosody is running, and can be used to perform various actions or retrieve information.
Adding a new command
Using the module:add_item()
API, add a
shell-command
item for each command you want your module to
provide.
Here is an example of the announce:online()
command from
Prosody’s mod_announce:
:add_item("shell-command", {
module= "announce";
section = "Broadcast announcements to users";
section_desc = "online";
name = "Send announcement to all online users on the host";
desc = {
args { name = "host", type = "string" };
{ name = "text", type = "string" };
};
= "host";
host_selector = function(shell, host, text) --luacheck: ignore 212/shell
handler local msg = st.message({ from = host, id = id.short(), type = "headline" })
:text_tag("body", text);
local count = send_to_online(msg, host);
return true, ("Announcement sent to %d users"):format(count);
end;
});
section
- The section the command should be in. Either choose an existing section, or make a new one for your module.
section_desc
- Descriptive text to tell the admin what this section contains.
name
- The name of your command (excluding the section).
desc
- A description of what your command can be used for.
args
- Arguments (parameters) accepted by your command. Structure described below.
host_selector
- If your module is not global, you must set this to the name of one of your parameters, which allows Prosody to determine which instance of your module to route the command invocation to. More information below.
handler
- A function which will be executed when the admin invokes the command. Parameters are explained below.
Host selection
Prosody supports multiple “virtual hosts” within a single server process. Each virtual host runs its own copy of a module. This means that when a command is invoked, Prosody needs to decide which host it should be executed on.
To do this, you can specify one of the parameters as a “host selector”. Prosody will interpret this as a JID (which may be only a domain), and use the extracted domain to determine which virtual host to use.
Global modules (i.e. those that call
module:set_global()
) are only loaded once per Prosody
instance, and do not need a host selector parameter.
Argument specification
The args
field should be an array of tables, each
describing a parameter passed to your command.
= {
args { name = "parameter_name" };
};
This is used when generating the automatic help text for your command. Prosody does not currently perform any additional validation of parameters for you.
Additionally, when executing a command via prosodyctl shell’s
non-interactive shortcut mode, the user can specify command-line options
which get passed to the handler. To enable this, specify a
flags
field which follows the util.argparse format:
= {
flags -- The --role and --group options can be specified multiple times
= { role = true, group = true };
array_params -- The --expires-after option accepts a value
= { expires_after = true };
value_params };
Handler function
The handler function is executed to actually run the command:
= function (shell, parameter_name, opts)
handler -- Do stuff here
end;
The first parameter, shell
, is always passed, and
contains information about the current shell session and command
execution environment.
In particular, shell.session.print()
can be used to
output text to the user during the command execution.
After shell
, any parameters specified by the user are
passed.
Finally, if the command was executed via prosodyctl shell’s
non-interactive shortcut mode and there was a flags
field
in the command config, there will be an additional opts
parameter containing any parsed flags/options, passed after the normal
parameters. Handler functions should gracefully handle opts
being nil (as it will typically be when invoked via an interactive shell
session).
Return value
All commands should return two values:
success, message
The success
value should be true
if the
command completed successfully, and nil
or
false
if the command failed. In either case,
message
value should contain text which will be shown to
the user to inform them of the result (i.e. a success or error
message).
If the command includes asynchronous elements, it can return a promise. When the promise is
settled, status
is taken to be true
if the
promise resolved, or false
if the promise was rejected. The
resolved value or the rejection reason are used for the
message
value.
The shell
parameter
This parameter contains information relating to the current shell session. Although many of the fields are for Prosody’s own internal use, some will be of general use to module developers.
shell.session.print()
can be used to send output text to the user while the command is in progress (if you only have one thing to say, you can return it as the status message at the end instead).shell.repl
is a boolean which indicates whether this session is using the interactive shell mode or whether it is executed directly via prosodyctl.shell.session.width
contains the width of the user’s terminal in columns, if known.shell.session.request_input("password")
prompts the user for a password and returns a promise that resolves to the user’s input. This is useful to avoid requiring the user to pass sensitive passwords as parameters.