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