Skip to content

Commit c9b8ada

Browse files
authored
Feature/voice machine detection (#404)
* Outbound call PHP8 cleanup * Advanced machine detection in outbound call * Add advanced machine detection to NCCO
1 parent 8dc0f5b commit c9b8ada

File tree

8 files changed

+258
-83
lines changed

8 files changed

+258
-83
lines changed

phpunit.xml.dist

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@
2323
<testsuite name="verify2">
2424
<directory>test/Verify2</directory>
2525
</testsuite>
26+
<testsuite name="voice">
27+
<directory>test/Voice</directory>
28+
</testsuite>
2629
<testsuite name="messages">
2730
<directory>test/Messages</directory>
2831
</testsuite>

src/Voice/Client.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,10 @@ public function createOutboundCall(OutboundCall $call): Event
8484
$json['ringing_timer'] = (string)$call->getRingingTimer();
8585
}
8686

87+
if (!is_null($call->getAdvancedMachineDetection())) {
88+
$json['advanced_machine_detection'] = $call->getAdvancedMachineDetection()->toArray();
89+
}
90+
8791
$event = $this->api->create($json);
8892
$event['to'] = $call->getTo()->getId();
8993
if ($call->getFrom()) {

src/Voice/NCCO/Action/Connect.php

Lines changed: 25 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
use InvalidArgumentException;
1515
use Vonage\Voice\Endpoint\EndpointInterface;
16+
use Vonage\Voice\VoiceObjects\AdvancedMachineDetection;
1617
use Vonage\Voice\Webhook;
1718

1819
class Connect implements ActionInterface
@@ -21,40 +22,15 @@ class Connect implements ActionInterface
2122
public const MACHINE_CONTINUE = 'continue';
2223
public const MACHINE_HANGUP = 'hangup';
2324

24-
/**
25-
* @var ?string
26-
*/
27-
protected $from;
28-
29-
/**
30-
* @var ?string
31-
*/
32-
protected $eventType;
25+
protected ?string $from = '';
26+
protected ?string $eventType = '';
3327

34-
/**
35-
* @var int
36-
*/
37-
protected $timeout;
38-
39-
/**
40-
* @var int
41-
*/
42-
protected $limit;
43-
44-
/**
45-
* @var string
46-
*/
47-
protected $machineDetection;
48-
49-
/**
50-
* @var ?Webhook
51-
*/
52-
protected $eventWebhook;
53-
54-
/**
55-
* @var ?string
56-
*/
57-
protected $ringbackTone;
28+
protected int $timeout = 0;
29+
protected int $limit = 0;
30+
protected $machineDetection = '';
31+
protected ?Webhook $eventWebhook = null;
32+
protected ?string $ringbackTone = '';
33+
protected ?AdvancedMachineDetection $advancedMachineDetection = null;
5834

5935
public function __construct(protected EndpointInterface $endpoint)
6036
{
@@ -93,6 +69,10 @@ public function toNCCOArray(): array
9369
$data['machineDetection'] = $this->getMachineDetection();
9470
}
9571

72+
if ($this->getAdvancedMachineDetection()) {
73+
$data['advancedMachineDetection'] = $this->getAdvancedMachineDetection()->toArray();
74+
}
75+
9676
$from = $this->getFrom();
9777

9878
if ($from) {
@@ -236,4 +216,16 @@ public function setRingbackTone(string $ringbackTone): self
236216

237217
return $this;
238218
}
219+
220+
public function getAdvancedMachineDetection(): ?AdvancedMachineDetection
221+
{
222+
return $this->advancedMachineDetection;
223+
}
224+
225+
public function setAdvancedMachineDetection(AdvancedMachineDetection $advancedMachineDetection): static
226+
{
227+
$this->advancedMachineDetection = $advancedMachineDetection;
228+
229+
return $this;
230+
}
239231
}

src/Voice/OutboundCall.php

