def match(self, user, message):
        """
        Returns a response message if a match is found, otherwise None
        :param user: The requesting client
        :type  user: agentml.User

        :param message: The message to match
        :type  message: agentml.Message

        :rtype: str or None
        """
        self._log.info('Attempting to match message against Pattern: {pattern}'
                       .format(pattern=self.pattern.pattern if hasattr(self.pattern, 'pattern') else self.pattern))
        self.user = user

        # Make sure the topic matches (if one is defined)
        if user.topic != self.topic:
            self._log.debug('User topic "{u_topic}" does not match Trigger topic "{t_topic}", skipping check'
                            .format(u_topic=user.topic, t_topic=self.topic))
            return

        def get_response():
            # Does the user have a limit for this response enforced?
            if user.is_limited(self):
                if self.ulimit_blocking:
                    self._log.debug('An active blocking limit for this trigger is being enforced against the user '
                                    '{uid}, no trigger will be matched'.format(uid=user.id))
                    raise LimitError

                self._log.debug('An active limit for this response is being enforced against the user {uid}, '
                                'skipping'.format(uid=user.id))
                return ''

            # Is there a global limit for this response enforced?
            if self.agentml.is_limited(self):
                if self.glimit_blocking:
                    self._log.debug('An active blocking limit for this trigger is being enforced globally, no trigger '
                                    'will be matched')
                    raise LimitError

                self._log.debug('An active limit for this response is being enforced against the user {uid}, '
                                'skipping'.format(uid=user.id))
                return ''

            # Chance testing
            if self.chance is not None and self.chance != 100:
                # Chance succeeded
                if self.chance >= random.uniform(0, 100):
                    self._log.info('Trigger had a {chance}% chance of being selected and succeeded selection'
                                   .format(chance=self.chance))
                # Chance failed
                else:
                    if self.chance_blocking:
                        self._log.info('Trigger had a blocking {chance}% chance of being selected but failed selection'
                                       ', no trigger will be matched'.format(chance=self.chance))
                        raise ChanceError

                    self._log.info('Response had a {chance}% chance of being selected but failed selection'
                                   .format(chance=self.chance))
                    return ''

            random_response = self._responses.random(user)
            if not random_response and self.blocking:
                self._log.info('Trigger was matched, but there are no available responses and the trigger is blocking '
                               'any further attempts. Giving up')
                raise ParserBlockingError

            if random_response:
                self.apply_reactions(user)
            else:
                self._log.info('Trigger was matched, but there are no available responses')
                random_response = ''

            return random_response

        # String match
        if isinstance(self.pattern, string_types) and str(message) == self.pattern:
            self._log.info('String Pattern matched: {match}'.format(match=self.pattern))
            return get_response()

        # Regular expression match
        if hasattr(self.pattern, 'match'):
            match = self.pattern.match(str(message))
            if match:
                self._log.info('Regex pattern matched: {match}'.format(match=self.pattern.pattern))

                # Parse pattern wildcards
                self.stars['normalized'] = match.groups()
                for message_format in [message.CASE_PRESERVED, message.RAW]:
                    message.format = message_format
                    format_match = self.pattern.match(str(message))
                    if format_match:
                        self.stars[message_format] = format_match.groups()

                self._log.debug('Assigning pattern wildcards: {stars}'.format(stars=str(self.stars)))
                return get_response()