Skip to content
Snippets Groups Projects
Commit 1fd80e87 authored by lambic's avatar lambic Committed by Shashank Kumar
Browse files

Issue #3232126: Add voice and phone number management to 8.x version

parent 4038e42a
No related branches found
No related tags found
2 merge requests!14Twilio 3359047,!1Issue #3232126: Add voice and phone number management to 8.x version
File added
......@@ -10,6 +10,19 @@ use Drupal\Component\Utility\SafeMarkup;
*/
class TwilioController extends ControllerBase {
/**
* Handle incoming status updates.
*/
public function receiveStatus() {
if (!empty($_REQUEST['CallSid'])) {
$this->moduleHandler()->invokeAll('twilio_status', $_REQUEST);
}
return [
'#type' => 'markup',
'#markup' => $this->t('Twilio status'),
];
}
/**
* Handle incoming SMS message requests.
*
......@@ -29,7 +42,7 @@ class TwilioController extends ControllerBase {
$number = SafeMarkup::checkPlain(str_replace('+' . $dial_code, '', $_REQUEST['From']));
$number_twilio = !empty($_REQUEST['To']) ? SafeMarkup::checkPlain(str_replace('+', '', $_REQUEST['To'])) : '';
$message = SafeMarkup::checkPlain(htmlspecialchars_decode($_REQUEST['Body'], ENT_QUOTES));
// @todo: Support more than one media entry.
// @todo Support more than one media entry.
$media = !empty($_REQUEST['MediaUrl0']) ? $_REQUEST['MediaUrl0'] : '';
$options = [];
// Add the receiver to the options array.
......@@ -76,16 +89,17 @@ class TwilioController extends ControllerBase {
* @param array $options
* Options array.
*/
public function messageIncoming($number, $number_twilio, $message, array $media = array(), array $options = array()) {
public function messageIncoming($number, $number_twilio, $message, array $media = [], array $options = []) {
// Build our SMS array to be used by our hook and rules event.
$sms = array(
$sms = [
'number' => $number,
'number_twilio' => $number_twilio,
'message' => $message,
'media' => $media,
);
];
// Invoke a hook for the incoming message so other modules can do things.
$this->moduleHandler()->invokeAll('twilio_message_incoming', [$sms, $options]);
$params = [$sms, $options];
$this->moduleHandler()->invokeAll('twilio_message_incoming', $params);
if ($this->moduleHandler()->moduleExists('rules')) {
rules_invoke_event('twilio_message_incoming', $sms);
}
......@@ -101,13 +115,14 @@ class TwilioController extends ControllerBase {
* @param array $options
* Options array.
*/
public function voiceIncoming($number, $number_twilio, array $options = array()) {
$voice = array(
public function voiceIncoming($number, $number_twilio, array $options = []) {
$voice = [
'number' => $number,
'number_twilio' => $number_twilio,
);
];
// Invoke a hook for the incoming message so other modules can do things.
$this->moduleHandler()->invokeAll('twilio_voice_incoming', [$voice, $options]);
$params = [$voice, $options];
$this->moduleHandler()->invokeAll('twilio_voice_incoming', $params);
if ($this->moduleHandler()->moduleExists('rules')) {
rules_invoke_event('twilio_voice_incoming', $voice);
}
......@@ -123,7 +138,7 @@ class TwilioController extends ControllerBase {
* Associative array of country calling codes and country names.
*/
public static function countryDialCodes($all = FALSE) {
$codes = array(
$codes = [
1 => "USA / Canada / Dominican Rep. / Puerto Rico (1)",
93 => "Afghanistan (93)",
355 => "Albania (355)",
......@@ -333,7 +348,7 @@ class TwilioController extends ControllerBase {
260 => "Zambia (260)",
255 => "Zanzibar (255)",
263 => "Zimbabwe (263)",
);
];
if ($all === TRUE) {
return $codes;
}
......@@ -359,7 +374,7 @@ class TwilioController extends ControllerBase {
* OR the full array of ISO to dial codes.
*/
public static function countryIsoToDialCodes(string $iso = NULL) {
$codes = array(
$codes = [
'US' => 1,
'CA' => 1,
'DO' => 1,
......@@ -564,7 +579,7 @@ class TwilioController extends ControllerBase {
'YE' => 967,
'ZM' => 260,
'ZW' => 263,
);
];
if ($iso) {
return $codes[$iso];
}
......
......@@ -2,6 +2,9 @@
namespace Drupal\twilio\Form;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Form\ConfigFormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Element;
......@@ -12,6 +15,36 @@ use Drupal\twilio\Controller\TwilioController;
*/
class TwilioAdminForm extends ConfigFormBase {
/**
* The module handler service.
*
* @var \Drupal\Core\Extension\ModuleHandlerInterface
*/
protected $moduleHandler;
/**
* Constructs a \Drupal\system\ConfigFormBase object.
*
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
* The factory for configuration objects.
* @param \Drupal\Core\Extension\ModuleHandlerInterface $moduleHandler
* The module handler service.
*/
final public function __construct(ConfigFactoryInterface $config_factory, ModuleHandlerInterface $moduleHandler) {
parent::__construct($config_factory);
$this->moduleHandler = $moduleHandler;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('config.factory'),
$container->get('module_handler')
);
}
/**
* {@inheritdoc}
*/
......@@ -48,26 +81,44 @@ class TwilioAdminForm extends ConfigFormBase {
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$form['account'] = [
'#type' => 'textfield',
'#required' => TRUE,
'#title' => $this->t('Twilio Account SID'),
'#default_value' => $this->config('twilio.settings')->get('account'),
'#description' => $this->t('Enter your Twilio account id'),
];
$form['token'] = [
'#type' => 'textfield',
'#required' => TRUE,
'#title' => $this->t('Twilio Auth Token'),
'#default_value' => $this->config('twilio.settings')->get('token'),
'#description' => $this->t('Enter your Twilio token id'),
];
$config = $this->config('twilio.settings');
$key_exists = $this->moduleHandler->moduleExists('key');
if ($key_exists) {
$form['account'] = [
'#type' => 'key_select',
'#required' => TRUE,
'#default_value' => $config->get('account'),
'#title' => $this->t('Twilio Account SID'),
];
$form['token'] = [
'#type' => 'key_select',
'#required' => TRUE,
'#default_value' => $config->get('token'),
'#title' => $this->t('Twilio authentication token'),
];
}
else {
$form['account'] = [
'#type' => 'textfield',
'#required' => TRUE,
'#title' => $this->t('Twilio Account SID'),
'#default_value' => $config->get('account'),
'#description' => $this->t('Enter your Twilio account id'),
];
$form['token'] = [
'#type' => 'textfield',
'#required' => TRUE,
'#title' => $this->t('Twilio Auth Token'),
'#default_value' => $config->get('token'),
'#description' => $this->t('Enter your Twilio token id'),
];
}
$form['number'] = [
'#type' => 'textfield',
'#required' => TRUE,
'#title' => $this->t('Twilio Phone Number'),
'#default_value' => $this->config('twilio.settings')->get('number'),
'#default_value' => $config->get('number'),
'#description' => $this->t('Enter your Twilio phone number'),
];
$form['long_sms'] = [
......@@ -78,7 +129,7 @@ class TwilioAdminForm extends ConfigFormBase {
$this->t('Send multiple messages'),
$this->t('Truncate message to 160 characters'),
],
'#default_value' => $this->config('twilio.settings')->get('long_sms'),
'#default_value' => $config->get('long_sms'),
];
$form['registration_form'] = [
'#type' => 'radios',
......@@ -89,7 +140,7 @@ class TwilioAdminForm extends ConfigFormBase {
$this->t('Optional'),
$this->t('Required'),
],
'#default_value' => $this->config('twilio.settings')->get('registration_form'),
'#default_value' => $config->get('registration_form'),
];
$form['twilio_country_codes_container'] = [
......@@ -100,10 +151,12 @@ class TwilioAdminForm extends ConfigFormBase {
'#collapsible' => TRUE,
'#collapsed' => TRUE,
];
$container = $config->get('twilio_country_codes_container') ?? [];
$default = $container['country_codes'] ?? [];
$form['twilio_country_codes_container']['country_codes'] = [
'#type' => 'checkboxes',
'#options' => TwilioController::countryDialCodes(TRUE),
'#default_value' => $this->config('twilio.settings')->get('twilio_country_codes_container')['country_codes'],
'#default_value' => $default,
];
// Expose the callback URLs to the user for convenience.
$form['twilio_callback_container'] = [
......@@ -115,6 +168,7 @@ class TwilioAdminForm extends ConfigFormBase {
// Initialize URL variables.
$voice_callback = $GLOBALS['base_url'] . '/twilio/voice';
$sms_callback = $GLOBALS['base_url'] . '/twilio/sms';
$status_callback = $GLOBALS['base_url'] . '/twilio/status';
$form['twilio_callback_container']['voice_callback'] = [
'#type' => 'item',
......@@ -128,6 +182,12 @@ class TwilioAdminForm extends ConfigFormBase {
'#markup' => '<p>' . $sms_callback . '</p>',
];
$form['twilio_callback_container']['status_callback'] = [
'#type' => 'item',
'#title' => $this->t('Status callback URL'),
'#markup' => '<p>' . $status_callback . '</p>',
];
return parent::buildForm($form, $form_state);
}
......
......@@ -7,7 +7,7 @@ use Drupal\Core\Form\FormStateInterface;
use Drupal\Component\Utility\UrlHelper;
use Drupal\twilio\Controller\TwilioController;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\twilio\Services\Command;
use Drupal\twilio\Services\Sms;
/**
* Form to send test SMS messages.
......@@ -15,25 +15,25 @@ use Drupal\twilio\Services\Command;
class TwilioAdminTestForm extends FormBase {
/**
* Injected Twilio service Command class.
* Injected Twilio service Sms class.
*
* @var Command
* @var \Drupal\twilio\Services\Sms
*/
private $command;
private $sms;
/**
* {@inheritdoc}
*/
public function __construct(Command $command) {
$this->command = $command;
final public function __construct(Sms $sms) {
$this->sms = $sms;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
$command = $container->get('twilio.command');
return new static($command);
$sms = $container->get('twilio.sms');
return new static($sms);
}
/**
......@@ -99,9 +99,9 @@ class TwilioAdminTestForm extends FormBase {
if ($mediaUrl = $form_state->getValue('mediaUrl')) {
$message['mediaUrl'] = $mediaUrl;
}
$number='+'.$form_state->getValue(['country']).$form_state->getValue(['number']);
$number = '+' . $form_state->getValue(['country']) . $form_state->getValue(['number']);
$this->command->messageSend($number, $message);
$this->sms->messageSend($number, $message);
$this->messenger()->addStatus($this->t('Attempted to send SMS message. Check your receiving device.'));
}
......
......@@ -14,8 +14,7 @@ class RouteSubscriber extends RouteSubscriberBase {
* {@inheritdoc}
*/
public function alterRoutes(RouteCollection $collection) {
// @TODO
// Parts of your hook_menu_alter() logic should be moved in here.
// @todo Parts of your hook_menu_alter() logic should be moved in here.
}
}
<?php
namespace Drupal\twilio\Services;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Cache\CacheFactoryInterface;
/**
* Service class for Twilio phone number handling.
*/
class Numbers extends TwilioBase {
/**
* Numbers being handled in this instance.
*
* @var array
*/
private $numbers;
/**
* Cache bin.
*
* @var \Drupal\Core\Cache\CacheBackendInterface
*/
private $bin;
/**
* Initialize properties.
*/
public function __construct(ConfigFactoryInterface $config_factory, ModuleHandlerInterface $moduleHandler, CacheFactoryInterface $cacheFactory) {
parent::__construct($config_factory, $moduleHandler, $cacheFactory);
$this->bin = $this->cacheFactory->get('twilio');
}
/**
* Get all purchased numbers.
*/
public function getAllNumbers(): Numbers {
if ($cache = $this->bin->get('allnumbers')) {
$this->numbers = $cache->data;
return $this;
}
$this->numbers = $this->client()->incomingPhoneNumbers->read();
$this->bin->set('allnumbers', $this->numbers);
return $this;
}
/**
* Get purchased numbers by capability.
*
* @param string $capability
* The capability we want, can be voice, sms, mms or fax.
*/
public function getByCapability(string $capability): Numbers {
if ($cache = $this->bin->get($capability . 'numbers')) {
$this->numbers = $cache->data;
return $this;
}
$numbers = $this->client()->incomingPhoneNumbers->stream();
$this->numbers = [];
foreach ($numbers as $number) {
$capable = $number->capabilities[$capability] ?? FALSE;
if ($capable) {
$this->numbers[] = $number;
}
}
$this->bin->set($capability . 'numbers', $this->numbers);
return $this;
}
/**
* Get voice enabled numbers.
*/
public function getVoiceNumbers(): Numbers {
$this->getByCapability('voice');
return $this;
}
/**
* Get SMS enabled numbers.
*/
public function getSmsNumbers(): Numbers {
$this->getByCapability('sms');
return $this;
}
/**
* Get MMS enabled numbers.
*/
public function getMmsNumbers(): Numbers {
$this->getByCapability('mms');
return $this;
}
/**
* Get Fax enabled numbers.
*/
public function getFaxNumbers(): Numbers {
$this->getByCapability('fax');
return $this;
}
/**
* Filter numbers by country.
*
* This is necessary because the IncomingPhoneNumbers endpoint
* does not return locality information for purchased numbers.
*
* @param string $country
* ISO country code.
*/
public function filterByCountry(string $country) {
$closure = function ($n) use ($country) {
$lookup = $this->lookup($n->phoneNumber);
return $lookup->countryCode == $country;
};
$this->numbers = array_values(array_filter($this->numbers, $closure));
return $this;
}
/**
* Perform a lookup request for a single number.
*
* @param string $number
* The phone number in e.164 format.
*/
public function lookup($number) {
if ($cache = $this->bin->get($number)) {
return $cache->data;
}
$response = $this->client()->lookups->v1->phoneNumbers($number)->fetch();
$this->bin->set($number, $response);
return $response;
}
/**
* Magic method to get a string representation of the found numbers.
*/
public function __toString() {
$closure = function ($r, $n) {
$r .= $n->phoneNumber . "\n";
return $r;
};
return array_reduce($this->numbers, $closure, count($this->numbers) . " numbers: \n");
}
/**
* Magic method to get numbers in a palatable form.
*
* @param string $name
* The name of the parameter.
*/
public function __get(string $name) {
if ($name == 'numbers') {
$closure = function ($n) {
return [
'friendly_name' => $n->friendlyName,
'phone_number' => $n->phoneNumber,
'capabilities' => $n->capabilities,
];
};
return array_map($closure, $this->numbers);
}
}
/**
* Manual cache reset function.
*/
public function resetCache() {
$this->bin->deleteAll();
return $this;
}
}
<?php
namespace Drupal\twilio\Services;
use Twilio\Rest\Client;
use Twilio\Exceptions\TwilioException;
use Drupal\Component\Utility\UrlHelper;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Cache\CacheFactoryInterface;
/**
* Service class for Twilio API commands.
*/
class Sms extends TwilioBase {
/**
* Send an SMS message.
*
* @param string $number
* The number to send the message to.
* @param string|array $message
* Message text or an array:
* [
* from => number
* body => message string
* mediaUrl => absolute URL
* ].
*/
public function messageSend(string $number, $message) {
if (is_string($message)) {
$message = [
'from' => $this->number,
'body' => $message,
];
}
$message['from'] = !empty($message['from']) ? $message['from'] : $this->number;
if (empty($message['body'])) {
throw new TwilioException("Message requires a body.");
}
if (!empty($message['mediaUrl']) && !UrlHelper::isValid($message['mediaUrl'], TRUE)) {
throw new TwilioException("Media URL must be a valid, absolute URL.");
}
$client = new Client($this->sid, $this->token);
$client->messages->create($number, $message);
}
}
<?php
namespace Drupal\twilio\Services;
use Twilio\Rest\Client;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Cache\CacheFactoryInterface;
/**
* Service class for Twilio API commands.
*/
class TwilioBase {
/**
* Twilio account ID.
*
* @var string
*/
protected $sid;
/**
* Twilio auth token.
*
* @var string
*/
protected $token;
/**
* The config factory.
*
* @var \Drupal\Core\Config\ConfigFactoryInterface
*/
protected $configFactory;
/**
* The module handler service.
*
* @var \Drupal\Core\Extension\ModuleHandlerInterface
*/
protected $moduleHandler;
/**
* The cache factory service.
*
* @var \Drupal\Core\Cache\CacheFactoryInterface
*/
protected $cacheFactory;
/**
* Twilio client.
*
* @var \Twilio\Rest\Client
*/
protected $twilio;
/**
* Phone number.
*
* @var string
*/
protected $number;
/**
* Initialize properties.
*/
public function __construct(ConfigFactoryInterface $config_factory, ModuleHandlerInterface $moduleHandler, CacheFactoryInterface $cacheFactory) {
$this->configFactory = $config_factory;
$this->moduleHandler = $moduleHandler;
$this->cacheFactory = $cacheFactory;
$this->sid = $this->getSid();
$this->token = $this->getToken();
$this->number = $this->getNumber();
}
/**
* Get the Twilio client.
*/
protected function client() {
if (empty($this->twilio)) {
$this->twilio = new Client($this->sid, $this->token);
}
return $this->twilio;
}
/**
* Get config/key value.
*/
private function getConfig(string $key):string {
$value = $this->configFactory
->get('twilio.settings')
->get($key);
if ($value && $this->moduleHandler->moduleExists('key')) {
// @phpstan-ignore-next-line
$key = \Drupal::service('key.repository')->getKey($value);
if ($key && $key->getKeyValue()) {
$value = $key->getKeyValue();
}
}
return $value ?? '';
}
/**
* Get the Twilio Account SID.
*
* @return string
* The configured Twilio Account SID.
*/
public function getSid():string {
if (empty($this->sid)) {
$this->sid = $this->getConfig('account');
}
return $this->sid;
}
/**
* Get the Twilio Auth Token.
*
* @return string
* The configured Twilio Auth Token.
*/
public function getToken():string {
if (empty($this->token)) {
$this->token = $this->getConfig('token');
}
return $this->token;
}
/**
* Get the Twilio Number.
*
* @return string
* The configured Twilio Number.
*/
public function getNumber() {
if (empty($this->number)) {
$this->number = $this->configFactory
->get('twilio.settings')
->get('number');
}
return $this->number;
}
}
<?php
namespace Drupal\twilio\Services;
use Twilio\Exceptions\TwilioException;
use SimpleXMLElement;
use Drupal\Core\Url;
/**
* Service class for Twilio voice calling.
*/
class Voice extends TwilioBase {
/**
* Status callback url.
*
* @var string
*/
private $statusCallback;
/**
* Status callback method.
*
* @var string
*/
private $statusCallbackMethod = 'POST';
/**
* Status callback events.
*
* @var array
*/
private $statusCallbackEvent = [
'initiated',
'ringing',
'answered',
'completed',
];
/**
* Answering machine detection enabled.
*
* @var bool
*/
private $machineDetection = FALSE;
/**
* Async Answering machine detection.
*
* @var bool
*/
private $amd = TRUE;
/**
* TwiML script to use on the call.
*
* @var \SimpleXMLElement
*/
private $twiml;
/**
* URL to fetch twiml from to use on the call.
*
* @var string
*/
private $twimlUrl;
/**
* Set the status callback url.
*
* @param string $url|null
* The url twilio will call for status updates.
* @param string $method
* The HTTP method to use.
* @param array $events
* The events to track.
*/
public function setCallback(string $url = NULL, string $method = 'POST', array $events = []): Voice {
$url = $url ?? $GLOBALS['base_url'] . '/twilio/status';
if (filter_var($url, FILTER_VALIDATE_URL)) {
$this->statusCallback = $url;
$this->statusCallbackMethod = $method;
if (!empty($events)) {
$this->statusCallbackEvent = $events;
}
}
else {
throw new TwilioException('Invalid status callback url.');
}
return $this;
}
/**
* Turn on machine detection.
*/
public function enableMachineDetection($async = TRUE) {
$this->machineDetection = TRUE;
$this->amd = $async;
return $this;
}
/**
* Make a call.
*
* @param string $twilioNumber
* The twilio number to use to process the call.
* @param string $destNumber
* The number of the person we are calling.
*
* @return string
* The ID of the initiated call.
*/
public function dial(string $twilioNumber, string $destNumber) {
if ($this->twimlUrl) {
$params['url'] = $this->twimlUrl;
}
elseif (!empty($this->twiml)) {
$params['twiml'] = $this->twiml->asXML();
}
else {
throw new TwilioException('No twiml specified');
}
if (!$this->statusCallback) {
$this->statusCallback = $GLOBALS['base_url'] . '/twilio/status';
}
if ($this->machineDetection) {
$params['MachineDetection'] = 'Enable';
$params['AsyncAMD'] = $this->amd;
}
$params += [
'statusCallback' => $this->statusCallback,
'statusCallbackEvent' => $this->statusCallbackEvent,
'statusCallbackMethod' => $this->statusCallbackMethod,
];
$call = $this->client()->calls->create(
$destNumber,
$twilioNumber,
$params
);
return $call->sid;
}
/**
* Set the twiml xml to use on the call.
*
* @param string $script
* Either a url or a twiml script.
*/
public function setScript($script): Voice {
if (filter_var($script, FILTER_VALIDATE_URL)) {
$this->twimlUrl = $script;
}
else {
libxml_use_internal_errors(TRUE);
$xml = simplexml_load_string($script);
if ($xml !== FALSE) {
$this->twiml = $xml;
}
else {
throw new TwilioException('Invalid twiml XML.');
}
}
return $this;
}
/**
* Start script.
*/
public function startScript() {
if (empty($this->twiml)) {
$this->twiml = new SimpleXMLElement('<?xml version="1.0" encoding="UTF-8"?><Response></Response>');
}
}
/**
* Add a Say command to the script.
*
* @param string $message
* The thing to say.
* @param string $voice
* The robot voice to use.
* @param string $language
* The language the robot should speak in.
*/
public function addSay(string $message, string $voice = 'woman', string $language = 'en'): Voice {
$this->startScript();
$say = $this->twiml->addChild('Say', $message);
$say->addAttribute('voice', $voice);
$say->addAttribute('language', $language);
return $this;
}
/**
* Add a Play command to the script.
*
* @param string $url
* The url to the recorded message.
*/
public function addPlay(string $url): Voice {
$this->startScript();
$this->twiml->addChild('Play', $url);
return $this;
}
/**
* Add a beep.
*
* @param string $soundFileUrl
* The URL to access the soundfile (beep). Has to be public - reachable by
* Twilio. You may copy the sound/beep.mp3 in this module to a server you
* host and use the full URL when calling addBeep().
*/
public function addBeep(string $soundFileUrl): Voice {
$this->startScript();
$this->twiml->addChild('Play', $soundFileUrl);
return $this;
}
/**
* Add a Dial command to the script.
*
* @param string $number
* The phone number to dial.
*/
public function addDial(string $number): Voice {
$this->startScript();
$dial = $this->twiml->addChild('Dial');
$number = $dial->addChild('Number', $number);
if ($this->statusCallback) {
$number->addAttribute('statusCallback', $this->statusCallback);
$number->addAttribute('statusCallbackEvent', implode(' ', $this->statusCallbackEvent));
$number->addAttribute('statusCallbackMethod', $this->statusCallbackMethod);
}
return $this;
}
}
......@@ -17,10 +17,16 @@ twilio.receive_message:
defaults:
_controller: '\Drupal\twilio\Controller\TwilioController::receiveMessage'
requirements:
_access: 'true'
_access: 'TRUE'
twilio.receive_voice:
path: /twilio/voice
defaults:
_controller: '\Drupal\twilio\Controller\TwilioController::receiveVoice'
requirements:
_access: 'true'
_access: 'TRUE'
twilio.receive_status:
path: /twilio/status
defaults:
_controller: '\Drupal\twilio\Controller\TwilioController::receiveStatus'
requirements:
_access: 'TRUE'
services:
twilio.command:
class: Drupal\twilio\Services\Command
twilio.sms:
class: Drupal\twilio\Services\Sms
arguments:
- '@config.factory'
- '@module_handler'
- '@cache_factory'
tags:
- { name: 'twilio' }
twilio.numbers:
class: Drupal\twilio\Services\Numbers
arguments:
- '@config.factory'
- '@module_handler'
- '@cache_factory'
tags:
- { name: 'twilio' }
twilio.voice:
class: Drupal\twilio\Services\Voice
arguments:
- '@config.factory'
- '@module_handler'
- '@cache_factory'
tags:
- { name: 'twilio' }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment