Tutorials: Intro, command
Creating a Command
In this tutorial we're going to make a /slap command, that will cause the user to slap the user specified. Make sure you've read the Introduction to Scripting in ChatZilla before you continue.
How to Define Commands
The basis for plugin-based commands is the following line:
plugin.commands = client.commandManager.defineCommands(plugin.cmdList);
This will add the commands defined in plugin.cmdList to the available commands, and store a blob of data in plugin.commands (used to remove them later). This is how you should add any commands to ChatZilla (for the simplicity and elegance, if nothing else).
The variable plugin.cmdList is an array of arrays, where the outer array is the list of commands, and each item in the inner arrays defines one part of an individual command.
The inner array items are as follows:
|
|
Item Type
|
Description
|
|
1
|
String
|
The command name.
|
|
2
|
Function or string
|
Either the function to handle the command, or a semicolon (;) separated list of commands to run ("command1 params; command2 params").
|
|
3
|
Number or null
|
A combination of various flags, defined below, or null to use the defaults.
|
|
4
|
String, optional
|
The usage of the command, which is explained in detail later.
|
|
In other words, plugin.cmdList is an array of items of this form:
[command, handler, flags, usage]
Command Flags
A combination of any of the following may be used (separate them with a pipe (|) character to combine them).
|
CMD_CONSOLE
|
The command may be run from the 'console' (i.e. the input box in ChatZilla). Commands without this flag may be run from scripts, but wont show up in the list of commands available to the user.
|
Included in default
|
|
CMD_NO_HELP
|
The command has no localisable help.
|
Included in default
|
|
CMD_NEED_NET
|
Command needs a network, and is guaranteed to have a valid e.network object when called.
|
|
|
CMD_NEED_SRV
|
Command needs a connected server, and will get a valid e.server object when called.
|
|
|
CMD_NEED_CHAN
|
Command needs a channel view, but not nessessarily a joined channel (i.e. the view may still be there, but the user may not actually be a member of it any more). e.channel will be a valid object when called.
|
|
|
CMD_NEED_USER
|
Command needs a user (i.e. in a /query view), and will get a valid e.user object when called.
|
|
|
Usage 101
The usage system for the Command Manager is more sophisticated than one might imagine, so here's a basic run-down on how to use it:
-
Parameters go inside angle brackets (for example, <nickname>).
-
Parameter by default only match a single 'word' of the input.
-
Apart from those parameter names listed below, a parameter will match a single word of the input, and pass the string value to the function using the parameter's name.
Thus, <nickname> will match one word only, and pass the value to the handler function as the property nickname (see Coding the Command Handler below for more details).
Exceptions: there are some other parameter names that are handled differently. For example, currently <reason>, <action>, <text>, <message>, <params>, <font>, <expression>, <ircCommand>, <prefValue>, <newTopic>, <file>, <password>, <commandList>, <commands> and <description> are all synonyms for the <rest> type.
-
A parameter <state> matches yes, on, true, 1, , false, off, no, and passes the value as a boolean (using the obvious mapping, e.g. yes is passed as true, etc.).
-
A parameter <toggle> matches those of <state>, but also allows the value toggle.
If this parameter is a valid <state> value, it's passed as a boolean exactly as with <state>. Otherwise, it will pass the string toggle (which is the only other value that matches).
-
A final parameter <rest> matches all the remaining input.
-
A final parameter <...> causes the parameter before it to be a list, and any unmatched items are also matched according to the penultimate item.
The items are returned in an array (where 'param' is the name for the penultimate parameter) called paramList. paramList[0] is the value of the penultimate item (and all subsequent array indices are for the extra items allowed by <...>). For example, the usage <nick> <...> will pass the list of 'nick's as an array called nickList.
-
Sections in square brackets ([]) are optional. The contents of an optional block must either be there in their entirety, or not at all.
For example, the usage [<p1> <p2> [<p3> <p4>]] will only match when there are 0, 2 or 4 parameters. With 1 or 3 parameters, <p2> or <p4> (respectively) would be required.
OK, so that was more than a quick explanation, the basic points to get from this are that you put your parameters in <> and any optional bits in [].
Our Command's Definition
This should be fairly self-explanatory now - the command /slap will use the handler cmdSlap, it can be used directly by the user, doesn't have any help, and must be done within a channel. The command has a single required parameter, nickname. The item in plugin.cmdList will therefore be:
[slap, cmdSlap, CMD_CONSOLE | CMD_NO_HELP | CMD_NEED_CHAN, <nickname>]
We will be coding cmdSlap in a minute, first we must write the full command defining code. The following code needs to go inside the init method, as defined in the Introduction to Scripting in ChatZilla tutorial.
plugin.cmdList = [ [slap, cmdSlap, CMD_CONSOLE | CMD_NO_HELP | CMD_NEED_CHAN, <nickname>] ];
Then, inside the enable method, we put:
plugin.commands = client.commandManager.defineCommands(plugin.cmdList);
Finally, inside the disable method, we put:
client.commandManager.removeCommands(plugin.commands);
By defining plugin.cmdList once when the plugin is loaded, we can then use it inside enable to add the command when needed. To remove the commands, we call removeCommands with the blob of data saved from defineCommands. As a plugin, we do not care what this blob is and should never depend on what is stored in it.
If you are wondering why we've called it nickname, this will become clear in the Menu tutorial but is not important for now.
Coding the Command Handler
Command handling functions, like many other event handling functions, take a single parameter, which has all the details of the event and data being passed in (this is where parameters and other useful information will come from). Convention dictates that this parameter is called e. We now have the following template for the command handler:
function cmdSlap(e) { // Code here! }
There are a number of properties that might be on the object e:
-
network
-
server
-
channel
-
user
Which of these are set will depend on the current view when the command is run, for example, from a channel view (as our command will be used from) e.network, e.server and e.channel will be set. The flags passed to the Command Manager define which of these items the command requires, in our case we only required a channel object.
The object e also contains the parameters passed to the command, using the names in the usage definition. Thus our only required parameter will be stored in e.nickname.
Note: multi-word parameters should be defined in the usage with a hyphen between words (e.g. <my-param>) but will be stored without the hyphen, and the following letter upper-cased (e.g. e.myParam).
Since the Command Manager will have made sure we're in a channel and have the e.nickname data, the code is really simple:
dispatch(me slaps + e.nickname + about a bit with a small fish);
The dispatch call is exactly the same as entering / followed by the text into ChatZilla. All we do is make the user execute the command /me slaps <nickname> about a bit with a small fish where <nickname> is the nickname passed to the command.
Thus, the entire cmdSlap code is:
function cmdSlap(e) { dispatch(me slaps + e.nickname + about a bit with a small fish); }
And that's it! Your first command.
If you aren't sure about any of the code, have a look at the final script in the source code directory.
Extension
You may have wondered if there are any requirements for the parameter <nickname> - does anything check this?
The answer is no - you can specify anything for the parameter, the Command Manager only checks that it's there, not what it is. For example, you can do /slap himself or /slap herself to make it look like you're slapping yourself.
Why not check that the parameter is a valid nickname? Firstly, it makes the example shorter and easier to follow, and secondly, it's actually non-trivial to check if a given string is an existing nickname.
Feel free to experiment and try and code this yourself - if you want the command to fail, just return from the function before calling dispatch.
Hint
The best way to check if a user exists is to use the e.channel.getUser function (see the first result here). This function will return the object for the user specified, if they exist - or undefined otherwise.
Powered by the Content Parser System, copyright 2002 - 2009 James G. Ross.
|