public static bool TryParseV2(string message, HashSet <string> registeredCommands, out ActionCommand command)
        {
            command = null;
            if (string.IsNullOrEmpty(message))
            {
                return(false);
            }

            try
            {
                // the message needs to start with the keyword after trim leading space.
                message = message.TrimStart();
                if (!message.StartsWith(_commandKey))
                {
                    return(false);
                }

                // Get the index of the separator between the command info and the data.
                int endIndex = message.IndexOf(_commandKey, _commandKey.Length);
                if (endIndex < 0)
                {
                    return(false);
                }

                // Get the command info (command and properties).
                int    cmdIndex = _commandKey.Length;
                string cmdInfo  = message.Substring(cmdIndex, endIndex - cmdIndex);

                // Get the command name
                int    spaceIndex  = cmdInfo.IndexOf(' ');
                string commandName =
                    spaceIndex < 0
                    ? cmdInfo
                    : cmdInfo.Substring(0, spaceIndex);

                if (registeredCommands.Contains(commandName))
                {
                    // Initialize the command.
                    command = new ActionCommand(commandName);
                }
                else
                {
                    return(false);
                }

                // Set the properties.
                if (spaceIndex > 0)
                {
                    string   propertiesStr   = cmdInfo.Substring(spaceIndex + 1).Trim();
                    string[] splitProperties = propertiesStr.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
                    foreach (string propertyStr in splitProperties)
                    {
                        string[] pair = propertyStr.Split(new[] { '=' }, count: 2, options: StringSplitOptions.RemoveEmptyEntries);
                        if (pair.Length == 2)
                        {
                            command.Properties[pair[0]] = UnescapeProperty(pair[1]);
                        }
                    }
                }

                command.Data = UnescapeData(message.Substring(endIndex + _commandKey.Length));
                return(true);
            }
            catch
            {
                command = null;
                return(false);
            }
        }
        public static bool TryParse(string message, HashSet <string> registeredCommands, out ActionCommand command)
        {
            command = null;
            if (string.IsNullOrEmpty(message))
            {
                return(false);
            }

            try
            {
                // Get the index of the prefix.
                int prefixIndex = message.IndexOf(Prefix);
                if (prefixIndex < 0)
                {
                    return(false);
                }

                // Get the index of the separator between the command info and the data.
                int rbIndex = message.IndexOf(']', prefixIndex);
                if (rbIndex < 0)
                {
                    return(false);
                }

                // Get the command info (command and properties).
                int    cmdIndex = prefixIndex + Prefix.Length;
                string cmdInfo  = message.Substring(cmdIndex, rbIndex - cmdIndex);

                // Get the command name
                int    spaceIndex  = cmdInfo.IndexOf(' ');
                string commandName =
                    spaceIndex < 0
                    ? cmdInfo
                    : cmdInfo.Substring(0, spaceIndex);

                if (registeredCommands.Contains(commandName))
                {
                    // Initialize the command.
                    command = new ActionCommand(commandName);
                }
                else
                {
                    return(false);
                }

                // Set the properties.
                if (spaceIndex > 0)
                {
                    string   propertiesStr   = cmdInfo.Substring(spaceIndex + 1);
                    string[] splitProperties = propertiesStr.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
                    foreach (string propertyStr in splitProperties)
                    {
                        string[] pair = propertyStr.Split(new[] { '=' }, count: 2, options: StringSplitOptions.RemoveEmptyEntries);
                        if (pair.Length == 2)
                        {
                            command.Properties[pair[0]] = Unescape(pair[1]);
                        }
                    }
                }

                command.Data = Unescape(message.Substring(rbIndex + 1));
                return(true);
            }
            catch
            {
                command = null;
                return(false);
            }
        }