SlideShare una empresa de Scribd logo
1 de 69
Agile Telephony Applications
Who the f...?
● Marcelo Gornstein
● Author of some open source projects, in Erlang, C,
PHP, Js (NodeJS), Ruby.
● A bit more of 15 years working in the industry
(sysadmin, developer, lead developer, architect, product
owner). A few more years, as a hobbyist.
● Code monkey: Currently an Erlang developer at Inaka (
http://www.inaka.net/).
● Variety of projects and scenarios (sysadmin, parking
systems, virtual hosting, telephony, etc).
● Last years: VoIP
● https://github.com/marcelog
● http://marcelog.github.io/
Agenda
● Introduction to some telephony concepts
● IVR
● Call Flow
● PBX
● Switch
● What is Asterisk, how does it fit
● Brief Introduction
● Dialplan
● Applications
● Interacting with Asterisk: Interfaces
● AMI Protocol
● AGI Protocol
● PAMI
● Basic use
● Actions / Responses
● Events / Filtering
● PAGI
● Basic use
● The PAGIApplication
● Nodes / NodeController
● Unit Testing
Asterisk
What can we know upfront
● Telephony related
● IVR? PBX? Switch?
● VoIP
Interactive Voice Response
“Interactive voice response (IVR) is a technology
that allows a computer to interact with humans
through the use of voice and DTMF tones input via
keypad.”
Wikipedia: http://en.wikipedia.org/wiki/Interactive_voice_response
http://www.ivrsworld.com/advanced-ivrs/using-ivrs-in-complaint-managment-system/
PBX
“A private branch exchange (PBX) is a telephone
exchange that serves a particular business or office,
as opposed to one that a common carrier or
telephone company operates for many businesses or
for the general public”
Wikipedia: http://en.wikipedia.org/wiki/Private_branch_exchange
Telephone Exchange
“A telephone exchange is a telecommunications
system used in the public switched telephone network
or in large enterprises. An exchange consists of
electronic components and in older systems also
human operators that interconnect (switch) telephone
subscriber lines or virtual circuits of digital systems
to establish telephone calls between subscribers.”
Wikipedia: http://en.wikipedia.org/wiki/Telephone_exchange
Without switches: not that fun
First switch
Hardware: humans
Mechanical switches
Hardware: relays, motors, strowger switch
Digital Switches
Hardware: modern electronic components, software
starts to make its appearance. First IVRs..
SoftSwitches
Software pretty much does everything. Any regular
computer can be a switch.
SoftSwitches
Yes, even Open Source :)
SoftSwitches
Yes, even Open Source :)
This is great for us!
Internet / PSTN
VoiceMail
This is great for us!
Internet / PSTN
VoiceMail Time of day
This is great for us!
Internet / PSTN
VoiceMail Customer SupportTime of day
This is great for us!
Internet / PSTN
VoiceMail Customer Support
PrePaid Calling
Cards
Time of day
This is great for us!
Internet / PSTN
VoiceMail Customer Support
PrePaid Calling
Cards
Telephone
Campaigns
Time of day
This is great for us!
Internet / PSTN
Asterisk: What we know now
● It's software. Can be run in a variety of unix-like operating systems
(http://www.asterisk.org/downloads)
● More a PBX than anything else
● Poor man's switch (scale might be difficult, some API inconsistencies,
not really designed as a softswitch)
● Easy to use (although your mileage can vary)
● Supports SIP, R2, SS7
● Huge community
● Thousands of propietary and open source applications
● Useful interfaces to interact with it and to create IVRs
Let's get a bit more serious
What's important for us, devs
● Dialplan (extensions.conf)
– Routing (Context / Priority / Extension)
● Patterns: _X.
– Roadmap for the call
● Applications / Functions
– Answer / Busy / Hangup / Ringing
– SetCallerID
– AGI
– Playback / Playtones / SayDigits
● Interfaces (AGI / AMI)
Dialplan
[default]
113, 1, Answer
113, 2, Goto(say_hi,${EXTEN},1)
[say_hi]
_X.,1,Playback(welcome)
_X.,n,Read(NUMBER,,4)
_X.,n,SayNumber(${NUMBER})
_X.,n,AGI(/usr/ivr/run.php)
_X.,n,Hangup
(can you imagine how a complex application would look like?)
Interfaces
● Asterisk Manager Interface (AMI)
– TCP/TLS
– Can listen for events in the whole box
– Can execute commands (actions)
– Privileges are configurable per user
● Asterisk Gateway Interface (AGI)
– stdin/stdout
– Serves a specific call
– Can execute some commands, only for the current call.
AMI and AGI: The protocols
AMI
Message Description Example
Events Sent in an async way by
Asterisk without notice
(sometimes as part of a
response)
Event: FullyBooted
Privilege: system,all
Status: Fully Booted
Variable: foo=bar
Actions
Sent by us, to execute
commands Action: Ping
ActionId: 1378159694
Responses Sent by asterisk as a reply
to an executed action
Response: Success
ActionID: 1378159694
Ping: Pong
Timestamp: 1378159694
● Text Protocol, TCP as transport
● Lines end with rn, Messages by rn
● https://wiki.asterisk.org/wiki/display/AST/AMI+Actions
● https://wiki.asterisk.org/wiki/display/AST/Asterisk+11+AMI+Events
AGI
● Text Protocol, uses stdin/stdout (although FastAGI allows tcp)
● Reply example:
error_code<space>result=<...>[<space>additional_data]rn
● ANSWER
● 200 result=0
● GET DATA /tmp/file.wav 5000 5
● 200 result=2 (timeout)
● https://wiki.asterisk.org/wiki/display/AST/AGI+Commands
Easy Protocols
● fopen / fread / fwrite / fclose
● Any language (even shell scripts!)
● Lack of real use without a proper abstraction
PAMI and PAGI
● There are other frameworks available: phpagi, shift8.
● Features from the real world, for devs and products.
● Unit tested: coverage ~100% (most of the time..)
● Object Oriented: Easy to use and extend
● Use log4php (http://logging.apache.org/log4php/)
● PAMI (Php-AMI): AMI Client
– Monitoring, Call Tracing (CDR, Billing)
– Can send/receive calls, SMS, etc
– Queue/Park calls
– Operator consoles, realtime events, CRMs
– Can provide IVR's via AsyncAGI + PAGI
● PAGI (Php-AGI): AGI Client
– IVR's (Surveys, Customer support, VoiceMail, Fax, etc)
Lot of code ahead of us now!
PAMI: Basic use
$options = array(
'log4php.properties' => 'log4php.properties',
'host' => 'mypbx.mycompany.com',
'scheme' => 'tcp://', // Or tls://
'port' => 5038,
'username' => 'mrwolf',
'secret' => 'badass'
);
// Create the client
use PAMIClientImplClientImpl as PamiClient;
$client = new PamiClient($options);
$client->open();
// Needed to process and deliver events and responses
while($running) {
$client->process();
usleep(1000);
}
$client->close();
Executing Actions
use PAMIMessageActionReloadAction;
$response = $pamiClient->send(new ReloadAction());
if ($response->isSuccess()) {
...
} else {
...
}
● Execution is synchronous, send() will only return when the response is
ready and complete (including any related events)
● $response->isSuccess() to know if eveything went well
● $response->getEvents() returns associated events
● $response->getKeys() All response keys
● $response->getVariable()/getVariables() To get response variables
Listening for events
● Closures
● Pre existant objects
$client->registerEventListener(
function (EventMessage $event) {
...
}
);
$client->registerEventListener(
array($object, 'method')
);
● Execution is asynchronous
● $response->getKeys() All response keys
● $response->getVariable()/getVariables() To get response variables
● $event->getName() Event name (Bridge, Unlink, Relad, Dial, etc)
Filtering events with predicates
use PAMIMessageEventDialEvent;
// The 2nd
argument allows the addition of predicates,
// that will return true (let the event pass)
// or false (drop it)
$client->registerEventListener(
function (EventMessage $event) {
...
},
function (EventMessage $event) {
return $event instanceof DialEvent
&& $event->getSubEvent() == 'Begin';
}
);
Example: Originating a call
$call = new OriginateAction('sip/marcelog');
$call->setApplication(“AGI”);
$call->setData(“myagi.php”);
$call->setAsync(true);
$response = $this->pami->send($call);
while($running) {
$client->process();
usleep(1000);
}
Example: Originating a call
$client->registerEventListener(
function (OriginateResponseEvent $event) {
$reason = $event->getReason();
switch ($reason) {
...
}
},
function (EventMessage $event) {
return instanceof OriginateResponseEvent;
}
);
PAMI
● Read more
– http://marcelog.github.io/PAMI
– http://marcelog.github.io/articles/articles.html
● How to install
– http://marcelog.github.io/PAMI/install.html
– https://github.com/marcelog/PAMI
– https://packagist.org/packages/marcelog/pami
– http://pear.marcelog.name/
– http://ci.marcelog.name/job/PAMI
IVRs!
PAGI Client: Basic use
// Get a client
$options = array(
'log4php.properties' => 'log4php.properties'
);
use PAGIClientImplClientImpl as PagiClient;
$client = PagiClient::getInstance($options);
// Handling the call
$client->answer();
$client->hangup();
// Logging through the asterisk logger (logger.conf)
$asteriskLogger = $client->getAsteriskLogger();
$asteriskLogger->notice("A NOTICE priority message");
$asteriskLogger->error("An ERROR priority message");
$asteriskLogger->warn("A WARNING priority message");
// Originating a call
$result = $client->dial(
"SIP/marcelog", array(60, 'rh')
);
if ($result->isAnswer()) {
$asteriskLogger->debug(
$result->getAnsweredTime()
);
}
PAGI Client: Basic use
// Play a file and get input
// timeout in 3s, up to 8 digits
$result = $client->getData($aSoundFile, 3000, 8);
if ($result->isTimeout()) {
// No input
} else {
// Get what the user pressed...
$digits = $result->getDigits();
}
// Accessing the Caller ID info
$clid = $client->getCallerId();
$clid->setNumber('123123');
// Music On Hold
$client->setMusic(true, 'myMOHClass');
PAGI Client: Basic use
// Playing different tones, sending indications
$client->playDialTone();
$client->playBusyTone($seconds);
// Sending indications
$client->indicateProgress();
$client->indicateBusy();
PAGI Client: Basic use
// Changing CDR records
$cdr = $pagiClient->getCDR();
$cdr->setUserfield("my own content here");
$cdr->setAccountCode("blah");
$cdr->setCustom("myOwnField", "withMyOwnValue");
$asteriskLogger->debug($cdr->getAnswerLength());
More
● Get / Set channel variables
● Add / Remove SIP headers
● Send / Receive FAXes
● Automatic Machine Detection
● Record audio
● Say a datetime, digits, complete numbers
● Spool calls
● Execute custom commands (exec)
● And some more :)
Optional: The PAGIApplication
class MyPagiApplication extends PAGIApplication
{
public function run() { }
public function init() { }
public function signalHandler($signo) { }
public function errorHandler($type, $message, $file, $line){}
public function shutdown() {}
}
$pagiAppOptions = array('pagiClient' => $pagiClient);
$pagiApp = new MyPagiApplication($pagiAppOptions);
$pagiApp->init();
$pagiApp->run();
Lots of boiler plate code included: signal handling, error handler, includes a logger, etc
Unit testing (Client)
$client->answer();
$number = $client->getCallerId()->getNumber();
if ($number == 'anonymous') {
$client->streamFile("i-cant-find-your-number");
} else {
$client->streamFile("you-are-calling-from");
$client->sayDigits($number);
}
$client->streamFile("bye")
Unit testing: General skeleton
/**
* @test
*/
public function can_read_caller_id()
{
$mock = new MockedClientImpl($options);
// In the next slides we'll see what to
// do with the mock...
$app = new App(array('pagiClient' => $mock));
$app->init();
$app->run();
$app->shutdown();
}
$mock
// We want to be sure these are called with
// these arguments...
->assert('answer')
->assert('getFullVariable', array('CALLERID(num)'))
->assert('streamFile', array('you-are-calling-from'))
->assert('sayDigits', array('5555555'))
->assert('streamFile', array('bye'))
->assert('hangup')
Unit testing: Assert behavior
// … and on calls to these functions,
// return the given values
->onAnswer(true)
->onGetFullVariable(true, '5555555')
->onStreamFile(false, '#')
->onSayDigits(true, '#')
->onStreamFile(false, '#')
->onHangup(true)
;
Unit testing: Mock asterisk responses
Nodes
● Client Wrapper
● Declarative programming
● Fluent interface
● DI compatible
● Testable
● Can keep state
● Prompt, input, validations
● Real life features: (un)interruptable prompts, messages for different situations
(invalid input, last attempt, etc), accept/discard input on pre prompt messages, etc
● Callbacks: executeOnValidInput(), executeOnInputFailed(), executeBeforeRun(),
executeAfterRun(), executeAfterFailedValidation()
● Execution result
– COMPLETE
– CANCEL
– TIMEOUT
– MAX_INPUT_REACHED
A Simple IVR Application
$pagiClient->streamFile('welcome');
$node = $pagiClient
->createNode('get-number')
->saySound('enter-number')
Playing Welcome and Prompt
->maxAttemptsForInput(3)
->playOnMaxValidInputAttempts(
'too-many-attempts'
)->expectAtLeast(10)
->expectAtMost(12)
->maxTotalTimeForInput(3000)
->playOnNoInput('no-input')
->cancelWith(Node::DTMF_STAR)
->endInputWith(Node::DTMF_HASH)
Some mundane details...
  ->validateInputWith(  
      'numberIsValid',  
      function (Node $node) use($db) {
        $input = $node->getInput();
        $db->saveNumber($input);
        return $db->numberIsValid($input); 
      },  
      'number-is-not-valid'  
  )
