def _match_by_prefix()

in api/views/phones.py [0:0]


def _match_by_prefix(text: str, candidate_numbers: set[str]) -> MatchByPrefix | None:
    """
    Look for a prefix in a text message matching a set of candidate numbers.

    Arguments:
    * A SMS text message
    * A set of phone numbers in E.164 format

    Return None if no prefix was found, or MatchByPrefix with likely match(es)
    """
    # Gather potential region codes, needed by PhoneNumberMatcher
    region_codes = set()
    for candidate_number in candidate_numbers:
        pn = phonenumbers.parse(candidate_number)
        if pn.country_code:
            region_codes |= set(
                phonenumbers.region_codes_for_country_code(pn.country_code)
            )

    # Determine where the message may start
    #  PhoneNumberMatcher doesn't work well with a number directly followed by text,
    #  so just feed it the start of the message that _may_ be a number.
    msg_start = 0
    phone_characters = set(string.digits + string.punctuation + string.whitespace)
    while msg_start < len(text) and text[msg_start] in phone_characters:
        msg_start += 1

    # Does PhoneNumberMatcher detect a full number at start of message?
    text_to_match = text[:msg_start]
    for region_code in region_codes:
        for match in phonenumbers.PhoneNumberMatcher(text_to_match, region_code):
            e164 = phonenumbers.format_number(
                match.number, phonenumbers.PhoneNumberFormat.E164
            )

            # Look for end of prefix
            end = match.start + len(match.raw_string)
            found_one_sep = False
            while True:
                if end >= len(text):
                    break
                elif text[end].isspace():
                    end += 1
                elif text[end] in _SMS_SEPARATORS and not found_one_sep:
                    found_one_sep = True
                    end += 1
                else:
                    break

            prefix = text[:end]
            if e164 in candidate_numbers:
                numbers = [e164]
            else:
                numbers = []
            return MatchByPrefix(
                match_type="full", prefix=prefix, detected=e164, numbers=numbers
            )

    # Is there a short prefix? Return all contacts whose last 4 digits match.
    text_prefix_match = _SMS_SHORT_PREFIX_RE.match(text)
    if text_prefix_match:
        text_prefix = text_prefix_match.group(0)
        digits = set(string.digits)
        digit_suffix = "".join(digit for digit in text_prefix if digit in digits)
        numbers = [e164 for e164 in candidate_numbers if e164[-4:] == digit_suffix]
        return MatchByPrefix(
            match_type="short",
            prefix=text_prefix,
            detected=digit_suffix,
            numbers=sorted(numbers),
        )

    # No prefix detected
    return None