Giter VIP home page Giter VIP logo

pagi's Introduction

License Latest Stable Version Documentation Status

Build Status Coverage Status Code Climate Issue Count

![Click here to lend your support to: PAGI and make a donation at pledgie.com !](https://pledgie.com/campaigns/30945.png?skin_name=chrome' border='0')

Introduction

This framework is intended to simply making ivr applications using Asterisk's AGI, providing a nice level of abstraction over what an IVR should look like from a developers' perspective.

Documentation

Installing

Add this library to your Composer configuration. In composer.json:

  "require": {
    "marcelog/pagi": "2.*"
  }

Using it

First, make sure you include the autoloader shipped with composer:

require __DIR__ . '/vendor/autoload.php';

Quickstart

You can start by doc/examples/quickstart for a very basic example. You'll need something like this in your dialplan:

[default]
exten => 1,1,AGI(/path/to/PAGI/doc/examples/quickstart/run.sh,a,b,c,d)
exten => 1,n,Hangup

Testing IVR applications

A mocked pagi client is included to easily test your ivr applications. See doc/examples/mock to see an example of how to use it.

Features

Nodes

For a tutorial about nodes, see this article

Simple Call Flow Nodes are available (see doc/examples/node/example.php). Using nodes will let you simplify how you build and test your ivr applications. Nodes are an abstraction layer above the pagi client, and support:

  • Prompts mixing sound files, playing numbers/digits/datetime's.
  • Cancel and End Of Input digits.
  • Validator callbacks for inputs, can optionally specify 1 or more sound files to play when the validation fails.
  • Callbacks for invalid and valid inputs.
  • Optional sound when no input.
  • Maximum valid input attempts.
  • Optional sound when maximum attempts has been reached.
  • Expecting at least/at most/exactly N digits per input.
  • Timeout between digits in more-than-1 digit inputs.
  • Timeout per input attempt.
  • Retry Attempts for valid inputs.
  • And much more!

The NodeController will let you control the call flow of your application, by registering nodes and actions based on node results. Thus, you can jump from one node to the other on cancel or complete inputs, hangup the call, execute a callback, etc. For an example, see doc/examples/nodecontroller/example.php

An article about the node controller is available here

AutoDial

CallFiles are supported. You can also schedule a call in the future.

Fax

Sending and receiving faxes is supported using spandsp (applications SendFax and ReceiveFax).

Available Facades

  • PAGI\Client\CDR: Provided to access cdr variables.
  • PAGI\Client\ChannelVariables: Provided to access channel variables and asterisk environment variables.
  • PAGI\Client\CallerID: Provided to access caller id variables.
  • PAGI\Client\Result: Provided to wrap up the result for agi commands.
  • PAGI\CallSpool\CallFile: Call file facade.
  • PAGI\CallSpool\CallSpool: Call spool facade.
  • PAGI\Logger\Asterisk: Provides access to asterisk logger (see logger.conf in your asterisk installation).

Results

For every operation, a Result is provided. Some operations decorate this Result to add functionality, like PlayResult, ReadResult, etc. For example, a stream file will return a PlayResult, which decorates a ReadResult which in turn, decorated a Result.

  • PAGI\Client\DialResult
  • PAGI\Client\ExecResult
  • PAGI\Client\ReadResult
  • PAGI\Client\PlayResult
  • PAGI\Client\FaxResult

Debugging, logging

You can optionally set a PSR-3 compatible logger:

$pagi->setLogger($logger);

By default, the client will use the NullLogger.

Developers

This project uses phing. Current tasks include:

Running a phing task

To run a task, just do:

vendor/bin/phing build

Contributing

To contribute:

  • Make sure you open a concise and short pull request.
  • Throw in any needed unit tests to accomodate the new code or the changes involved.
  • Run phing and make sure everything is ok before submitting the pull request (make phpmd and CodeSniffer happy, also make sure that phpDocumentor does not throw any warnings, since all our documentation is automatically generated).
  • Your code must comply with PSR-2, CodeSniffer should take care of that.

LICENSE

Copyright 2011 Marcelo Gornstein [email protected]

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

pagi's People

Contributors

agvstin avatar marcelog avatar rybakit avatar thomasvargiu avatar wormling avatar xware42 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

pagi's Issues

NodeController ExpectAtleast Hangup

Hello,

I seem to be having an issue with the NodeController when setting up a node to accept a range of input lengths, for e.g. using:

->expectAtleast(2)
->expectAtMost(3)

I've also got a function registered I'd expect it to go to:

$self =& $this;
$this->cont->registerResult('verifyMenu')->onMaxAttemptsReached()->execute(function() use ($self) { $self->_fail(); });

If the user tries and input that's too long, or just doesn't enter anything, it goes to that function successfully - but if they enter something but it's not long enough to meet the minimum, it just hangs up.

To get around this, I've had to replace expectAtleast(7) with

->validateInputWith(
   'option',
      function(Node $node) {
         return strlen( $node->getInput() ) >= 7 ? true : false;
      })

I'll try and pin down what's causing this, but in the meantime, asterisk logs:

[Sep  8 10:43:17]  /home/callcentrepagi/agi_entry.php: Going to CS Verify Menu
[Sep  8 10:43:17]     -- AGI Script Executing Application: (LOG) Options: (DEBUG,NodeController: extOrderVerify: unknown -> 56999999444214441: Running verifyMenu)
[Sep  8 10:43:17]     -- AGI Script Executing Application: (LOG) Options: (DEBUG,Node: verifyMenu: unknown -> 56999999444214441: streamFile(/home/a1audio/extension_orderverify_numberPrompt,0123456789*#))
[Sep  8 10:43:17]     -- Playing '/home/a1audio/extension_orderverify_numberPrompt' (escape_digits=0123456789*#) (sample_offset 0)
[Sep  8 10:43:20] DTMF[23980]: channel.c:4151 __ast_read: DTMF begin '4' received on SIP/A1General-000002d5
[Sep  8 10:43:20] DTMF[23980]: channel.c:4155 __ast_read: DTMF begin ignored '4' on SIP/A1General-000002d5
[Sep  8 10:43:20] DTMF[23980]: channel.c:4066 __ast_read: DTMF end '4' received on SIP/A1General-000002d5, duration 220 ms
[Sep  8 10:43:20] DTMF[23980]: channel.c:4135 __ast_read: DTMF end passthrough '4' on SIP/A1General-000002d5
[Sep  8 10:43:20]     -- AGI Script Executing Application: (LOG) Options: (DEBUG,Node: verifyMenu: unknown -> 56999999444214441: verifyMenu: Reading Digit #: 2)
[Sep  8 10:43:20]     -- AGI Script Executing Application: (LOG) Options: (DEBUG,Node: verifyMenu: unknown -> 56999999444214441: waitDigit(5000))
[Sep  8 10:43:20] DTMF[23980]: channel.c:4151 __ast_read: DTMF begin '5' received on SIP/A1General-000002d5
[Sep  8 10:43:20] DTMF[23980]: channel.c:4155 __ast_read: DTMF begin ignored '5' on SIP/A1General-000002d5
[Sep  8 10:43:21] DTMF[23980]: channel.c:4066 __ast_read: DTMF end '5' received on SIP/A1General-000002d5, duration 140 ms
[Sep  8 10:43:21] DTMF[23980]: channel.c:4135 __ast_read: DTMF end passthrough '5' on SIP/A1General-000002d5
[Sep  8 10:43:21]     -- AGI Script Executing Application: (LOG) Options: (DEBUG,Node: verifyMenu: unknown -> 56999999444214441: verifyMenu: Reading Digit #: 3)
[Sep  8 10:43:21]     -- AGI Script Executing Application: (LOG) Options: (DEBUG,Node: verifyMenu: unknown -> 56999999444214441: waitDigit(5000))
[Sep  8 10:43:21] DTMF[23980]: channel.c:4151 __ast_read: DTMF begin '7' received on SIP/A1General-000002d5
[Sep  8 10:43:21] DTMF[23980]: channel.c:4155 __ast_read: DTMF begin ignored '7' on SIP/A1General-000002d5
[Sep  8 10:43:21] DTMF[23980]: channel.c:4066 __ast_read: DTMF end '7' received on SIP/A1General-000002d5, duration 160 ms
[Sep  8 10:43:21] DTMF[23980]: channel.c:4135 __ast_read: DTMF end passthrough '7' on SIP/A1General-000002d5
[Sep  8 10:43:21]     -- AGI Script Executing Application: (LOG) Options: (DEBUG,Node: verifyMenu: unknown -> 56999999444214441: verifyMenu: Reading Digit #: 4)
[Sep  8 10:43:21]     -- AGI Script Executing Application: (LOG) Options: (DEBUG,Node: verifyMenu: unknown -> 56999999444214441: waitDigit(5000))
[Sep  8 10:43:21] DTMF[23980]: channel.c:4151 __ast_read: DTMF begin '8' received on SIP/A1General-000002d5
[Sep  8 10:43:21] DTMF[23980]: channel.c:4155 __ast_read: DTMF begin ignored '8' on SIP/A1General-000002d5
[Sep  8 10:43:21] DTMF[23980]: channel.c:4066 __ast_read: DTMF end '8' received on SIP/A1General-000002d5, duration 160 ms
[Sep  8 10:43:21] DTMF[23980]: channel.c:4135 __ast_read: DTMF end passthrough '8' on SIP/A1General-000002d5
[Sep  8 10:43:21]     -- AGI Script Executing Application: (LOG) Options: (DEBUG,Node: verifyMenu: unknown -> 56999999444214441: verifyMenu: Reading Digit #: 5)
[Sep  8 10:43:21]     -- AGI Script Executing Application: (LOG) Options: (DEBUG,Node: verifyMenu: unknown -> 56999999444214441: waitDigit(5000))
[Sep  8 10:43:26]     -- AGI Script Executing Application: (LOG) Options: (DEBUG,Node: verifyMenu: unknown -> 56999999444214441: Expired available time per digit)
[Sep  8 10:43:26]     -- AGI Script Executing Application: (LOG) Options: (DEBUG,Node: verifyMenu: unknown -> 56999999444214441: Input validated)
[Sep  8 10:43:26]     -- AGI Script Executing Application: (LOG) Options: (DEBUG,Node: verifyMenu: unknown -> 56999999444214441: [ Node: verifyMenu input: (4578)  state: (timeout)])
[Sep  8 10:43:26]     -- AGI Script Executing Application: (LOG) Options: (NOTICE,Shutdown)
[Sep  8 10:43:26] NOTICE[23980]: Ext. 56999999444214441:1 @ default: Shutdown
[Sep  8 10:43:26]     -- <SIP/A1General-000002d5>AGI Script /home/callcentrepagi/agi_entry.php completed, returning 0

Incoming audio

Hi there,

sorry for a newbie question, but is it possible to read incoming audio of a call using this library?

call spool

with scheduling support (touch(file))

[BUG] Error when trying to instantiate MockedClientImpl

When trying to write a test using the MockedClientImpl, when I try to pass an instance of MockedClientImpl into my mocked class under test, I get an error:
ErrorException : The behavior of unparenthesized expressions containing both '.' and '+'/'-' will change in PHP 8: '+'/'-' will take a higher precedence
The exception is being thrown on line 127 of PAGI/Client/Impl/MockedClientImpl.php, which would indicate it's trying to make a call assertion, but it is only being instantiated, no assertions were specified.

Here's my test code for reference

$agiClient = new MockedClientImpl();
$callLog = new Call();
$mockHandler = Mockery::mock(CallHandler::class, [$agiClient, $callLog])->makePartial();
$mockHandler->shouldAllowMockingProtectedMethods();

White Space

agi vars had white space. Added trim to readEnvironmentVariable on line 722 in AbstractClient.php. trim($value)

Multi CDR records per call

In Asterisk 13.18.5 there is a Call Detail Record for each ringing phone. Is there a way to access each record via PAGI?

Bug in readEnvironmentVariable.

There is a bug in master branch, the space at the beginning of the value is not removed.
It looks like the bug happened at refactoring, check screenshot i switched to 2.* for now.

ss

Ability to execute a Node only when a precondition is met

Execute Node conditionally

NOTE BEFORE DISCUSSION: I am willing to implement this myself.

However, before starting implementation, I would like to know if this idea has any change of being merged eventually. As you probably found out in earlier discussions, I'm a strong proponent of sending patches upstream.

Furthermore, I would like some feedback on the architectural side of it.

The issue

Currently, Nodes have an executeBeforeRun callback, but this one (rightly) seems to be unable to influence the actual running of the Node.

What we need in our applications is a way to perform checks before a Node is ran, and act upon the result of the check. Examples of checks to be performed are:

  • The date and time. Some of our actions are specific to date and time, requiring different approaches based on the current time. The canonical example of this would of course be the business hours check.
  • Current application state (in a broad sense, not necessarily specific to Nodes or the NodeController). For example, for some of our customers we need to forward their call to another phone number, wait for them to return and then perform a specific action.
  • Existence of a specific Caller ID (either for the inbound or the outbound leg)

The main issue is that we have IVR scripts which should perform different actions based on a state that has nothing to do like the state of the current call, like user input. We could of course solve this with adding if-then-else blocks all over the place and completely passing over the NodeController (and possible Nodes themselves). However, adding this- relatively simple, but very powerful - functionality to NodeController would give us the ability to perform some 90% of our requirements within the PAGI framework.

Furthermore, I think it would be a nice addition to PAGI itself, which is the primary reason I actually added this issue at all. If I was convinced that we were the only ones with such requirements, I would not have brought it up.

Requirements

  • Add one or more precondition(s) to check before executing a Node (example: specific application state, date/time range, caller ID, etc)
  • Only execute Node when precondition(s) are met
  • Jump to a different Node when precondition fails
  • Optional: have multiple results for a precondition and act upon them individually

Suggested implementation

The most logical place to add a precondition seems to be in the NodeController, since that is where the overall logic is being handled.

The idea is to add the ability to add the precondition to the NodeController together with the Node. Then, when jump()ing to a Node, before executing it, the precondition should be checked.

When the precondition fails, the NodeController checks whether the precondition has conditional actions defined based upon the status of the preconditions. These actions should be Nodes themselves.

Of course, when the precondition is met, it's business as usual, i.e. the Node is ran.

Wrong arguments numbering [BUG]

Hello.
I run script like this:
AGI(agi-scripts/createCall/run.php,HI,THERE,WITH,ARGUMENTS)

In exmaples I found this way to get arguments:

$variables = $pagiClient->getChannelVariables();

for ($i = 0; $i < $variables->getTotalArguments(); $i++) {
    $pagiClient->getAsteriskLogger()->notice(' -- Argument ' . intval($i) . ': ' . $variables->getArgument($i));
}

but in this case argument with index 0 is always empty and last argument is always absent.

TEMPORARY FIX:

I have to start from "index = 1 to <= total" counts in for loop.

Using just node->expectAtLeast() does not wait a single moment for input

Hi,

I experience a behavior I am not sure if it is a bug or intention.

If I configure a node to expect at least 3 digits and the prompts are played the node seems to switch to state TIMEOUT immediately (playing playOnNoInput)() without waiting a single moment for the user to input something (because the user e.g. listens carefully till the end of the prompt).

I tried several combinations to avoid this behavior (commented code of the example below). In the end if I set at least 3 digits and at most 19 digits the node waits after the prompts for user input. But what if I have the case that I don't know the length of the input?

The same behavior I experience with only expectAtMost() configured and using non of the 3 expectXXXX() methods used. Only using the expectExactly() works like I would expect it.

Any suggestions? Below is the configuration of the node.

$node->saySound($x1);
$node->playOnNoInput($x2);
$node->playOnMaxValidInputAttempts($x3);

$node->maxAttemptsForInput(3);
$node->maxTimeBetweenDigits(7000);
//$node->maxTotalTimeForInput(5000);
$node->expectAtLeast(3);
//$node->expectAtMost(19);

$node->validateInputWith(  
   'standardValidation',  
    function(Node $node) {  
        return preg_match('/^[0-9]{3,19}$/', $node->getInput()) == 1;
    },  
    $sound);

xml application

it would make easier to create new ivr applications, by interpreting an xml

PSR-3 Logging support

Hi Marcelo,

Now PSR-3 logging is merged in PAMI, I can work on PSR-3 support here in PAGI as said in the original PR on PAMI. Are you still interested in that?

Since I have carried patches for that for a long time in my own branch, it is probably not that much work to extract them and create a PR for it.

Please let me know.

Thanks, Jacob

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.