Adding validators
  ->executeOnValidInput(function (Node $node) {
    $pagiClient = $node->getClient();
    $pagiClient->streamFile('thank-you');
  })
On valid input...
  ->executeOnInputFailed(function (Node $node) {  
    if($node->getTotalInputAttemptsUsed() <= 2) {
      $node->addPrePromptMessage("please-try-again"); 
    }  
  })
On failed validation...
When done, hangup!
  ->executeAfterRun(function (Node $node) {
    $pagiClient->hangup(); 
  })
  ->run();
That's it :)
$pagiClient->streamFile('welcome');
$pagiClient
  ->createNode('menu')
  ->saySound('enter-number')
  ->maxAttemptsForInput(3)
  ->playOnMaxValidInputAttempts(
    'too-many-attempts'
  )->expectAtLeast(10)  
  ->expectAtMost(12)  
  ->maxTotalTimeForInput(3000)  
  ->playOnNoInput('no-input')
  ->cancelWith(Node::DTMF_STAR)  
  ->endInputWith(Node::DTMF_HASH)  
  ->validateInputWith(  
      'numberIsValid',  
      function (Node $node) use($db) {
        $input = $node->getInput();
        $db->saveNumber($input);
        return $db->numberIsValid($input); 
      },  
      'number-is-not-valid'  
  )->executeOnValidInput(function (Node $node) {
    $pagiClient = $node->getClient();
    $pagiClient->streamFile('thank-you');
  })->executeOnInputFailed(function (Node $node) {  
    if($node->getTotalInputAttemptsUsed() <= 2) {
      $node->addPrePromptMessage("please-try-again"); 
    }  
  })->executeAfterRun(function (Node $node) {
    $pagiClient->hangup(); 
  })->run();
Checking the result
if($node->maxInputsReached()) {  
  $node->getClient()->hangup();  
} else if($node->isComplete()) {  
  $input = $node->getInput();
  $otherNode->run();
} else if($node->wasCancelled()) {
  ...
}
Nice, but too many nodes = too many ifs
The NodeController
      // 1. Create the node controller
    $controller = $client
      ->createNodeController('customerSupport');
    // 2. Create a few nodes
    $controller->register('salutation')
      ->saySound('salutation');
    $controller->register('menu')
      ->saySound('main-menu')
      ->maxAttemptsForInput(3)
      ->expectExactly(1)
      ->validateInputWith(...);
    $controller->register('maxAttempts')
      ->saySound('too-many-attempts');
    $controller->register('operator')
      ->dial('SIP/operator', array(60, 'rh');
General Flow Behavior
    // 3. Specify behavior
    $controller->registerResult('salutation')
      ->onComplete()->jumpTo('menu');
    
    $controller->registerResult('menu')
      ->onMaxAttemptsReached()->jumpTo('maxAttempts');
    $controller->registerResult('maxAttempts')
      ->onComplete()->hangup(16);
   $controller->registerResult('menu')
     ->onComplete()->withInput('1')->jumpTo('sales');
   
   $controller->registerResult('menu')
     ->onComplete()->withInput('2')->jumpTo('support');
   $controller->registerResult('menu')->onComplete()->
       jumpAfterEval(function (Node $node) { 
           return 'operator';  
       });
Menu Behavior
    // 4. Run!
    $controller->jumpTo('salutation');
The NodeController
Unit testing (Node apps)
// For nodes, we can specify the input, and
// the rest is just like testing “client-only” apps.
$mockedMenu = $mockedPagiClient
  ->onCreateNode('mainMenu')
$mockedMenu->runWithInput('123')
    ->assertSaySound('some-audio', 1)
    ->assertSayDigits(123, 1)
    ->assertSayNumber(321, 1)
    ->assertSayDateTime(1, 'dmY', 1)
…
$pagiApp->init();
$pagiApp->run();
PAGI
● Read more
– http://marcelog.github.io/PAGI
– http://marcelog.github.io/articles/articles.html
● How to install
– http://marcelog.github.io/PAGI/install.html
– https://github.com/marcelog/PAGI
– https://packagist.org/packages/marcelog/pagi
– http://pear.marcelog.name/
– http://ci.marcelog.name/job/PAGI
Thank you :)
● @all, for coming today
● All the organizers
● Mariano Iglesias @mgiglesias
● Mi so-cool-and-beautiful wife for her support
Dvorak and Qwerty, of course
Questions?
● marcelog@gmail.com
● https://github.com/marcelog
● http://marcelog.github.io/

