Fix inbound caller detection for Bird voice webhooks

This commit is contained in:
Jeppe B
2026-03-04 11:35:49 +01:00
parent 37f0f280d8
commit cc4e59a3e5
@@ -130,6 +130,16 @@ class birdVoiceWebhooksRoute
$payload['caller'] ?? null,
$payload['source'] ?? null,
$payload['originator'] ?? null,
$payload['call']['from'] ?? null,
$payload['call']['caller'] ?? null,
$payload['data']['from'] ?? null,
$payload['data']['caller'] ?? null,
$payload['payload']['from'] ?? null,
$payload['payload']['caller'] ?? null,
$payload['message']['from'] ?? null,
$payload['message']['caller'] ?? null,
$payload['callEvent']['from'] ?? null,
$payload['callEvent']['caller'] ?? null,
];
foreach ($candidates as $candidate) {
@@ -139,14 +149,66 @@ class birdVoiceWebhooksRoute
}
}
$recursiveCandidates = $this->extractRecursiveCallerCandidates($payload);
foreach ($recursiveCandidates as $candidate) {
$normalized = $this->normalizePhoneCandidate($candidate);
if ($normalized !== null) {
return $normalized;
}
}
return null;
}
private function extractRecursiveCallerCandidates(array $payload): array
{
$candidates = [];
$priorityKeys = [
'from', 'caller', 'source', 'originator', 'from_number', 'fromnumber', 'caller_id', 'callerid', 'msisdn'
];
$this->collectCandidates($payload, $priorityKeys, $candidates);
return $candidates;
}
private function collectCandidates(array $node, array $priorityKeys, array &$candidates): void
{
foreach ($node as $key => $value) {
$normalizedKey = strtolower((string)$key);
if (in_array($normalizedKey, $priorityKeys, true)) {
$candidates[] = $value;
}
if (is_array($value)) {
if (
array_key_exists('phone_number', $value)
|| array_key_exists('phone', $value)
|| array_key_exists('number', $value)
|| array_key_exists('msisdn', $value)
|| array_key_exists('e164', $value)
) {
$candidates[] = $value;
}
$this->collectCandidates($value, $priorityKeys, $candidates);
}
}
}
private function normalizePhoneCandidate(mixed $candidate): ?array
{
if (is_array($candidate)) {
$country = isset($candidate['country_code']) ? (int)$candidate['country_code'] : null;
$phone = $candidate['phone_number'] ?? $candidate['phone'] ?? $candidate['number'] ?? null;
if ($country === null && isset($candidate['countryCode'])) {
$country = (int)$candidate['countryCode'];
}
$phone = $candidate['phone_number']
?? $candidate['phone']
?? $candidate['number']
?? $candidate['msisdn']
?? $candidate['e164']
?? $candidate['from']
?? null;
if ($phone !== null) {
return $this->normalizePhone((string)$phone, $country);
}
@@ -162,10 +224,11 @@ class birdVoiceWebhooksRoute
private function normalizePhone(string $raw, ?int $defaultCountryCode = 45): ?array
{
$trimmed = trim($raw);
$trimmed = trim(str_ireplace('tel:', '', $raw));
if ($trimmed === '') {
return null;
}
$digits = preg_replace('/\D+/', '', $trimmed);
if (!is_string($digits) || $digits === '') {
return null;
@@ -175,8 +238,20 @@ class birdVoiceWebhooksRoute
$phone = $digits;
if (str_starts_with($trimmed, '+') && strlen($digits) > 8) {
$country = (int)substr($digits, 0, 2);
$phone = substr($digits, 2);
$candidateCodes = [1, 45, 46, 47, 31, 44, 49];
$matchedCode = null;
foreach ($candidateCodes as $candidateCode) {
$codeString = (string)$candidateCode;
if (str_starts_with($digits, $codeString) && strlen($digits) > strlen($codeString) + 4) {
$matchedCode = $candidateCode;
break;
}
}
if ($matchedCode !== null) {
$country = $matchedCode;
$phone = substr($digits, strlen((string)$matchedCode));
}
}
if ($country === null || $country <= 0 || $phone === '') {
@@ -196,7 +271,19 @@ class birdVoiceWebhooksRoute
return true;
}
return (new subusers_o())->getSubuserByPhone($countryCode, $phone) !== null;
$defaultCountryRows = (new users_o())->getFieldsWhere([
'phone_country_code' => 45,
'phone' => $phone,
], ['id']);
if (!empty($defaultCountryRows)) {
return true;
}
if ((new subusers_o())->getSubuserByPhone($countryCode, $phone) !== null) {
return true;
}
return (new subusers_o())->getSubuserByPhone(45, $phone) !== null;
}
private function extractDepartmentId(array $payload, array $departmentOptions): ?int
@@ -229,6 +316,8 @@ class birdVoiceWebhooksRoute
$payload['digits'] ?? null,
$payload['dtmf'] ?? null,
$payload['digit'] ?? null,
$payload['data']['digits'] ?? null,
$payload['call']['digits'] ?? null,
];
foreach ($digitCandidates as $candidate) {