Fix inbound caller detection for Bird voice webhooks
This commit is contained in:
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user