Lines changed: 25 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -15,61 +15,46 @@
1515
use Vonage\Voice\Endpoint\EndpointInterface;
1616
use Vonage\Voice\Endpoint\Phone;
1717
use Vonage\Voice\NCCO\NCCO;
18+
use Vonage\Voice\VoiceObjects\AdvancedMachineDetection;
1819

1920
class OutboundCall
2021
{
2122
public const MACHINE_CONTINUE = 'continue';
2223
public const MACHINE_HANGUP = 'hangup';
23-
24-
/**
25-
* @var Webhook
26-
*/
27-
protected $answerWebhook;
28-
29-
/**
30-
* @var Webhook
31-
*/
32-
protected $eventWebhook;
24+
protected ?Webhook $answerWebhook = null;
25+
protected ?Webhook $eventWebhook = null;
3326

3427
/**
3528
* Length of seconds before Vonage hangs up after going into `in_progress` status
36-
*
37-
* @var int
3829
*/
39-
protected $lengthTimer;
30+
protected int $lengthTimer = 7200;
4031

4132
/**
4233
* What to do when Vonage detects an answering machine.
43-
*
44-
* @var ?string
4534
*/
46-
protected $machineDetection;
35+
protected ?string $machineDetection = '';
36+
4737
/**
48-
* @var NCCO
38+
* Overrides machine detection if used for more configuration options
4939
*/
50-
protected $ncco;
40+
protected ?AdvancedMachineDetection $advancedMachineDetection = null;
41+
42+
protected ?NCCO $ncco = null;
5143

5244
/**
53-
* Whether or not to use random numbers linked on the application
54-
*
55-
* @var bool
45+
* Whether to use random numbers linked on the application
5646
*/
57-
protected $randomFrom = false;
47+
protected bool $randomFrom = false;
5848

5949
/**
6050
* Length of time Vonage will allow a phone number to ring before hanging up
61-
*
62-
* @var int
6351
*/
64-
protected $ringingTimer;
52+
protected int $ringingTimer = 60;
6553

6654
/**
6755
* Creates a new Outbound Call object
6856
* If no `$from` parameter is passed, the system will use a random number
6957
* that is linked to the application instead.
70-
*
71-
*
72-
* @return void
7358
*/
7459
public function __construct(protected EndpointInterface $to, protected ?Phone $from = null)
7560
{
@@ -118,39 +103,27 @@ public function getTo(): EndpointInterface
118103
return $this->to;
119104
}
120105

121-
/**
122-
* @return $this
123-
*/
124106
public function setAnswerWebhook(Webhook $webhook): self
125107
{
126108
$this->answerWebhook = $webhook;
127109

128110
return $this;
129111
}
130112

131-
/**
132-
* @return $this
133-
*/
134113
public function setEventWebhook(Webhook $webhook): self
135114
{
136115
$this->eventWebhook = $webhook;
137116

138117
return $this;
139118
}
140119

141-
/**
142-
* @return $this
143-
*/
144120
public function setLengthTimer(int $timer): self
145121
{
146122
$this->lengthTimer = $timer;
147123

148124
return $this;
149125
}
150126

151-
/**
152-
* @return $this
153-
*/
154127
public function setMachineDetection(string $action): self
155128
{
156129
if ($action === self::MACHINE_CONTINUE || $action === self::MACHINE_HANGUP) {
@@ -162,19 +135,13 @@ public function setMachineDetection(string $action): self
162135
throw new InvalidArgumentException('Unknown machine detection action');
163136
}
164137

165-
/**
166-
* @return $this
167-
*/
168138
public function setNCCO(NCCO $ncco): self
169139
{
170140
$this->ncco = $ncco;
171141

172142
return $this;
173143
}
174144

175-
/**
176-
* @return $this
177-
*/
178145
public function setRingingTimer(int $timer): self
179146
{
180147
$this->ringingTimer = $timer;
@@ -186,4 +153,16 @@ public function getRandomFrom(): bool
186153
{
187154
return $this->randomFrom;
188155
}
156+
157+
public function getAdvancedMachineDetection(): ?AdvancedMachineDetection
158+
{
159+
return $this->advancedMachineDetection;
160+
}
161+
162+
public function setAdvancedMachineDetection(?AdvancedMachineDetection $advancedMachineDetection): static
163+
{
164+
$this->advancedMachineDetection = $advancedMachineDetection;
165+
166+
return $this;
167+
}
189168
}
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
<?php
2+
3+
namespace Vonage\Voice\VoiceObjects;
4+
5+
use Vonage\Entity\Hydrator\ArrayHydrateInterface;
6+
7+
class AdvancedMachineDetection implements ArrayHydrateInterface
8+
{
9+
public const MACHINE_BEHAVIOUR_CONTINUE = 'continue';
10+
public const MACHINE_BEHAVIOUR_HANGUP = 'hangup';
11+
public const MACHINE_MODE_DETECT = 'detect';
12+
public const MACHINE_MODE_DETECT_BEEP = 'detect_beep';
13+
public const BEEP_TIMEOUT_MIN = 45;
14+
public const BEEP_TIMEOUT_MAX = 120;
15+
protected array $permittedBehaviour = [self::MACHINE_BEHAVIOUR_CONTINUE, self::MACHINE_BEHAVIOUR_HANGUP];
16+
protected array $permittedModes = [self::MACHINE_MODE_DETECT, self::MACHINE_MODE_DETECT_BEEP];
17+
18+
public function __construct(
19+
protected string $behaviour,
20+
protected int $beepTimeout,
21+
protected string $mode = 'detect'
22+
) {
23+
if (!$this->isValidBehaviour($behaviour)) {
24+
throw new \InvalidArgumentException($behaviour . ' is not a valid behavior string');
25+
}
26+
27+
if (!$this->isValidMode($mode)) {
28+
throw new \InvalidArgumentException($mode . ' is not a valid mode string');
29+
}
30+
31+
if (!$this->isValidTimeout($beepTimeout)) {
32+
throw new \OutOfBoundsException('Timeout ' . $beepTimeout . ' is not valid');
33+
}
34+
}
35+
36+
protected function isValidBehaviour(string $behaviour): bool
37+
{
38+
if (in_array($behaviour, $this->permittedBehaviour, true)) {
39+
return true;
40+
}
41+
42+
return false;
43+
}
44+
45+
protected function isValidMode(string $mode): bool
46+
{
47+
if (in_array($mode, $this->permittedModes, true)) {
48+
return true;
49+
}
50+
51+
return false;
52+
}
53+
54+
protected function isValidTimeout(int $beepTimeout): bool
55+
{
56+
$range = [
57+
'options' => [
58+
'min_range' => self::BEEP_TIMEOUT_MIN,
59+
'max_range' => self::BEEP_TIMEOUT_MAX
60+
]
61+
];
62+
63+
if (filter_var($beepTimeout, FILTER_VALIDATE_INT, $range)) {
64+
return true;
65+
}
66+
67+
return false;
68+
}
69+
70+
public function fromArray(array $data): static
71+
{
72+
$this->isArrayValid($data);
73+
74+
$this->behaviour = $data['behaviour'];
75+
$this->mode = $data['mode'];
76+
$this->beepTimeout = $data['beep_timeout'];
77+
78+
return $this;
79+
}
80+
81+
public function toArray(): array
82+
{
83+
return [
84+
'behaviour' => $this->behaviour,
85+
'mode' => $this->mode,
86+
'beep_timeout' => $this->beepTimeout
87+
];
88+
}
89+
90+
protected function isArrayValid(array $data): bool
91+
{
92+
if (
93+
!array_key_exists('behaviour', $data)
94+
|| !array_key_exists('mode', $data)
95+
|| !array_key_exists('beep_timeout', $data)
96+
) {
97+
return false;
98+
}
99+
100+
return $this->isValidBehaviour($data['behaviour'])
101+
|| $this->isValidMode($data['mode'])
102+
|| $this->isValidTimeout($data['beep_timeout']);
103+
}
104+
}

0 commit comments

Comments
 (0)