Skip to content

Commit 53ad66f

Browse files
committed
First commit - first version of Telephonify lib
1 parent 96e8993 commit 53ad66f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+4224
-0
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
composer.phar
2+
/vendor/

README.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
## PHP Telephonify
2+
3+
Telephonify is a PHP library which helps you to create telephone interactive menus (IVRs), automated attendants, call-centers and many more!
4+
5+
If you ever wanted to create one of those popular over-the-phone assistants, menus (and even a call-center!), then this library is for you.
6+
7+
This library can be used for the following providers of such services:
8+
* [Twilio](https://twilio.com)
9+
* [Nexmo](https://nexmo.com) (now known as Vonage)
10+
11+
### Installation
12+
13+
This library can be installed by using [composer](https://getcomposer.org/):
14+
15+
```
16+
composer install kattsoftware/php-telephonify
17+
```
18+
19+
### Getting started
20+
21+
You can get started using Telephonify [here](docs/getting_started_01.md).
22+
23+
### About
24+
25+
This library tries to unify more providers of such services under one, unique usage. While all the providers were thoroughly tested and checked for corner cases, as this library is just new, there could be very rare situations where functionality and/or features' specific behaviors work slightly different than expected. If you find any such cases, kindly please report them as issues on this repo. You are also more than welcome to submit your own PRs!
26+
27+
To do:
28+
* unit tests
29+
* a tool for testing locally the implementation (without making the actual calls)
30+
* recording feature (for human-to-human dialog parts of the call)
31+
* implement more events across all providers
32+
33+
### License
34+
35+
The library is licensed under the MIT License (MIT). See the `LICENSE` file for more information.
36+
37+
Copyright (c) KattSoftware dev team.

composer.json

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"name": "kattsoftware/php-telephonify",
3+
"description": "Create IVR/phone robots/automated telephone applications/call-centers easy",
4+
"type": "library",
5+
"license": "MIT",
6+
"autoload": {
7+
"psr-4": {
8+
"KattSoftware\\Telephonify\\": "src/"
9+
}
10+
},
11+
"require": {
12+
"php": ">=5.6"
13+
},
14+
"suggest": {
15+
"ext-json": "REQUIRED if you are using Nexmo",
16+
"ext-dom": "REQUIRED if you are using Twilio",
17+
"nexmo/client": "^1.9 || ^2.0 (REQUIRED if you plan to create outgoing or change ongoing calls - see docs)",
18+
"twilio/sdk": "^5.42 || ^6.1 (REQUIRED if you plan to create outgoing or change ongoing calls - see docs)"
19+
}
20+
}

docs/callmanager.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
### Using the `CallManager`
2+
3+
The `\KattSoftware\Telephonify\CallManager` allows you to modify an existing call (i.e. start executing another controller's code and stop the current execution - e.g. while playing an audio file in an infinite loop) and to create an outgoing call (i.e. one of your numbers that you bought from your provider to call an external number, such as one of your customers).
4+
5+
6+
7+
8+
9+
Next, you will learn about call events.

docs/core_concepts_03.md

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
### The core concepts of Telephonify
2+
3+
In the previous example, we created a simple Telephonify application and tested it to see that it works.
4+
5+
Let's see what concepts you've already came across:
6+
7+
#### The `IVRController` base class
8+
9+
Your Telephonify application code will live in classes which must always extend the `\KattSoftware\Telephonify\IVRController` base class. By doing so, you will have a method to implement, the `run()` method, which holds the partial or entire logic of the call flow. The `run()` method has 2 parameters:
10+
11+
* `\KattSoftware\Telephonify\Request $request` - contains information about the ongoing call. Read more about it [here](request.md).
12+
* `\KattSoftware\Telephonify\Response $response` - controls the actual call flow, by creating a stack of actions to be executed. Read more about it [here](response.md).
13+
14+
So, your call flow logic will work by calling the `Response`'s methods in order to sequentially perform certain actions (such as reading a text, playing an audio file, asking for input etc.) and also by using the information from the `Request` object about the current call (such as who called, the user's input etc).
15+
16+
It's important to understand that the methods you are calling on the `Response` instance are **not** synchronous. They stack on an internal array immediately as soon as they are called, and when the controller's code ends, they will be iterated by Telephonify to build a provider-specific response.
17+
18+
#### The `Application` instance
19+
20+
When you want to process any webhook incoming requests, you will have to use the public methods of an `Application` instance.
21+
* For the "Answer URL" requests, you will use `Application::processIncomingCall()` (for incoming calls) and `Application::processOutgoingCall()` (for outgoing calls). These methods will execute your controllers' code and return to the provider the response for controlling the call flow. These methods will set any required HTTP response headers, as well as the response output, so there is nothing left for you to do.
22+
* For the "Event URL" requests, you will use the `Application::processEventRequest()` method. This will not output anything, nor execute controllers' code, as it doesn't control th call flow, but just execute your callbacks, defined per event situation for a call (e.g. when the call ends, an event request will be issued to this URL).
23+
24+
So generally speaking, if you will use only incoming calls, you will need 2 endpoints: an "Answer URL" which handles the execution to `Application::processIncomingCall()` and an "Event URL", which uses `Application::processEventRequest()`. If your application will make outgoing calls as well, an additional endpoint will be required, where `Application::processOutgoingCall()` will be called.
25+
26+
Note: it's not something mandatory to process events! As long as your "Event URL" will respond with a blank HTTP response, with a response code of 200, then everything should be fine. In such cases, you don't even need to call `Application::processEventRequest()`, or anything from Telephonify.
27+
28+
When you create an `Application` instance, it requires 3 parameters:
29+
* a driver instance (a class implementing `\KattSoftware\Telephonify\Drivers\DriverInterface`)
30+
* a session storage instance (a class implementing `\KattSoftware\Telephonify\SessionStorage\SessionStorageInterface`)
31+
* (optional) a callable which should be used for creating the controller instances, instead of just applying `new` to them.
32+
33+
The first one is obvious. You already created one such driver, based on your provider of voice services.
34+
35+
Additionally, Telephonify needs some sort of session storage for each call, as a call can span over many HTTP requests made to your "Answer URL" endpoint. As such, this library will always need a way to match the current incoming HTTP request to an existing call state, so it will be able to follow to the subsequent controller, based on the call flow.
36+
37+
Such session storage implementations are implementing the `SessionStorageInterface` interface, and they have a few methods to declare. For convenience, this library comes already with one session storage implementation, and that is `LocalFiles`. The class needs on its constructor an absolute path, for storing locally the session files.
38+
39+
While the examples from this documentation use the `sys_get_temp_dir()` path, it may not be the best place to store those files in a production environment, as they contain sensitive data, such as participants' phone numbers and user's input. Also, the temp dir of a system can get emptied at any time.
40+
41+
The third parameter stands for a callable which Telephonify should use, instead of attempting to issue a `new` on any controller class name. This is useful if you are using dependency injection, or any other class management mechanism. As you already seen, this library works only by providing to its input fully qualified class names, and not instances of `IVRController`.
42+
43+
For example:
44+
45+
```php
46+
$driver = ...;
47+
$sessionStorage = ...;
48+
49+
$controllerFactory = function ($controllerClassName) {
50+
return MyClassFactory::make($controllerClassName);
51+
};
52+
53+
new Application($driver, $sessionStorage, $controllerFactory);
54+
```
55+
56+
Next, you will learn how to use the `CallManager` to modify existing calls or create outgoing calls.

docs/events.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
### Processing the events
2+
3+
TODO

docs/first_app_nexmo_02.md

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
### Creating your first IVR application with Telephonify and Nexmo
2+
3+
When you are accepting incoming calls (or create outgoing calls), the library will use the code you write inside a PHP class, called the _controller_. Based on the input (what number is calling, the input from his/her phone keyboard, etc.), known as the _request_, you will create a _response_, which is a collection of actions that will control the flow of the call. Examples of such actions are:
4+
* using a TTS (text-to-speech) voice you can speak texts to your user;
5+
* playing an MP3 file (waiting music, pre-recorded speech, etc.)
6+
* asking for user's input (asking the option from the menu for navigating to it, a numeric password, a phone number, etc.)
7+
* transferring the user to a phone number, speaking directly to a person;
8+
* and more!
9+
10+
Let's start by creating a class which extends `KattSoftware\Telephonify\IVRController`, naming it `SayHelloController`. This is essential for any Telephonify application, as the code/logic for your calls will always live in classes which extend the `IVRController` class, more exactly in the `run()` method:
11+
12+
```php
13+
<?php
14+
15+
use KattSoftware\Telephonify\IVRController;
16+
use KattSoftware\Telephonify\Request;
17+
use KattSoftware\Telephonify\Response;
18+
use KattSoftware\Telephonify\Drivers\Voices\NexmoVoice;
19+
20+
class SayHelloController extends IVRController
21+
{
22+
public function run(Request $request, Response $response)
23+
{
24+
$text = 'Hello! Welcome, ' . $request->getCallingNumber() . '!';
25+
$voice = NexmoVoice::EN_US_SALLI();
26+
27+
$response->say($text, $voice);
28+
}
29+
}
30+
31+
```
32+
33+
When somebody will call your number, they will hear the text `Hello! Welcome, <number>!`, where the `<number>` is the phone number in the E.164 format (that is - full phone number, including the country code, but without the `+` sign - e.g. `14155551234`). The text is read by a TTS (text-to-speech) voice, called `Salli`, a female voice which can read any text in the English (US) language. Nexmo offers many TTS voices for different languages, and they will be covered later.
34+
35+
### Creating the endpoint for incoming calls
36+
37+
OK, so let's test what you've just created.
38+
It is assumed that you already have a phone number bought from Nexmo, and a Nexmo Application created, with the "Voice" capability enabled, and that phone number linked to the Nexmo Application.
39+
40+
The way this library works for incoming calls is like this: you create a public endpoint URL, Nexmo will make a `POST` request to that URL and after that, Telephony will control the entire call flow by using one or more `IVRController` instances (in our case, we have only one controller (which is also the starting one), and that is `SayHelloController`).
41+
42+
The most basic implementation of a such endpoint is this:
43+
44+
```php
45+
<?php
46+
47+
use KattSoftware\Telephonify\Application;
48+
use KattSoftware\Telephonify\Drivers\Nexmo;
49+
use KattSoftware\Telephonify\EventsManager;
50+
use KattSoftware\Telephonify\SessionStorage\LocalFiles;
51+
use KattSoftware\Telephonify\Exceptions\TelephonifyException;
52+
// include here the SayHelloController.php file...
53+
54+
$driver = new Nexmo();
55+
$sessionStorage = new LocalFiles(sys_get_temp_dir());
56+
57+
$app = new Application(
58+
$driver,
59+
$sessionStorage
60+
);
61+
62+
$endpointUrl = 'http://myserver.com/telephonify-server.php';
63+
$eventUrl = 'http://myserver.com/telephonify-server.php?eventUrl=1';
64+
65+
try {
66+
// Is this an event request - i.e. containing info about the call?
67+
if (isset($_GET['eventUrl'])) {
68+
// (will be discussed later)
69+
$app->processEventRequest(new EventsManager(), $eventUrl);
70+
} else {
71+
$startingController = SayHelloController::class;
72+
73+
$result = $app->processIncomingCall($endpointUrl, $eventUrl, $startingController);
74+
}
75+
} catch (TelephonifyException $e) {
76+
// An error has occurred, do the logging, alerting etc.
77+
}
78+
```
79+
80+
Save that as `telephonify-server.php` and make it publicly available.
81+
82+
If you have a server online, making it public shouldn't be a problem, however, if you want to test it from your local machine, use a HTTP tunneling tool, such as [ngrok](https://ngrok.com/) or [Serveo](https://serveo.net/).
83+
84+
Let's assume that the file you just created is available from `http://myserver.com/telephonify-server.php`. Then, edit your Nexmo application and under "Voice" section, fill in the following fields:
85+
86+
* **Answer URL**: set it as _HTTP POST_ and the value to `http://myserver.com/telephonify-server.php`
87+
* **Event URL**: set it as _HTTP POST_ and the value to `http://myserver.com/telephonify-server.php?eventUrl=1`.
88+
89+
That's it! You have just created your first automatic phone system (known also as IVR) by using Telephonify!
90+
91+
Just call your Nexmo phone number and you will hear `Hello! Welcome, <your phone number>!`. Then the call will finish.
92+
93+
Now let's see the explanations of a few things that you encountered, but not explained yet, in the [next part](core_concepts_03.md).
94+
95+
Other pages:
96+
* [`Request` class reference](request.md)
97+
* [`Response` class reference](response.md)
98+
* [Reading text by using the TTS](tts.md)
99+
* [Security considerations](security.md)

docs/first_app_twilio_02.md

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
### Creating your first IVR application with Telephonify and Twilio
2+
3+
When you are accepting incoming calls (or create outgoing calls), the library will use the code you write inside a PHP class, called the _controller_. Based on the input (what number is calling, the input from his/her phone keyboard, etc.), known as the _request_, you will create a _response_, which is a collection of actions that will control the flow of the call. Examples of such actions are:
4+
* using a TTS (text-to-speech) voice you can speak texts to your user;
5+
* playing an MP3 file (waiting music, pre-recorded speech, etc.)
6+
* asking for user's input (asking the option from the menu for navigating to it, a numeric password, a phone number, etc.)
7+
* transferring the user to a phone number, speaking directly to a person;
8+
* and more!
9+
10+
Let's start by creating a class which extends `KattSoftware\Telephonify\IVRController`, naming it `SayHelloController`. This is essential for any Telephonify application, as the code/logic for your calls will always live in classes which extend the `IVRController` class, more exactly in the `run()` method:
11+
12+
```php
13+
<?php
14+
15+
use KattSoftware\Telephonify\IVRController;
16+
use KattSoftware\Telephonify\Request;
17+
use KattSoftware\Telephonify\Response;
18+
use KattSoftware\Telephonify\Drivers\Voices\TwilioVoice;
19+
20+
class SayHelloController extends IVRController
21+
{
22+
public function run(Request $request, Response $response)
23+
{
24+
$text = 'Hello! Welcome, ' . $request->getCallingNumber() . '!';
25+
$voice = TwilioVoice::EN_US_JOANNA();
26+
27+
$response->say($text, $voice);
28+
}
29+
}
30+
31+
```
32+
33+
When somebody will call your number, they will hear the text `Hello! Welcome, <number>!`, where the `<number>` is the phone number in the E.164 format (that is - full phone number, including the country code, but without the `+` sign - e.g. `14155551234`). The text is read by a TTS (text-to-speech) voice, called `Joanna`, a female voice which can read any text in the English (US) language. Twilio offers many TTS voices for different languages, and they will be covered later.
34+
35+
### Creating the endpoint for incoming calls
36+
37+
OK, so let's test what you've just created.
38+
It is assumed that you already have a phone number bought from Twilio.
39+
40+
The way this library works for incoming calls is like this: you create a public endpoint URL, Twilio will make a `POST` request to that URL and after that, Telephony will control the entire call flow by using one or more `IVRController` instances (in our case, we have only one controller (which is also the starting one), and that is `SayHelloController`).
41+
42+
The most basic implementation of a such endpoint is this:
43+
44+
```php
45+
<?php
46+
47+
use KattSoftware\Telephonify\Application;
48+
use KattSoftware\Telephonify\Drivers\Twilio;
49+
use KattSoftware\Telephonify\EventsManager;
50+
use KattSoftware\Telephonify\SessionStorage\LocalFiles;
51+
use KattSoftware\Telephonify\Exceptions\TelephonifyException;
52+
// include here the SayHelloController.php file...
53+
54+
$driver = new Twilio();
55+
$sessionStorage = new LocalFiles(sys_get_temp_dir());
56+
57+
$app = new Application(
58+
$driver,
59+
$sessionStorage
60+
);
61+
62+
$endpointUrl = 'http://myserver.com/telephonify-server.php';
63+
$eventUrl = 'http://myserver.com/telephonify-server.php?eventUrl=1';
64+
65+
try {
66+
// Is this an event request - i.e. containing info about the call?
67+
if (isset($_GET['eventUrl'])) {
68+
// (will be discussed later)
69+
$app->processEventRequest(new EventsManager(), $eventUrl);
70+
} else {
71+
$startingController = SayHelloController::class;
72+
73+
$result = $app->processIncomingCall($endpointUrl, $eventUrl, $startingController);
74+
}
75+
} catch (TelephonifyException $e) {
76+
// An error has occurred, do the logging, alerting etc.
77+
}
78+
```
79+
80+
Save that as `telephonify-server.php` and make it publicly available.
81+
82+
If you have a server online, making it public shouldn't be a problem, however, if you want to test it from your local machine, use a HTTP tunneling tool, such as [ngrok](https://ngrok.com/) or [Serveo](https://serveo.net/).
83+
84+
Let's assume that the file you just created is available from `http://myserver.com/telephonify-server.php`. Then, head over Programmable Voice > Numbers > Manage numbers > select your number from the listing, then fill in the following fields:
85+
86+
* **A call comes in**: set it as _HTTP POST_ and the value to `http://myserver.com/telephonify-server.php`
87+
* **Call status changes**: set it as _HTTP POST_ and the value to `http://myserver.com/telephonify-server.php?eventUrl=1`.
88+
89+
That's it! You have just created your first automatic phone system (known also as IVR) by using Telephonify!
90+
91+
Just call your Twilio phone number and you will hear `Hello! Welcome, <your phone number>!`. Then the call will finish.
92+
93+
Now let's see the explanations of a few things that you encountered, but not explained yet, in the [next part](core_concepts_03.md).
94+
95+
Other pages:
96+
* [`Request` class reference](request.md)
97+
* [`Response` class reference](response.md)
98+
* [Reading text by using the TTS](tts.md)
99+
* [Security considerations](security.md)

docs/getting_started_01.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Getting started with PHP Telephonify
2+
3+
During this tutorial, you will be able to see and understand how the Telephonify library works.
4+
It's assumed that you already installed the library via the Composer package manager and already included its autoloader.
5+
6+
This library works with the following providers:
7+
* [Twilio](https://twilio.com)
8+
* [Nexmo](https://nexmo.com) (now known as Vonage)
9+
10+
For the one you will use, ensure that you already have an account registered with them.
11+
12+
There are 2 types of calls which you can operate through this library:
13+
* **incoming calls**: you bought a phone number from your provider and now you want to programmatically accept incoming calls to that number, from outside - e.g. from your customers. You can play waiting music, read some information, accept user's input and much more.
14+
* **outgoing calls**: they are the same as the incoming calls, but are started by your application, upon calling a method from this library, and the recipient must answer the call in order launch the flow of actions.
15+
16+
To continue with the tutorial, please choose which provider you are going to use:
17+
18+
* [Twilio](first_app_twilio_02.md)
19+
* [Nexmo](first_app_nexmo_02.md)

0 commit comments

Comments
 (0)