Más contenido relacionado

La actualidad más candente

Going realtime with Socket.IO
Going realtime with Socket.IOGoing realtime with Socket.IO
Going realtime with Socket.IOChristian Joudrey
 
Three Ways Kamailio Can Help Your FreeSWITCH Deployment
Three Ways Kamailio Can Help Your FreeSWITCH DeploymentThree Ways Kamailio Can Help Your FreeSWITCH Deployment
Three Ways Kamailio Can Help Your FreeSWITCH DeploymentFred Posner
 
SIPREC RTPEngine Media Forking
SIPREC RTPEngine Media ForkingSIPREC RTPEngine Media Forking
SIPREC RTPEngine Media ForkingHossein Yavari
 
Software Defined Datacenter with Proxmox
Software Defined Datacenter with ProxmoxSoftware Defined Datacenter with Proxmox
Software Defined Datacenter with ProxmoxGLC Networks
 
Getting a live_transcript_of_your_call_using_the_ari
Getting a live_transcript_of_your_call_using_the_ariGetting a live_transcript_of_your_call_using_the_ari
Getting a live_transcript_of_your_call_using_the_ariPascal Cadotte-Michaud
 
Asterisk Rest Interface - ARI
Asterisk Rest Interface - ARIAsterisk Rest Interface - ARI
Asterisk Rest Interface - ARIDavid Muñoz
 
FreeSWITCH Cluster by K8s
FreeSWITCH Cluster by K8sFreeSWITCH Cluster by K8s
FreeSWITCH Cluster by K8sChien Cheng Wu
 
Kamailio :: A Quick Introduction
Kamailio :: A Quick IntroductionKamailio :: A Quick Introduction
Kamailio :: A Quick IntroductionOlle E Johansson
 
FreeSWITCH as a Microservice
FreeSWITCH as a MicroserviceFreeSWITCH as a Microservice
FreeSWITCH as a MicroserviceEvan McGee
 
Why is Kamailio so different? An introduction.
Why is Kamailio so different? An introduction.Why is Kamailio so different? An introduction.
Why is Kamailio so different? An introduction.Olle E Johansson
 
ZeroMQ Is The Answer
ZeroMQ Is The AnswerZeroMQ Is The Answer
ZeroMQ Is The AnswerIan Barber
 
Real Time Communication using Node.js and Socket.io
Real Time Communication using Node.js and Socket.ioReal Time Communication using Node.js and Socket.io
Real Time Communication using Node.js and Socket.ioMindfire Solutions
 
Asterisk WebRTC frontier: make client SIP Phone with sipML5 and Janus Gateway
Asterisk WebRTC frontier: make client SIP Phone with sipML5 and Janus GatewayAsterisk WebRTC frontier: make client SIP Phone with sipML5 and Janus Gateway
Asterisk WebRTC frontier: make client SIP Phone with sipML5 and Janus GatewayAlessandro Polidori
 
FreeSWITCH on Docker
FreeSWITCH on DockerFreeSWITCH on Docker
FreeSWITCH on Docker建澄 吳
 

La actualidad más candente (20)

Kamailio - Secure Communication
Kamailio - Secure CommunicationKamailio - Secure Communication
Kamailio - Secure Communication
 
Going realtime with Socket.IO
Going realtime with Socket.IOGoing realtime with Socket.IO
Going realtime with Socket.IO
 
Three Ways Kamailio Can Help Your FreeSWITCH Deployment
Three Ways Kamailio Can Help Your FreeSWITCH DeploymentThree Ways Kamailio Can Help Your FreeSWITCH Deployment
Three Ways Kamailio Can Help Your FreeSWITCH Deployment
 
SIPREC RTPEngine Media Forking
SIPREC RTPEngine Media ForkingSIPREC RTPEngine Media Forking
SIPREC RTPEngine Media Forking
 
Software Defined Datacenter with Proxmox
Software Defined Datacenter with ProxmoxSoftware Defined Datacenter with Proxmox
Software Defined Datacenter with Proxmox
 
Getting a live_transcript_of_your_call_using_the_ari
Getting a live_transcript_of_your_call_using_the_ariGetting a live_transcript_of_your_call_using_the_ari
Getting a live_transcript_of_your_call_using_the_ari
 
Asterisk Rest Interface - ARI
Asterisk Rest Interface - ARIAsterisk Rest Interface - ARI
Asterisk Rest Interface - ARI
 
Kamailio on Docker
Kamailio on DockerKamailio on Docker
Kamailio on Docker
 
Kamailio - API Based SIP Routing
Kamailio - API Based SIP RoutingKamailio - API Based SIP Routing
Kamailio - API Based SIP Routing
 
FreeSWITCH Cluster by K8s
FreeSWITCH Cluster by K8sFreeSWITCH Cluster by K8s
FreeSWITCH Cluster by K8s
 
Kamailio :: A Quick Introduction
Kamailio :: A Quick IntroductionKamailio :: A Quick Introduction
Kamailio :: A Quick Introduction
 
Kamailio World 2014 - Kamailio - The Platform for Interoperable WebRTC
Kamailio World 2014 - Kamailio - The Platform for Interoperable WebRTCKamailio World 2014 - Kamailio - The Platform for Interoperable WebRTC
Kamailio World 2014 - Kamailio - The Platform for Interoperable WebRTC
 
Kamailio - Load Balancing Load Balancers
Kamailio - Load Balancing Load BalancersKamailio - Load Balancing Load Balancers
Kamailio - Load Balancing Load Balancers
 
JavaScript Promises
JavaScript PromisesJavaScript Promises
JavaScript Promises
 
FreeSWITCH as a Microservice
FreeSWITCH as a MicroserviceFreeSWITCH as a Microservice
FreeSWITCH as a Microservice
 
Why is Kamailio so different? An introduction.
Why is Kamailio so different? An introduction.Why is Kamailio so different? An introduction.
Why is Kamailio so different? An introduction.
 
ZeroMQ Is The Answer
ZeroMQ Is The AnswerZeroMQ Is The Answer
ZeroMQ Is The Answer
 
Real Time Communication using Node.js and Socket.io
Real Time Communication using Node.js and Socket.ioReal Time Communication using Node.js and Socket.io
Real Time Communication using Node.js and Socket.io
 
Asterisk WebRTC frontier: make client SIP Phone with sipML5 and Janus Gateway
Asterisk WebRTC frontier: make client SIP Phone with sipML5 and Janus GatewayAsterisk WebRTC frontier: make client SIP Phone with sipML5 and Janus Gateway
Asterisk WebRTC frontier: make client SIP Phone with sipML5 and Janus Gateway
 
FreeSWITCH on Docker
FreeSWITCH on DockerFreeSWITCH on Docker
FreeSWITCH on Docker
 

Destacado

WebRTC & Asterisk 11
WebRTC & Asterisk 11WebRTC & Asterisk 11
WebRTC & Asterisk 11Sanjay Willie
 
Implementation Lessons using WebRTC in Asterisk
Implementation Lessons using WebRTC in AsteriskImplementation Lessons using WebRTC in Asterisk
Implementation Lessons using WebRTC in AsteriskMoises Silva
 
Minion pool - a worker pool for nodejs
Minion pool - a worker pool for nodejsMinion pool - a worker pool for nodejs
Minion pool - a worker pool for nodejsMarcelo Gornstein
 
WebRTC From Asterisk to Headline - MoNage
WebRTC From Asterisk to Headline - MoNageWebRTC From Asterisk to Headline - MoNage
WebRTC From Asterisk to Headline - MoNageChad Hart
 
