From 6bf0a5cb5034a7e684dcc3500e841785237ce2dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:32:43 +0200 Subject: Adding upstream version 1:115.7.0. Signed-off-by: Daniel Baumann --- comm/chat/components/src/imCommands.sys.mjs | 289 ++++++++++++++++++++++++++++ 1 file changed, 289 insertions(+) create mode 100644 comm/chat/components/src/imCommands.sys.mjs (limited to 'comm/chat/components/src/imCommands.sys.mjs') diff --git a/comm/chat/components/src/imCommands.sys.mjs b/comm/chat/components/src/imCommands.sys.mjs new file mode 100644 index 0000000000..d28bd9a592 --- /dev/null +++ b/comm/chat/components/src/imCommands.sys.mjs @@ -0,0 +1,289 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +import { IMServices } from "resource:///modules/IMServices.sys.mjs"; +import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs"; +import { l10nHelper } from "resource:///modules/imXPCOMUtils.sys.mjs"; + +const lazy = {}; + +XPCOMUtils.defineLazyGetter(lazy, "_", () => + l10nHelper("chrome://chat/locale/commands.properties") +); + +export function CommandsService() {} +CommandsService.prototype = { + initCommands() { + this._commands = {}; + // The say command is directly implemented in the UI layer, but has a + // dummy command registered here so it shows up as a command (e.g. when + // using the /help command). + this.registerCommand({ + name: "say", + get helpString() { + return lazy._("sayHelpString"); + }, + usageContext: Ci.imICommand.CMD_CONTEXT_ALL, + priority: Ci.imICommand.CMD_PRIORITY_HIGH, + run(aMsg, aConv) { + throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED); + }, + }); + + this.registerCommand({ + name: "raw", + get helpString() { + return lazy._("rawHelpString"); + }, + usageContext: Ci.imICommand.CMD_CONTEXT_ALL, + priority: Ci.imICommand.CMD_PRIORITY_DEFAULT, + run(aMsg, aConv) { + let conv = IMServices.conversations.getUIConversation(aConv); + if (!conv) { + return false; + } + conv.sendMsg(aMsg); + return true; + }, + }); + + this.registerCommand({ + // Reference the command service so we can use the internal properties + // directly. + cmdSrv: this, + + name: "help", + get helpString() { + return lazy._("helpHelpString"); + }, + usageContext: Ci.imICommand.CMD_CONTEXT_ALL, + priority: Ci.imICommand.CMD_PRIORITY_DEFAULT, + run(aMsg, aConv) { + aMsg = aMsg.trim(); + let conv = IMServices.conversations.getUIConversation(aConv); + if (!conv) { + return false; + } + + // Handle when no command is given, list all possible commands that are + // available for this conversation (alphabetically). + if (!aMsg) { + let commands = this.cmdSrv.listCommandsForConversation(aConv); + if (!commands.length) { + return false; + } + + // Concatenate the command names (separated by a comma and space). + let cmds = commands + .map(aCmd => aCmd.name) + .sort() + .join(", "); + let message = lazy._("commands", cmds); + + // Display the message + conv.systemMessage(message); + return true; + } + + // A command name was given, find the commands that match. + let cmdArray = this.cmdSrv._findCommands(aConv, aMsg); + + if (!cmdArray.length) { + // No command that matches. + let message = lazy._("noCommand", aMsg); + conv.systemMessage(message); + return true; + } + + // Only show the help for the one of the highest priority. + let cmd = cmdArray[0]; + + let text = cmd.helpString; + if (!text) { + text = lazy._("noHelp", cmd.name); + } + + // Display the message. + conv.systemMessage(text); + return true; + }, + }); + + // Status commands + let status = { + back: "AVAILABLE", + away: "AWAY", + busy: "UNAVAILABLE", + dnd: "UNAVAILABLE", + offline: "OFFLINE", + }; + for (let cmd in status) { + let statusValue = Ci.imIStatusInfo["STATUS_" + status[cmd]]; + this.registerCommand({ + name: cmd, + get helpString() { + return lazy._("statusCommand", this.name, lazy._(this.name)); + }, + usageContext: Ci.imICommand.CMD_CONTEXT_ALL, + priority: Ci.imICommand.CMD_PRIORITY_HIGH, + run(aMsg) { + IMServices.core.globalUserStatus.setStatus(statusValue, aMsg); + return true; + }, + }); + } + }, + unInitCommands() { + delete this._commands; + }, + + registerCommand(aCommand, aPrplId) { + let name = aCommand.name; + if (!name) { + throw Components.Exception("", Cr.NS_ERROR_INVALID_ARG); + } + + if (!this._commands.hasOwnProperty(name)) { + this._commands[name] = {}; + } + this._commands[name][aPrplId || ""] = aCommand; + }, + unregisterCommand(aCommandName, aPrplId) { + if (this._commands.hasOwnProperty(aCommandName)) { + let prplId = aPrplId || ""; + let commands = this._commands[aCommandName]; + if (commands.hasOwnProperty(prplId)) { + delete commands[prplId]; + } + if (!Object.keys(commands).length) { + delete this._commands[aCommandName]; + } + } + }, + listCommandsForConversation(aConversation) { + let result = []; + let prplId = aConversation && aConversation.account.protocol.id; + for (let name in this._commands) { + let commands = this._commands[name]; + if (commands.hasOwnProperty("")) { + result.push(commands[""]); + } + if (prplId && commands.hasOwnProperty(prplId)) { + result.push(commands[prplId]); + } + } + if (aConversation) { + result = result.filter(this._usageContextFilter(aConversation)); + } + return result; + }, + // List only the commands for a protocol (excluding the global commands). + listCommandsForProtocol(aPrplId) { + if (!aPrplId) { + throw new Error("You must provide a prpl ID."); + } + + let result = []; + for (let name in this._commands) { + let commands = this._commands[name]; + if (commands.hasOwnProperty(aPrplId)) { + result.push(commands[aPrplId]); + } + } + return result; + }, + _usageContextFilter(aConversation) { + let usageContext = + Ci.imICommand["CMD_CONTEXT_" + (aConversation.isChat ? "CHAT" : "IM")]; + return c => c.usageContext & usageContext; + }, + _findCommands(aConversation, aName) { + let prplId = null; + if (aConversation) { + let account = aConversation.account; + if (account.connected) { + prplId = account.protocol.id; + } + } + + let commandNames; + // If there is an exact match for the given command name, + // don't look at any other commands. + if (this._commands.hasOwnProperty(aName)) { + commandNames = [aName]; + } else { + // Otherwise, check if there is a partial match. + commandNames = Object.keys(this._commands).filter(command => + command.startsWith(aName) + ); + } + + // If a single full command name matches the given (partial) + // command name, return the results for that command name. Otherwise, + // return an empty array (don't assume a certain command). + let cmdArray = []; + for (let commandName of commandNames) { + let matches = []; + + // Get the 2 possible commands (the global and the proto specific). + let commands = this._commands[commandName]; + if (commands.hasOwnProperty("")) { + matches.push(commands[""]); + } + if (prplId && commands.hasOwnProperty(prplId)) { + matches.push(commands[prplId]); + } + + // Remove the commands that can't apply in this context. + if (aConversation) { + matches = matches.filter(this._usageContextFilter(aConversation)); + } + + if (!matches.length) { + continue; + } + + // If we have found a second matching command name, return the empty array. + if (cmdArray.length) { + return []; + } + + cmdArray = matches; + } + + // Sort the matching commands by priority before returning the array. + return cmdArray.sort((a, b) => b.priority - a.priority); + }, + executeCommand(aMessage, aConversation, aReturnedConv) { + if (!aMessage) { + throw Components.Exception("", Cr.NS_ERROR_INVALID_ARG); + } + + let matchResult; + if ( + aMessage[0] != "/" || + !(matchResult = /^\/([a-z0-9]+)(?: |$)([\s\S]*)/.exec(aMessage)) + ) { + return false; + } + + let [, name, args] = matchResult; + + let cmdArray = this._findCommands(aConversation, name); + if (!cmdArray.length) { + return false; + } + + // cmdArray contains commands sorted by priority, attempt to apply + // them in order until one succeeds. + if (!cmdArray.some(aCmd => aCmd.run(args, aConversation, aReturnedConv))) { + // If they all failed, print help message. + this.executeCommand("/help " + name, aConversation); + } + return true; + }, + + QueryInterface: ChromeUtils.generateQI(["imICommandsService"]), + classDescription: "Commands", +}; -- cgit v1.2.3