Source: lobby/LobbyPage/Chat/ChatCommandHandler.js

/**
 * The purpose of this class is to test if a given textual input of the current player
 * is not a chat message to be sent but a command to be performed locally or on the
 * server, and if so perform it.
 */
class ChatCommandHandler
{
	constructor(chatMessagesPanel, systemMessageFormat)
	{
		this.chatMessagesPanel = chatMessagesPanel;
		this.systemMessageFormat = systemMessageFormat;
	}

	/**
	 * @returns {boolean} true if the input was successfully parsed as a chat command.
	 */
	handleChatCommand(text)
	{
		if (!text.startsWith('/'))
			return false;

		let index = text.indexOf(" ");
		let command = text.substr(1, index == -1 ? undefined : index - 1);
		let args = index == -1 ? "" : text.substr(index + 1);

		let commandObj = this.ChatCommands[command] || undefined;
		if (!commandObj)
		{
			this.chatMessagesPanel.addText(
				Date.now() / 1000,
				this.systemMessageFormat.format(
					sprintf(translate("The command '%(cmd)s' is not supported. Try %(help)s to get more information about the supported commands."), {
						"cmd": setStringTags(escapeText(command), this.ChatCommandTags),
						"help": setStringTags(escapeText("/help"), this.ChatCommandTags)
					})));
			this.chatMessagesPanel.flushMessages();
			return true;
		}

		if (commandObj.moderatorOnly && Engine.LobbyGetPlayerRole(g_Nickname) != "moderator")
		{
			this.chatMessagesPanel.addText(
				Date.now() / 1000,
				this.systemMessageFormat.format(
					sprintf(translate("The command '%(cmd)s' is restricted to moderators."), {
						"cmd": setStringTags(escapeText(command), this.ChatCommandTags)
					})));
			this.chatMessagesPanel.flushMessages();
			return true;
		}

		let handler = commandObj && commandObj.handler || undefined;
		if (!handler)
			return false;

		return handler.call(this, args);
	}

	argumentCount(commandName, args)
	{
		if (args.trim())
			return false;

		this.chatMessagesPanel.addText(
			Date.now() / 1000,
			this.systemMessageFormat.format(
				sprintf(translate("The command '%(cmd)s' requires at least one argument."), {
					"cmd": setStringTags(commandName, this.ChatCommandTags)
				})));
		this.chatMessagesPanel.flushMessages();
		return true;
	}
}

/**
 * Color to highlight chat commands in the explanation.
 */
ChatCommandHandler.prototype.ChatCommandTags = {
	"color": "200 200 255"
};

/**
 * Commands that can be entered by clients via chat input.
 * A handler returns true if the user input should be sent as a chat message.
 */
ChatCommandHandler.prototype.ChatCommands = {
	"away": {
		"description": translate("Set your state to 'Away'."),
		"handler": function(args) {
			Engine.LobbySetPlayerPresence("away");
			return true;
		}
	},
	"back": {
		"description": translate("Set your state to 'Online'."),
		"handler": function(args) {
			Engine.LobbySetPlayerPresence("available");
			return true;
		}
	},
	"kick": {
		"description": translate("Kick a specified user from the lobby. Usage: /kick nick reason"),
		"handler": function(args) {
			let index = args.indexOf(" ");
			if (index == -1)
				Engine.LobbyKick(args, "");
			else
				Engine.LobbyKick(args.substr(0, index), args.substr(index + 1));
			return true;
		},
		"moderatorOnly": true
	},
	"ban": {
		"description": translate("Ban a specified user from the lobby. Usage: /ban nick reason"),
		"handler": function(args) {
			let index = args.indexOf(" ");
			if (index == -1)
				Engine.LobbyBan(args, "");
			else
				Engine.LobbyBan(args.substr(0, index), args.substr(index + 1));
			return true;
		},
		"moderatorOnly": true
	},
	"help": {
		"description": translate("Show this help."),
		"handler": function(args) {
			let isModerator = Engine.LobbyGetPlayerRole(g_Nickname) == "moderator";
			let txt = translate("Chat commands:");
			for (let command in this.ChatCommands)
				if (!this.ChatCommands[command].moderatorOnly || isModerator)
					// Translation: Chat command help format
					txt += "\n" + sprintf(translate("%(command)s - %(description)s"), {
						"command": setStringTags(command, this.ChatCommandTags),
						"description": this.ChatCommands[command].description
					});

			this.chatMessagesPanel.addText(
				Date.now() / 1000,
				this.systemMessageFormat.format(txt));

			this.chatMessagesPanel.flushMessages();
			return true;
		}
	},
	"me": {
		"description": translate("Send a chat message about yourself. Example: /me goes swimming."),
		"handler": function(args) {
			// Translation: Chat command
			return this.argumentCount(translate("/me"), args);
		}
	},
	"say": {
		"description": translate("Send text as a chat message (even if it starts with slash). Example: /say /help is a great command."),
		"handler": function(args) {
			// Translation: Chat command
			return this.argumentCount(translate("/say"), args);
		}
	},
	"clear": {
		"description": translate("Clear all chat scrollback."),
		"handler": function(args) {
			this.chatMessagesPanel.clearChatMessages();
			return true;
		}
	}
};