Pagi World from RPI Licato and Bringsjord
Pagi World from RPI Licato and BringsjordPagi World from RPI Licato and Bringsjord
Pagi World from RPI Licato and Bringsjorddiannepatricia
 
6 Months with WebRTC
6 Months with WebRTC6 Months with WebRTC
6 Months with WebRTCArin Sime
 
Vtiger CRM and asterisk
Vtiger CRM and asteriskVtiger CRM and asterisk
Vtiger CRM and asteriskRommel León
 
Las 12 pruebas de Asterisk
Las 12 pruebas de AsteriskLas 12 pruebas de Asterisk
Las 12 pruebas de AsteriskElio Rojano
 
Profundizando manager
Profundizando managerProfundizando manager
Profundizando managerElio Rojano
 
Exploring the Possibilities of Sencha and WebRTC
Exploring the Possibilities of Sencha and WebRTCExploring the Possibilities of Sencha and WebRTC
Exploring the Possibilities of Sencha and WebRTCGrgur Grisogono
 
Tecnicas monitoreo reportes con Asterisk
Tecnicas monitoreo reportes con AsteriskTecnicas monitoreo reportes con Asterisk
Tecnicas monitoreo reportes con AsteriskNicolás Gudiño
 
Writing High Quality Code
Writing High Quality CodeWriting High Quality Code
Writing High Quality CodeGrgur Grisogono
 
Developing an ivr payment system with asterisk (astricon 2014 las vegas nevada)
Developing an ivr payment system with asterisk (astricon 2014 las vegas nevada)Developing an ivr payment system with asterisk (astricon 2014 las vegas nevada)
Developing an ivr payment system with asterisk (astricon 2014 las vegas nevada)Digium
 
MITIGASI DAN ADAPTASI BENCANA ALAM
MITIGASI DAN ADAPTASI BENCANA ALAMMITIGASI DAN ADAPTASI BENCANA ALAM
MITIGASI DAN ADAPTASI BENCANA ALAMTuti Rina Lestari
 
Asterisk and WebRTC - Digium 'Demo & Eggs' Presentation Slides
Asterisk and WebRTC - Digium 'Demo & Eggs' Presentation SlidesAsterisk and WebRTC - Digium 'Demo & Eggs' Presentation Slides
Asterisk and WebRTC - Digium 'Demo & Eggs' Presentation SlidesDavid Duffett dCAP
 

Destacado (16)

WebRTC & Asterisk 11
WebRTC & Asterisk 11WebRTC & Asterisk 11
WebRTC & Asterisk 11
 
Implementation Lessons using WebRTC in Asterisk
Implementation Lessons using WebRTC in AsteriskImplementation Lessons using WebRTC in Asterisk
Implementation Lessons using WebRTC in Asterisk
 
Minion pool - a worker pool for nodejs
Minion pool - a worker pool for nodejsMinion pool - a worker pool for nodejs
Minion pool - a worker pool for nodejs
 
WebRTC From Asterisk to Headline - MoNage
WebRTC From Asterisk to Headline - MoNageWebRTC From Asterisk to Headline - MoNage
WebRTC From Asterisk to Headline - MoNage
 
Pagi World from RPI Licato and Bringsjord
Pagi World from RPI Licato and BringsjordPagi World from RPI Licato and Bringsjord
Pagi World from RPI Licato and Bringsjord
 
6 Months with WebRTC
6 Months with WebRTC6 Months with WebRTC
6 Months with WebRTC
 
Vtiger CRM and asterisk
Vtiger CRM and asteriskVtiger CRM and asterisk
Vtiger CRM and asterisk
 
Las 12 pruebas de Asterisk
Las 12 pruebas de AsteriskLas 12 pruebas de Asterisk
Las 12 pruebas de Asterisk
 
Profundizando manager
Profundizando managerProfundizando manager
Profundizando manager
 
Exploring the Possibilities of Sencha and WebRTC
Exploring the Possibilities of Sencha and WebRTCExploring the Possibilities of Sencha and WebRTC
Exploring the Possibilities of Sencha and WebRTC
 
Tecnicas monitoreo reportes con Asterisk
Tecnicas monitoreo reportes con AsteriskTecnicas monitoreo reportes con Asterisk
Tecnicas monitoreo reportes con Asterisk
 
Writing High Quality Code
Writing High Quality CodeWriting High Quality Code
Writing High Quality Code
 
Developing an ivr payment system with asterisk (astricon 2014 las vegas nevada)
Developing an ivr payment system with asterisk (astricon 2014 las vegas nevada)Developing an ivr payment system with asterisk (astricon 2014 las vegas nevada)
Developing an ivr payment system with asterisk (astricon 2014 las vegas nevada)
 
MITIGASI DAN ADAPTASI BENCANA ALAM
MITIGASI DAN ADAPTASI BENCANA ALAMMITIGASI DAN ADAPTASI BENCANA ALAM
MITIGASI DAN ADAPTASI BENCANA ALAM
 
Artikel Ilmiah Non Penelitian
Artikel Ilmiah Non PenelitianArtikel Ilmiah Non Penelitian
Artikel Ilmiah Non Penelitian
 
Asterisk and WebRTC - Digium 'Demo & Eggs' Presentation Slides
Asterisk and WebRTC - Digium 'Demo & Eggs' Presentation SlidesAsterisk and WebRTC - Digium 'Demo & Eggs' Presentation Slides
Asterisk and WebRTC - Digium 'Demo & Eggs' Presentation Slides
 

Similar a Phpconf 2013 - Agile Telephony Applications with PAMI and PAGI

Real-Time Python Web: Gevent and Socket.io
Real-Time Python Web: Gevent and Socket.ioReal-Time Python Web: Gevent and Socket.io
Real-Time Python Web: Gevent and Socket.ioRick Copeland
 
Stuff we noticed while building "Asterisk in the cloud"
Stuff we noticed while building "Asterisk in the cloud"Stuff we noticed while building "Asterisk in the cloud"
Stuff we noticed while building "Asterisk in the cloud"troyd
 
Tornado Web Server Internals
Tornado Web Server InternalsTornado Web Server Internals
Tornado Web Server InternalsPraveen Gollakota
 
Introduction to Go language
Introduction to Go languageIntroduction to Go language
Introduction to Go languageTzar Umang
 
Node.js: CAMTA Presentation
Node.js: CAMTA PresentationNode.js: CAMTA Presentation
Node.js: CAMTA PresentationRob Tweed
 
Python Streaming Pipelines with Beam on Flink
Python Streaming Pipelines with Beam on FlinkPython Streaming Pipelines with Beam on Flink
Python Streaming Pipelines with Beam on FlinkAljoscha Krettek
 
Flink Forward Berlin 2018: Thomas Weise & Aljoscha Krettek - "Python Streamin...
Flink Forward Berlin 2018: Thomas Weise & Aljoscha Krettek - "Python Streamin...Flink Forward Berlin 2018: Thomas Weise & Aljoscha Krettek - "Python Streamin...
Flink Forward Berlin 2018: Thomas Weise & Aljoscha Krettek - "Python Streamin...Flink Forward
 
Ob1k presentation at Java.IL
Ob1k presentation at Java.ILOb1k presentation at Java.IL
Ob1k presentation at Java.ILEran Harel
 
Pwning Your Phone with Adhearsion and Asterisk
Pwning Your Phone with Adhearsion and AsteriskPwning Your Phone with Adhearsion and Asterisk
Pwning Your Phone with Adhearsion and Asteriskjicksta
 
Adhearsion and Telegraph Framework Presentation
Adhearsion and Telegraph Framework PresentationAdhearsion and Telegraph Framework Presentation
Adhearsion and Telegraph Framework PresentationJustin Grammens
 
Build reliable, traceable, distributed systems with ZeroMQ
Build reliable, traceable, distributed systems with ZeroMQBuild reliable, traceable, distributed systems with ZeroMQ
Build reliable, traceable, distributed systems with ZeroMQRobin Xiao
 
Psgi Plack Sfpm
Psgi Plack SfpmPsgi Plack Sfpm
Psgi Plack Sfpmsom_nangia
 
Psgi Plack Sfpm
Psgi Plack SfpmPsgi Plack Sfpm
Psgi Plack Sfpmwilburlo
 
Original slides from Ryan Dahl's NodeJs intro talk
Original slides from Ryan Dahl's NodeJs intro talkOriginal slides from Ryan Dahl's NodeJs intro talk
Original slides from Ryan Dahl's NodeJs intro talkAarti Parikh
 
Getting started with SIP Express Media Server SIP app server and SBC - workshop
Getting started with SIP Express Media Server SIP app server and SBC - workshopGetting started with SIP Express Media Server SIP app server and SBC - workshop
Getting started with SIP Express Media Server SIP app server and SBC - workshopstefansayer
 
