| 1 | """Phone-number normalization helpers for WhatsApp integration.""" |
| 2 | |
| 3 | from __future__ import annotations |
| 4 | |
| 5 | import re |
| 6 | from collections.abc import Iterable |
| 7 | |
| 8 | |
| 9 | _JID_SUFFIX_RE = re.compile(r"[@:].*") |
| 10 | _NON_DIGIT_RE = re.compile(r"\D+") |
| 11 | _LEADING_ZERO_RE = re.compile(r"^0+") |
| 12 | |
| 13 | |
| 14 | def normalize_number(raw: str) -> str: |
| 15 | """Normalize WhatsApp sender identifiers and phone numbers to comparable digits.""" |
| 16 | text = _JID_SUFFIX_RE.sub("", str(raw or "")) |
| 17 | digits = _NON_DIGIT_RE.sub("", text) |
| 18 | return _LEADING_ZERO_RE.sub("", digits) |
| 19 | |
| 20 | |
| 21 | def normalize_allowed_numbers(value: object) -> set[str]: |
| 22 | """Accept stored config as a list/tuple/set or comma-delimited string.""" |
| 23 | if isinstance(value, str): |
| 24 | candidates = value.split(",") |
| 25 | elif isinstance(value, Iterable): |
| 26 | candidates = value |
| 27 | else: |
| 28 | return set() |
| 29 | |
| 30 | normalized = {normalize_number(item) for item in candidates} |
| 31 | normalized.discard("") |
| 32 | return normalized |