65 lines
1.9 KiB
Python
65 lines
1.9 KiB
Python
from sqlalchemy import select
|
|
from sqlalchemy.orm import Session
|
|
|
|
from ..models import PhoneNumber, VoiceCall
|
|
from .validators import format_bonvoice_destination, normalize_phone_digits
|
|
|
|
|
|
def find_inbound_by_did(db: Session, did_value: str) -> PhoneNumber | None:
|
|
did = normalize_phone_digits(did_value)
|
|
if len(did) != 10:
|
|
return None
|
|
return db.scalar(
|
|
select(PhoneNumber).where(
|
|
PhoneNumber.phone_number == did,
|
|
PhoneNumber.status == "assigned",
|
|
PhoneNumber.user_id.is_not(None),
|
|
)
|
|
)
|
|
|
|
|
|
def _phone_variants(value: str) -> set[str]:
|
|
raw = value.strip()
|
|
normalized = normalize_phone_digits(raw)
|
|
variants = {raw, normalized, format_bonvoice_destination(raw)}
|
|
if len(normalized) == 10:
|
|
variants.add(f"91{normalized}")
|
|
return {item for item in variants if item}
|
|
|
|
|
|
def find_pending_outbound_call(db: Session, *, from_value: str, to_value: str) -> VoiceCall | None:
|
|
"""
|
|
Match a pending outbound call to Bonvoice media `start`.
|
|
|
|
Outbound Bonvoice streams report:
|
|
- `to` = leg-A caller ID (purchased DID, e.g. 7946350706)
|
|
- `from` = destination/callee (e.g. 919149154337)
|
|
"""
|
|
did_candidates = _phone_variants(to_value)
|
|
owned_dids = {
|
|
normalize_phone_digits(value)
|
|
for value in did_candidates
|
|
if len(normalize_phone_digits(value)) == 10
|
|
}
|
|
if not owned_dids:
|
|
return None
|
|
|
|
candidates = db.scalars(
|
|
select(VoiceCall)
|
|
.where(
|
|
VoiceCall.direction == "outbound",
|
|
VoiceCall.status == "initiated",
|
|
VoiceCall.from_did.in_(owned_dids),
|
|
)
|
|
.order_by(VoiceCall.created_at.desc())
|
|
.limit(5)
|
|
).all()
|
|
if not candidates:
|
|
return None
|
|
|
|
callee_variants = _phone_variants(from_value)
|
|
for call in candidates:
|
|
if _phone_variants(call.to_number) & callee_variants:
|
|
return call
|
|
|
|
return None
|