Controlling Arduino With PHP
Controlling Arduino With PHPControlling Arduino With PHP
Controlling Arduino With PHPThomas Weinert
 

Similar a Phpconf 2013 - Agile Telephony Applications with PAMI and PAGI (20)

Erlang OTP
Erlang OTPErlang OTP
Erlang OTP
 
Real-Time Python Web: Gevent and Socket.io
Real-Time Python Web: Gevent and Socket.ioReal-Time Python Web: Gevent and Socket.io
Real-Time Python Web: Gevent and Socket.io
 
Stuff we noticed while building "Asterisk in the cloud"
Stuff we noticed while building "Asterisk in the cloud"Stuff we noticed while building "Asterisk in the cloud"
Stuff we noticed while building "Asterisk in the cloud"
 
Tornado Web Server Internals
Tornado Web Server InternalsTornado Web Server Internals
Tornado Web Server Internals
 
Ruby voip
Ruby voipRuby voip
Ruby voip
 
Introduction to Go language
Introduction to Go languageIntroduction to Go language
Introduction to Go language
 
Node.js: CAMTA Presentation
Node.js: CAMTA PresentationNode.js: CAMTA Presentation
Node.js: CAMTA Presentation
 
Python Streaming Pipelines with Beam on Flink
Python Streaming Pipelines with Beam on FlinkPython Streaming Pipelines with Beam on Flink
Python Streaming Pipelines with Beam on Flink
 
Flink Forward Berlin 2018: Thomas Weise & Aljoscha Krettek - "Python Streamin...
Flink Forward Berlin 2018: Thomas Weise & Aljoscha Krettek - "Python Streamin...Flink Forward Berlin 2018: Thomas Weise & Aljoscha Krettek - "Python Streamin...
Flink Forward Berlin 2018: Thomas Weise & Aljoscha Krettek - "Python Streamin...
 
Ob1k presentation at Java.IL
Ob1k presentation at Java.ILOb1k presentation at Java.IL
Ob1k presentation at Java.IL
 
Plack - LPW 2009
Plack - LPW 2009Plack - LPW 2009
Plack - LPW 2009
 
Pwning Your Phone with Adhearsion and Asterisk
Pwning Your Phone with Adhearsion and AsteriskPwning Your Phone with Adhearsion and Asterisk
Pwning Your Phone with Adhearsion and Asterisk
 
Adhearsion and Telegraph Framework Presentation
Adhearsion and Telegraph Framework PresentationAdhearsion and Telegraph Framework Presentation
Adhearsion and Telegraph Framework Presentation
 
Build reliable, traceable, distributed systems with ZeroMQ
Build reliable, traceable, distributed systems with ZeroMQBuild reliable, traceable, distributed systems with ZeroMQ
Build reliable, traceable, distributed systems with ZeroMQ
 
Psgi Plack Sfpm
Psgi Plack SfpmPsgi Plack Sfpm
Psgi Plack Sfpm
 
Psgi Plack Sfpm
Psgi Plack SfpmPsgi Plack Sfpm
Psgi Plack Sfpm
 
Original slides from Ryan Dahl's NodeJs intro talk
Original slides from Ryan Dahl's NodeJs intro talkOriginal slides from Ryan Dahl's NodeJs intro talk
Original slides from Ryan Dahl's NodeJs intro talk
 
NodeJS
NodeJSNodeJS
NodeJS
 
Getting started with SIP Express Media Server SIP app server and SBC - workshop
Getting started with SIP Express Media Server SIP app server and SBC - workshopGetting started with SIP Express Media Server SIP app server and SBC - workshop
Getting started with SIP Express Media Server SIP app server and SBC - workshop
 
Controlling Arduino With PHP
Controlling Arduino With PHPControlling Arduino With PHP
Controlling Arduino With PHP
 

Último

How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerThousandEyes
 
How to convert PDF to text with Nanonets
How to convert PDF to text with NanonetsHow to convert PDF to text with Nanonets
How to convert PDF to text with Nanonetsnaman860154
 
Partners Life - Insurer Innovation Award 2024
Partners Life - Insurer Innovation Award 2024Partners Life - Insurer Innovation Award 2024
Partners Life - Insurer Innovation Award 2024The Digital Insurer
 
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking MenDelhi Call girls
 
🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘RTylerCroy
 
CNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of ServiceCNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of Servicegiselly40
 
What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?Antenna Manufacturer Coco
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Drew Madelung
 
Strategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a FresherStrategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a FresherRemote DBA Services
 
Tech Trends Report 2024 Future Today Institute.pdf
Tech Trends Report 2024 Future Today Institute.pdfTech Trends Report 2024 Future Today Institute.pdf
Tech Trends Report 2024 Future Today Institute.pdfhans926745
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerThousandEyes
 
Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationRadu Cotescu
 
Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsJoaquim Jorge
 
Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processorsdebabhi2
 
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfThe Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfEnterprise Knowledge
 
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemkeProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemkeProduct Anonymous
 
presentation ICT roal in 21st century education
presentation ICT roal in 21st century educationpresentation ICT roal in 21st century education
presentation ICT roal in 21st century educationjfdjdjcjdnsjd
 
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot TakeoffStrategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoffsammart93
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024The Digital Insurer
 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationSafe Software
 

Último (20)

How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
How to convert PDF to text with Nanonets
How to convert PDF to text with NanonetsHow to convert PDF to text with Nanonets
How to convert PDF to text with Nanonets
 
Partners Life - Insurer Innovation Award 2024
Partners Life - Insurer Innovation Award 2024Partners Life - Insurer Innovation Award 2024
Partners Life - Insurer Innovation Award 2024
 
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
 
🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘
 
CNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of ServiceCNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of Service
 
What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
 
Strategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a FresherStrategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a Fresher
 
Tech Trends Report 2024 Future Today Institute.pdf
Tech Trends Report 2024 Future Today Institute.pdfTech Trends Report 2024 Future Today Institute.pdf
Tech Trends Report 2024 Future Today Institute.pdf
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organization
 
Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and Myths
 
Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processors
 
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfThe Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
 
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemkeProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
 
presentation ICT roal in 21st century education
presentation ICT roal in 21st century educationpresentation ICT roal in 21st century education
presentation ICT roal in 21st century education
 
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot TakeoffStrategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024
 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
 

Phpconf 2013 - Agile Telephony Applications with PAMI and PAGI

