:param extractor:
        a function that takes one argument (the message) and returns a portion
        of message to be interpreted. To extract the text of a chat message,
        use ``lambda msg: msg['text']``.

    :type regex: str or regex object
    :param regex: the pattern to look for

    :param key: the part of match object to be used as key

    :return:
        a key function that returns ``match.group(key)`` as key (where ``match``
        is the match object) and the match object as a positional argument.
        If no match is found, it returns a 1-tuple ``(None,)`` as the key.
        This is to distinguish with the special ``None`` key in routing table.

def by_regex(extractor, regex, key=1):
    """
    :param extractor:
        a function that takes one argument (the message) and returns a portion
        of message to be interpreted. To extract the text of a chat message,
        use ``lambda msg: msg['text']``.

    :type regex: str or regex object
    :param regex: the pattern to look for

    :param key: the part of match object to be used as key

    :return:
        a key function that returns ``match.group(key)`` as key (where ``match``
        is the match object) and the match object as a positional argument.
        If no match is found, it returns a 1-tuple ``(None,)`` as the key.
        This is to distinguish with the special ``None`` key in routing table.
    """
    if _isstring(regex):
        regex = re.compile(regex)

    def f(msg):
        text = extractor(msg)
        match = regex.search(text)
        if match:
            index = key if isinstance(key, tuple) else (key,)
            return match.group(*index), (match,)
        else:
            return (None,),  # to distinguish with `None`
    return f