Refactor Bird API client to extract reusable executeCallAndHangupWhenAccepted method, update callGateAndHangupWhenAccepted and test outbound call logic to use shared implementation, and enhance timeout handling.
This commit is contained in:
@@ -382,6 +382,15 @@ class bird
|
||||
}
|
||||
|
||||
public function createOutboundTestCallAndHangupWhenAccepted(string $workspaceId, string $channelId, array $options = []): array
|
||||
{
|
||||
return $this->executeCallAndHangupWhenAccepted($workspaceId, $channelId, $options, 'BIRD_TEST_OUTBOUND_CALL');
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal method to place a call, wait for it to be accepted/ongoing, and then hang up.
|
||||
* Used by both test outbound calls and actual gate calls.
|
||||
*/
|
||||
private function executeCallAndHangupWhenAccepted(string $workspaceId, string $channelId, array $options, string $logPrefix): array
|
||||
{
|
||||
$pollIntervalSeconds = max(1, (int)($options['pollIntervalSeconds'] ?? 2));
|
||||
$maxPollSeconds = max(5, (int)($options['maxPollSeconds'] ?? 30));
|
||||
@@ -396,15 +405,20 @@ class bird
|
||||
}
|
||||
$payload['to'] = $targetNumber;
|
||||
|
||||
// Ensure Bird terminates an unanswered call after we've stopped polling for it.
|
||||
if (!isset($payload['timeout'])) {
|
||||
$payload['timeout'] = $maxPollSeconds;
|
||||
}
|
||||
|
||||
$this->logBirdAction(
|
||||
'BIRD_TEST_OUTBOUND_CALL_START',
|
||||
$logPrefix . '_START',
|
||||
'workspace=' . $workspaceId . ' channel=' . $channelId . ' to=' . $targetNumber
|
||||
);
|
||||
|
||||
$createResponse = $this->createVoiceCall($workspaceId, $channelId, $payload);
|
||||
$callId = $this->extractId($createResponse);
|
||||
if ($callId === null) {
|
||||
$this->logBirdAction('BIRD_TEST_OUTBOUND_CALL_NO_ID', 'Call accepted but no call ID returned', 0);
|
||||
$this->logBirdAction($logPrefix . '_NO_ID', 'Call accepted but no call ID returned', 0);
|
||||
return [
|
||||
'to' => $targetNumber,
|
||||
'to_e164' => $targetNumber,
|
||||
@@ -422,7 +436,7 @@ class bird
|
||||
$status = $this->extractStatus($current);
|
||||
|
||||
$this->logBirdAction(
|
||||
'BIRD_TEST_OUTBOUND_CALL_POLL',
|
||||
$logPrefix . '_POLL',
|
||||
'call=' . $callId . ' attempt=' . $attempt . '/' . $maxAttempts . ' status=' . ($status ?? 'unknown')
|
||||
);
|
||||
|
||||
@@ -435,7 +449,7 @@ class bird
|
||||
}
|
||||
}
|
||||
$hangupResponse = $this->hangupVoiceCall($workspaceId, $channelId, $callId, $hangupPayload);
|
||||
$this->logBirdAction('BIRD_TEST_OUTBOUND_CALL_HANGUP_SENT', 'call=' . $callId . ' status=' . $status);
|
||||
$this->logBirdAction($logPrefix . '_HANGUP_SENT', 'call=' . $callId . ' status=' . $status);
|
||||
return [
|
||||
'to' => $targetNumber,
|
||||
'to_e164' => $targetNumber,
|
||||
@@ -455,7 +469,7 @@ class bird
|
||||
|
||||
$lastStatus = $this->extractStatus($lastCall);
|
||||
$this->logBirdAction(
|
||||
'BIRD_TEST_OUTBOUND_CALL_TIMEOUT',
|
||||
$logPrefix . '_TIMEOUT',
|
||||
'call=' . $callId . ' last_status=' . ($lastStatus ?? 'unknown'),
|
||||
0
|
||||
);
|
||||
@@ -598,17 +612,23 @@ class bird
|
||||
|
||||
public function callGateAndHangupWhenAccepted(int $countryCode, int $phone, int $timeout)
|
||||
{
|
||||
$this->logBirdAction('callGateAndHangupWhenAccepted', "Calling gate with country code $countryCode and phone $phone");
|
||||
$this->logBirdAction('callGateAndHangupWhenAccepted', "Timeout set to $timeout seconds");
|
||||
$voiceResponse = $this->createVoiceCall(
|
||||
$this->config->workplaceId->getVariableValue(),
|
||||
$this->config->channelId->getVariableValue(),
|
||||
// Call the gate with the provided phone number, and a short timeout to minimize potential disturbance.
|
||||
[
|
||||
'to' => '+' . $countryCode . $phone,
|
||||
'timeout' => max(5, min(60, (int)$timeout)),
|
||||
]
|
||||
);
|
||||
$ws = $this->config->workplaceId->getVariableValue();
|
||||
$ch = $this->config->channelId->getVariableValue();
|
||||
|
||||
$options = [
|
||||
'to' => '+' . $countryCode . $phone,
|
||||
'maxPollSeconds' => (int)$timeout,
|
||||
];
|
||||
|
||||
$result = $this->executeCallAndHangupWhenAccepted($ws, $ch, $options, 'BIRD_GATE_CALL');
|
||||
|
||||
if (!($result['hangup_sent'] ?? false)) {
|
||||
$msg = $result['message'] ?? 'Failed to call gate and hangup when accepted';
|
||||
if ($result['timed_out_waiting_for_accepted'] ?? false) {
|
||||
$msg = 'Timed out waiting for gate to accept call';
|
||||
}
|
||||
throw new Exception("Failed to call gate (+{$countryCode}{$phone}): " . $msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -205,13 +205,17 @@ class birdVoiceWebhooksRoute
|
||||
return false;
|
||||
}
|
||||
[$countryCode, $phone] = $normalized;
|
||||
// Use bird voice call to call the phone number, and then hang up after 5 seconds to trigger the gate
|
||||
|
||||
// Use the configured threshold or default to 10 seconds.
|
||||
$timeout = (int)($config['call_duration_threshold'] ?? 10);
|
||||
|
||||
// Use bird voice call to call the phone number, and then hang up after it is accepted to trigger the gate.
|
||||
$client = new bird();
|
||||
try {
|
||||
$client->callGateAndHangupWhenAccepted(
|
||||
$countryCode,
|
||||
$phone,
|
||||
5,
|
||||
(int)$countryCode,
|
||||
(int)$phone,
|
||||
$timeout,
|
||||
);
|
||||
return true;
|
||||
} catch (\Throwable $e) {
|
||||
|
||||
Reference in New Issue
Block a user