---
title: Adding prosodyctl shell commands
---

Prosody allows modules to easily expose commands to its [interactive
shell](/doc/console). 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](/doc/modules/mod_announce):


```lua
module:add_item("shell-command", {
	section = "announce";
	section_desc = "Broadcast announcements to users";
	name = "online";
	desc = "Send announcement to all online users on the host";
	args = {
		{ name = "host", type = "string" };
		{ name = "text", type = "string" };
	};
	host_selector = "host";
	handler = function(shell, host, text) --luacheck: ignore 212/shell
		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.

```lua
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](/doc/developers/util/argparse) format:

```lua
flags = {
	-- The --role and --group options can be specified multiple times
	array_params = { role = true, group = true };
	-- The --expires-after option accepts a value
	value_params = { expires_after = true };
};
```

### Handler function

The handler function is executed to actually run the command:

```lua
handler = function (shell, parameter_name, opts)
	-- 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](/doc/developers/util/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.