Notas del editor

  1. Good evening! Thank you all for coming in today, considering that we&apos;re the last 3 talks of the day, and also, considering that the beer has already been delivered! So I guess this topic should be of interest to you, and I hope that the talk is good enough to encourage that interest even more.
  2. So a bit about me, first. My name is Marcelo Gornstein, I&apos;m the author of several open source projects (two of them are the reason for this talk, : PAGI and PAMI). I&apos;ve been working in the IT field for a bit more than 15 years, but I do have a few more years as a hobbist. Currently, I&apos;m working at Inaka, as an erlang developer, which is awesome, because this gives me the chance to use a languange and an environment that I love, on a daily basis. At Inaka we mostly do social applications, that handle millions of users per day. And we do everything, from the server (which is almost always written in erlang) up to the applications for the final users, most of the time, Android and IOS applications. On the personal side and besides my current work, I had to deal with a lot of different scenarios, from parking and virtual hosting systems to telephony, and other backend stuff. And in the last few years I have dedicated myself a bit more to the telephony world, specially voip. For those who are interested, those are my github account and my personal website, with other open source projects and some articles that I like to write from time to time.
  3. Now about the talk for today. I thought about splitting the talk in 5 big topics, as some kind of roadmap that can take us from the big overview, up to the very details, as some kind of zoom in into the subject. We&apos;re going to start by defining some telephony concepts, as a way to state a common language between us, with the ones that are not familiarized with the subject, but also with the ones that already have some experience in the field. Afterwards, there&apos;s a small introduction to the asterisk world, but mostly to the concepts that are more important to us, as developers, like the interfaces that asterisk provides so we can actually do something useful with it. And the final part of the talk is more technical, focused in how to actually use those interfaces with the frameworks pagi and pami, to create our cool applications.
  4. So let&apos;s get started. When we first start working with asterisk, there are a couple of words that tend to appear more often, so let&apos;s define them, to get a common language between us.
  5. An IVR is just a system (an interface actually) that allow us to interact with a computer, using our voice or the DTMF tones. The DTMF tones are generated each time we press a button on our numeric keypad, in our telephones. Lots of IVRs application can be found out there, like surveys, customer support systems, voicemails, etc. This kind of stuff is the window that we can use to enter this new world!
  6. Now, every time we speak about an IVR, we can also speak about call flows. A call flow is actually just a flow chart, where we have “nodes” (let&apos;s remember that word, “nodes” for the last part of the talk), and those nodes are connected to each other with arrows. There are nodes that represent actions (like telling when a sound needs to be played), but also nodes where decisions are made and so and so. The flow itself describes how the ivr is navigated, where the user is asked for input, how that input is validated, what happens when the user presses 1, or 2, or the asterisk in the menu, etc.
  7. Another word that we probably will hear about is PBX. Let&apos;s say for now that a pbx is just a small telephone exchange. It will serve a small number of “extensions” (let&apos;s say extensions, and “subscribers” when speaking about telephone exchanges). A PBX is small, meaning that it will be serving an office, or a specific floor of a building, or maybe the building itself, although that would even be a bit big. A lot of pbx nowadays will offer some IVRs, like voicemails, very similar to what our mobile or landline telephone company can offer.
  8. So what is a telephone exchange? For what we care, a telephone exchange is a system that is part of the public switched telephone network, and it has all the needed components to actually switch the needed circuits to let two points of the network talk between each other, either two subscribers or maybe even other telephone exchanges. Let&apos;s see a brief historic summary, of how do we go from these definitions up to the point of why asterisk exists, what can we do with it, and what is exactly used for.
  9. Telephone exchanges are the magic thing behind the scenes. Without them, we wouldn&apos;t be able to do much, actually. Telephone exchanges (let&apos;s call them switches, from now on) are what allow us to route calls from one subscriber to another, and the actual connections between them are the core of the public switched telephone network.
  10. The first switches used humans, that literally connected the right plugs to close the needed circuits to establish a connection between two subscribers.
  11. A few years later, these crazy machines were invented, these were mechanical switches, that had very complex mechanical components, like relays, motors, and other stuff. These where too big, compared to the number of subscribers that they could serve, so.. not much to say about them.
  12. Some time later, these digital switches appeared, with modern electronic components (transistors), and the software started to enter the scene, these switches could be programmed, and also the very first IVRs were born
  13. And nowadays we have these “softswitches”, where the software has completely taken over the situation, even in the name (the word “soft” is actually referring to software). They are standard computers, with standard hardware, but also with standard operating systems, like linux, solaris, etc. They are rackable computers, much smaller than their ancestors, and cheaper. Since they have standard operating systems, everyone can actually create software for them.
  14. This means that we can even use open source platforms, like asterisk
  15. Or freeswitch, or any of the available alternatives
  16. Currently, the public switched telephone network is a mixed environment, where we can find modern softswitches, along with old digital switches that haven&apos;t been replaced yet, and also along new softswitches using software like asterisk and freeswitch. And this is really great for us, because we can download and install the very same software that&apos;s being used right now, in the telephony world. In the past, the only way to access such systems was to work for a company, pay a lot of money, even wait for a lot of time for simple support requests. Today we can just download the software, invest a weekend playing around with it, and we&apos;re ready to start creating our own applications, even submit new features to these projects, or change the source code to match our needs. We now have a complete new market to work for, where we can create applications, get new clients, have fun, and learn a lot of new stuff!
  17. We can now create our own applications, like voicemails, for companies or even home subscribers...
  18. Or regular ivrs, like the “official hour of the day”...
  19. … or customer support systems, autoprovisioning, or a ticket system, to let users report problems..
  20. .. also some very specific platforms, like platforms for prepaid calling cards, that allows anyone to buy a calling card and use it in our system, to make calls, transfer funds, etc...
  21. ...and we can also create telephone campaign systems, to do surveys, or advertising, just like those times when a famous politician or actor call us before the elections, the kind of applications that we like so much!
  22. So as a summary. We now know that asterisk is software, and that it can be run in a wide set of unix-like operating systems. We also know that it&apos;s more a pbx than anything else, although it can be used as a softswitch in a small-medium scale, since it wasn&apos;t really designed as a softswitch in the first place. It&apos;s also very easy to use, which is great for us as developers, we can get it up and running in a short time. We can also connect different kind of endpoints to it, like SIP, R2, SS7, Jabber, etc. R2 and SS7, for those who haven&apos;t heard about them, are the standard protocols for the public switched telephone network. R2 is analog, it uses dtmf tones for signalling, and SS7 is pretty much what is out there now, a complete stack of digital protocols, that can also send and receive digital data, etc. Asterisk also has a huge community, with thousands of available propietary and free or open source applications where to choose from. And also, it has two interfaces for us, developers, to actually work with it and create our own applications.
  23. So this is a good time to ask some questions before moving on to the gory details of asterisk. We have now taken a ride through the telephony world, what&apos;s out there, what&apos;s asterisk, how can we use it, and where we, developers, stand in all this new world.
  24. Ok, let&apos;s start looking at what is most important for us, as developers, to know about asterisk. First of all, asterisk has a dialplan. The dialplan allow us to route calls, that means getting the call to where we want to, based on different criteria, like destination number, or other stuff. On the other hand, the dialplan is where we can sequentially program what is going to happen to that call, when it&apos;s going to be answered, what messages are going to be played to it, or if we want to accept input from the caller, etc. It is effectively the roadmap for the call, line by line. This takes me to the second point, which is the dialplan applications. Asterisk has a lot of them by default, and they let us do our thing, they are used to answer a call, play audio, accept input, play tones, get information like the caller id, etc. We actually use these from the dialplan to execute stuff for that call. Also, there are lot of propietary and open source applications out there, that we can download, compile, and use to extend the features of our dialplan. And last but not least, the third important point, are the AGI and AMI interfaces that asterisk offers, so we can write applications without depending on the dialplan.
  25. Here&apos;s a small dialplan example. The dialplan is nothing more than a mere text file, that asterisk reads as part of its configuration, it&apos;s actually called extensions.conf. In this small example, there are two “contexts”. Let&apos;s say that a dialplan can be divided into contexts, and in this case there are two of them, named “default”, and “say_hi”. For the sake of the example, let&apos;s say that calls start (because of system configuration), into the context default. So in this case, we&apos;re saying that we want to handle the calls with destination number equal to “113”. What we&apos;re going to do with those calls is to answer them, and then immediately jump to the context “say_hi”, where we are going to play a welcome message and read up to 4 digits from the user. We then read that back to the user, and execute an agi application. Note that we are only using dialplan functions, like Answer, Goto, Playback, etc, including the AGI dialplan function. This tell asterisk to actually fork a new process and execute the command /usr/ivr/run.php, the call will get handlded by that agi application aftewards. So without paying too much attention to the details, the idea for this slide is to actually show what&apos;s the first tool that we can use to create an ivr application, which is the dialplan, and also, to show how limited this tool is, and why we need interfaces like AMI and AGI to do more powerful things (it&apos;s not that awesome if we need to redeploy asterisk configuration files for a change in our application, right?)
  26. So let&apos;s talk a bit more about those interfaces. These are called AMI and AGI (Asterisk Manager Interface, and Asterisk Gateway Interface). It&apos;s really important that the difference between them is clear. With AMI, we use a TCP socket to connect to asterisk, and we can execute commands that affect the whole box and all the ongoing calls, like reloading configuration, restarting modules, terminating channels, originating new calls, etc. Also, we can listen for events in the whole system. On the other hand, with AGI, we can create IVR applications, everything we execute will only affect the current call. With AMI, we can react to realtime events, writing to a db (like for a crm, like sugarcrm, etc), and we can also execute stuff, create console operators, and things like that. With AGI, we can write systems that do surveys, or voicemails, etc.
  27. Let&apos;s now take a look at what&apos;s behind the scenes of AGI and AMI, which (suprisingly) are the AGI and AMI protocols.
  28. AMI uses TCP as the transport protocol for the messages, which are plain text, and separated by a standard dos-like newline, \r\n, similar to how other protocols, like http, and smtp, work. Each message contains lines which are also separated by \r\n. When using AMI, there are three concepts that we wiil work with. Events, actions, and responses. Events are sent asynchronously by asterisk, that means that we don&apos;t know in advance, when a specific event or set of events will arrive, but asterisk will send them as interesting things happen in the system, like the start of a new call, a channel in the system changing state, etc. On the other hand, actions are sent by us to execute commands in the box, we can edit configuration, reload modules, terminate channels, etc. And each one of those actions have a response, that will tell us what happened to that specific command. Sometimes, a response will have associated events with it, that complete it, so those events wont be related to things happening in the system, but as part of a response to an action.
  29. Now let&apos;s see the AGI protocol. Just like AMI, this protocol is plain text, but it&apos;s always synchronous, we send a command, and we get a response. All commands will only affect the current call. With this command we can create our IVR applications. Also, the lines are separated by \r\n. AGI was originally designed to use stdin and stdout as transport for the messages. If you think about it, it kind of makes sense to use stdin and stdout because the original idea is that we&apos;re going to tell asterisk to fork a process and execute a file, by using the AGI dialplan function, like we mentioned before. So that makes sense, to use stdin and stdout as a way of doing IPC between asterisk and the new process. But, eventually, they understood that this wasn&apos;t scalable, because we&apos;re forced to run all the calls in just one server. So they came up with fastagi, which is basically the same, but using tcp as the transport for the AGI protocol. With AGI we can play sounds, get input, send and receive faxes, etc.
  30. As you can see these are very simple protocols, and writing a client would only involve a couple of function calls, like fopen, fread, fwrite, and the like, plus some text parsing. This is part of the success of asterisk, because we can write clients in any language, even shell scripts, and it&apos;s fun. But, they are a bit primitive to be working with them directly, we actually have very specific needs as developers, we want to write code that&apos;s easily readable and maintainable, and also we want to know as few implementation details as possible when dealing with 3 rd party products like asterisk. We just want to focus on our code, on the business logic of our application, and mainly do what we want to do with as few distractions as possible. That&apos;s why we need a proper layer of abstraction over the AGI and AMI protocols and interfaces.
  31. So let&apos;s talk a bit about PAMI and PAGI, which are the php clients for the AGI and AMI interfaces. There are other frameworks out there, but PAGI and PAMI were born due to a not-so-great personal experience while using other frameworks. I think that the best thing about PAGI and PAMI is that they offer a proper layer of abstraction, comfortable enough for us, devs, to work with. They are closer to what we would expect of them in every sense, they help us write code and hide lot of asterisk implementation details. They let us focus on the important parts of the application, the business logic. Also, they are unit tested, there&apos;s a jenkins server that automatically runs the tests and generates api documentation, and the coverage is almost always around 100%, which is cool, not a bulletproof thing, but a very safe bet. They are object oriented, and they are easy to use and extend. They use log4php, too. For those who don&apos;t know about it, it&apos;s a logger, and an apache project. It offers similar features to what log4j offers in the java world, giving us the option to route different log classes to different appenders, it has NDC and MDC which are really helpful when tracing our application behavior at runtime, and other cool features. So with PAMI, we can write monitoring applications, write accounting logs, send and receive sms or calls, queue or park calls, write console operators, and a long list of etceteras. With PAGI, we can write our IVR applications.
  32. Let&apos;s now enter the final part of the talk, with lots of code ahead, the fun part begins. Let&apos;s see how to write applications using PAMI and PAGI to interact with asterisk.
  33. So a very basic example of using PAMI. First we instantiate the client, passing in some options, like the credentials, and host and port of the asterisk box. We can also pass the log4php configuration file, but this can also be done by ourselves in our application, and pami will use the already configured log4php instance. So once we have the client, we just call open() to connect to asterisk, and close() when we&apos;re done to close the connection. What we see in the middle is the main loop. This is actually needed because we don&apos;t have threads in php, or any other way to read the data that asterisk is sending in parallel to our code. So what we see here is actually the skeleton of a cliché pami application, where we connect, do our thing (sending actions and/or listening for events), and then close the connection. This might very well be a daemon that runs in the background, or a process that does some specific things like sending sms or originating calls.
  34. To execute actions we just need to instantiate the appropiate class and use the method send to send it. The actions in pami are represented by classes, and we can find all the available actions in the api documentation and also in the asterisk wiki. Different actions will have different arguments for the constructor, and also different methods to manipulate it. Once we send the action, we get a response, and we can also look into that response to find any specific information we&apos;re interested in. Sending an action is a synchronous process, that means that the method send will return only when a response has arrived, and not before, including any associated events.
  35. We can also listen for events, by registering an event listener, calling the registerEventListener() method. This allow us to listen for events coming in from asterisk, and we can register an anonymous function, but we can also register a specific method of an already instantiated object. The execution of these listeners is completely asynchronous, we don&apos;t know when they will be executed or if they will be executed at all, because it will depend on when asterisk decides to send us the events. Once we get an event, we can manipulate it, get it&apos;s properties, name, etc. We can register as many event listeners as needed.
  36. We also have the change to filter the incoming events before they reach the listeners. For this, we use the 2 nd argument of the registerEventListener() method, which is closure, called “predicate”. Predicates will return true or false, and true means that we want to let that event pass. In the example, we&apos;re filtering for events of type “dial”, but only those that indicate a new call (subevent begin). The exact details of what events are sent or needed to do our job is mostly in the asterisk wiki.
  37. Now let&apos;s see a more complete example, coming directly from the real world. This is mostly used in telephone campaigns, where we want to dispatch a lot of calls and then trace what happened with those calls. Were they answered? Were they busy? Etc. In this case, we instantiate the Originate action, passing as argument the channel to dial. It doesn&apos;t actually matter what is a channel for this talk, let&apos;s just say that a channel can mean a real phone number, a sip device, etc. In this case, is the target we&apos;re trying to reach. So we&apos;re telling asterisk that for that call, more specifically, when that call IS ANSWERED, we want to execute an agi application, which is contained in the file myagi.php, and we&apos;re also telling asterisk that we want to originate the call in an asynchronous fashion. This means that the call will get spooled, send() will return the control immediately and asterisk will tell us what happened with that call later, asynchronously, by sending us a specific event we should listen for. This is the exact opposite of originating a call synchronously, where the send method will only return when that call is over, being answered, busy, or with error. Originating calls asynchronously has the advantage that we can generate a lot of calls without having to process them one by one. We then send the action, and afterwards, execute the now already known main loop of our application.
  38. So we originated a bunch of calls, and now we have to listen for the events that correspond to those calls, that contain the needed information to know how those calls ended. For that, we register an event listener that will handle only OriginateResponse events. So we send an Originate action, and wait for the OriginateResponse events, right? We filter the events that get to that event listener by only letting through the right type of events. Then, in our event listener, we get the result of that call, and we can then do different things based on that. So this code is pretty simple, and it&apos;s actually the skeleton for telephone campaign applications, generating calls and then tracing what happened with those calls, maybe retrying some numbers after a while, writing into a crm, etc.
  39. PAMI is available via composer, and also via a pear channel. Jenkins will also generate api documentation and the phar and tarball files for those who want to use it or install it manually. There&apos;s also more information about pami, the ami protocol, and general pami usage in the first two links.
  40. So let&apos;s enter the final part of the talk. We&apos;re going to dive into PAGI now, and learn how to create IVR applications.
  41. Just like PAMI, PAGI has a client (an AGI client in this case) that we need to instantiate before using it. And we pass in some options, too, like the log4php configuration (again, we could configure log4php in our own application instead of letting pagi do it). Once we get the client, we can start working with it, for example answering or hanging up a call...
  42. We also have access to the asterisk logger. Asterisk has a logger that we can configure to send different logs to different files, or even to the asterisk console, so we can use it and log there. We can also generate calls, for example in our pami example we were originating calls and running an agi application when those calls were answered. This might very well be that agi application that once the call is connected, it will issue another call to some other place, call centers to this a lot to reach potential customers and then forwarding the call to the real seller. Of course we can also know how that call ended, and do specific things with that information.
  43. We can also read input from the user, and know what the user pressed, or even if nothing was input at all. We can manipulate the caller id used to generate calls, or read the caller id information of the incoming calls, we can also start music on hold for the current call
  44. There&apos;s also the chance to manipulate some of the fields of the CDR. The CDR is the call detail record, one CDR entry is saved for every incoming/outgoing call, so we can write some custom stuff in them. We can also play audio for signalling call states, like dial tone, busy tone, etc. But besides the audio, we can also send specific signalling info, like call in progress, or busy, congestion, etc. This is one of the most important features of a softswitch, for example.
  45. And of course we can do a lot more! Like manipulating channel variables, adding or removing sip headers, send and receive faxes, start automatic machine detection, record calls, say dates in a specific format, or digits, or whole numbers, we can also spool calls. Asterisk can be configured to poll in a directory for new files, so when a file is created with a specific format, it will read it and originate calls with the contained information, we can write those files using pagi. And we can also execute custom agi commands, for example when we want to use an extension not natively supported by pagi, so we are not limited in that sense. And there&apos;s more, I invite you all to play with it!
  46. At one side of the pagi client, there&apos;s the pagi application. Its use is completely optional, you&apos;re not required to use it, although its very useful and handy. It gives you a nice skeleton to define your application lifecycle (it has an init method, a run method, and a shutdown method), and it also has some boiler plate code embedded, like signal and error handling, which are very handy, since in php our ivr applications are all separate process, so we should be able to handle those as well. To use a pagiapplication, you only need to extend it and implement the abstract methods shown, then we instantiate it passing in a pagi client, then call init, run, and shutdown. This is not a minor detail, because it allow us to do unit testing real easy, because the pagi application will use “a pagi client” to do its job, but not necessarily the real one, but a mock. Suprisingly enough, this mocked client comes with pagi, and it&apos;s the one we&apos;re going to use to write unit tests for our ivr applications in a minute.
  47. Before moving on to the unit testing, let&apos;s see a small application example, a very small one, suitable to be shown in just one slide. What this application does is to answer a call, and try to read the caller id information of the caller. If it can&apos;t find the needed information, it will play a specific sound, but if it can get the phone number of the caller, it will play another sound and play the digits of that number. In both cases, we play a sound after hanging up.
  48. And this is how a unit test looks like for our ivr applications, this skeleton can be used in almost all pagi applications, and if you look at this closely, you will notice that we&apos;re doing the same things I&apos;ve shown in the last slides. We instantiate a client, pass it to a pagi application as a constructor argument, and then call init, run, and shutdown. The only thing we do differently is that we&apos;re instantiating a MockedClient, instead of the regular pagi client. But everything else is the same, so our application will be run in the same way, either for tests or for production environments.
  49. So what can we do with this mocked client? On one hand, we can assert our application behavior. We can assert that some specific client methods will be called in a specific order, like in this case, we&apos;re asserting that the call will be answered, and that the caller id information will be read from a specific variable, and also that the correct audio files and digits will be played, and that we will hangup the call when done.
  50. We can also specify exact return values for when those client methods are called, like returning true when answering the call, or the custom phone number when asking for the caller id variable, or even the result of the sound played, including any digits the user input while playing it, so that&apos;s pretty cool, we can forge the user behavior and test our application against that!
  51. So... nodes! So we can say that the client allow us to work with the agi protocol from a high level perspective, and we can say more or less the same about nodes. The nodes in pagi are a facade to the client, so we can write applications using an even higher abstraction layer, that feels more natural than using the client, just like using the client feels more natural than using the dialplan directly (or the agi protocol, for that case). A node is intended to represent either one or more of the nodes that we find in a call flow. It will help us write cleaner code, in a declarative way. They implement a fluent interface, so the code ends up being more verbose, it&apos;s DI compatible and can be tested. A node can keep state, and that state can be accessed from inside or outside of the node, for example when we ask for user input and we want to access that input from some other node of our application. We can play sounds, read input, and validate that input. Also, the nodes have real life features, like letting the user interrupt the sounds, or play different messages in different situations, like last input attempt, or on invalid inputs, etc. We can also choose if we consider as input what the user presses during a sound, etc. Also, we can have custom callbacks before or after the node execution, or when a validation fails. And after the node is executed, we can check how it ended, if it was completed, or cancelled, if it had input or exceeded the maximum attempts, etc.
  52. Let&apos;s go back to the callflow I showed at the very first slides, and let&apos;s take a closer look at it. In this case, this call flow describes a small application that allow users to register complains by entering their telephone number. A user calls, a welcome message is played, and then the user is asked to input its telephone number, so the system can validate that number and save that to a database, so this effectively is a user complaining about some malfunctioning in his telephone. So after a user enters its telephone number, the system will validate it, save it, and if it&apos;s not valid will ask the user again and again, until a valid telephone number is entered and the application says “goodbye” and then the call ends. Let&apos;s see how we could code this application using the pagi nodes.
  53. So let&apos;s start by playing the welcome message, and then we&apos;re going to create a node that will represent the rest of the application. We&apos;re creating a node named &apos;get-number&apos;, that will play the sound &apos;enter-number&apos; as a prompt.
  54. Let&apos;s do a pause here, to show you some real world features of pagi. Let&apos;s configure the node with some stuff not present in the callflow, but very probably part of a real world application. Let&apos;s have a maximum number of attempts to input a valid telephone number (instead of looping infinitely), let&apos;s say we have 3 attempts to enter a valid number. Also, we&apos;re going to play a specific message when those maximum attempts have been exceeded, and also, let&apos;s say that a valid phone number has between 10 to 12 digits, and that the user has 3 seconds in total to input all of the digits. If this time is exceeded, we&apos;re going to play another message, and also, another thing that wasn&apos;t in the flow, and something completely optional: we&apos;re going to configure that the user can “cancel” the node, by pressing star, this could be used later to jump to another node (like the previous one). And let&apos;s configure the node to have a special digit to finish the input, like the hash, so when a user enters the digit 10 or 11, he/she can press hash, and the system wont wait for more digits.
  55. Ok, let&apos;s now add a validator. We can add as many as needed, in this case we&apos;re going to add just one. So the first argument is the name of the validator, this is useful for debugging the application and later traceability. The second argument is an anonymous function that is the validator itself, it should return true or false, where true means that the input is valid. The last (3 rd ) argument is the name of the sound we want to play every time this validation fails.
  56. So if the input is valid, we&apos;re going to play a sound
  57. And on the other hand, when the input is not valid, we&apos;re going to do something that wasn&apos;t in the original call flow, but I&apos;d like to show it anyway. We are going to reproduce a sound telling the user to try again, but only if we&apos;re not in the last attempt. This is so, because if we are on the last attempt and it failed, then we can&apos;t tell the user to try again, because there wont be another chance to enter a valid input.
  58. When the node is done, hangup the call
  59. And... run the node!
  60. This is the complete source code for the application. As you can see, it&apos;s actuallly a small piece of code, if we take into account that it is a complete ivr application. Also, there are no ifs, fors, whiles, switch-case, break-continue, etc, which is nice! Nothing weird at sight, we just have a nice piece of declarative code, with our business logic in very specific points, without anything else to worry about.
  61. After the node ends, we can check how it ended. Did it have any input? Was it cancelled? Was the maximum number of attempts exceeded? Etc. After gathering the needed information, we can just run another node, or do other stuff. But what happens when we have lots of nodes? Our application starts to have lots of ifs lying around there, uglyfing our code. Let&apos;s see another way to do this, using the NodeController.
  62. So the NodeController takes its name for what it does. It&apos;s a way of controlling and defining the behavior of our application, in a more integral way. How do we go from one node to the other, and what happens in between. So in this example, we&apos;re defining four very simple nodes. An initial node, that will play a welcome message, a menu, a node that will be used for when the menu reaches the maximum input attempts, and another node that will call a human operator, for example when pressing &apos;0&apos; in the menu, or whatever.
  63. So we have defined our nodes, and let&apos;s now define the behavior for those nodes. We&apos;re going to tell the NodeController that when the initial node is done, it should run the menu. And when the menu finishes due to exceeding the maximum number of attempts, it should jump to the node we created for that particular case. Also, when that node ends, it should hangup the call.
  64. When the menu ends with input &apos;1&apos;, it should jump to a node (that we haven&apos;t defined, but it serves our purpose) called &apos;sales&apos;, and when it ends with input &apos;2&apos;, it should run the node named &apos;support&apos;. In any other case, we&apos;re going to jump to the &apos;operator&apos; node. I wanted to implement this behavior using the jumpAfterEval() method, just to show that it exists. When using this method, we pass in a closure, that will return a string. This string will be evaluated by the NodeController as the name of the node where it should jump afterwards. This is a nice place to put complex and custom logic to manage the navigation.
  65. After we&apos;re done, we just jump to the node where we want to start the execution.
  66. Applications that use nodes can also be unit tested, in a very similar way as the ones that only use the client. We have a mocked node, and we can obtain a MockedNode from it. And we can define that we want to run that node with some specific input, and that input will be consumed by the node, when it needs to. We can also do some asserts, to be sure there&apos;s some specific behavior we want to do, like playing dates, or digits, etc. After that, we just run the pagi application as usual.
  67. Just like PAMI, PAGI is available via composer and pear, and also you can download the phar or tarball file if you&apos;re interested in doing a manual installation. The jenkins server will also autogenerate the api documentation and the coverage reports, and you can also find more about the ami protocol and pami in the first two links.
  68. So before going to the questions, I&apos;d like to thank you all for coming in today, and also to the organizers that did a wonderful job, creating a conference that trascends the fact that it&apos;s called phpconf, having a very nice set of interesting topics. Also, on the personal side, to Mariano Iglesias that gave me the opportunity to be here with you today, and of course my awesome wife, and “the kids”, Qwerty and Dvorak.
  69. And that&apos;s pretty much it... Questions?