Skip to content

Commit

Permalink
Merge pull request #7449 from mautic-inc/feature.sms-replies-in-campa…
Browse files Browse the repository at this point in the history
…igns

Add support for sms replies as a campaign decision
  • Loading branch information
dennisameling committed Jan 28, 2020
2 parents 1c2dcbd + d3e7d54 commit 9d595c8
Show file tree
Hide file tree
Showing 36 changed files with 1,973 additions and 204 deletions.
61 changes: 57 additions & 4 deletions app/bundles/CoreBundle/Helper/PhoneNumberHelper.php
Expand Up @@ -17,10 +17,8 @@
class PhoneNumberHelper
{
/**
* Format a phone number.
*
* @param string|int $number
* @param int $format
* @param $number
* @param int $format
*
* @return string
*/
Expand All @@ -31,4 +29,59 @@ public function format($number, $format = PhoneNumberFormat::E164)

return $phoneUtil->format($phoneNumber, $format);
}

/**
* @param $number
*
* @return array
*/
public function getFormattedNumberList($number)
{
return array_unique(
[
$number,
$this->format($number, PhoneNumberFormat::E164),
$this->formatNumericalNational($number),
$this->format($number, PhoneNumberFormat::NATIONAL),
$this->formatDelimitedNational($number),
$this->format($number, PhoneNumberFormat::INTERNATIONAL),
$this->formatNumericalInternational($number),
$this->formatDelimitedNational($number, '.'),
]
);
}

/**
* @param $number
*
* @return string
*/
public function formatNumericalInternational($number)
{
return preg_replace('/[^0-9]/', '', $this->format($number, PhoneNumberFormat::INTERNATIONAL));
}

/**
* @param $number
*
* @return string
*/
public function formatNumericalNational($number)
{
return preg_replace('/[^0-9]/', '', $this->format($number, PhoneNumberFormat::NATIONAL));
}

/**
* @param string $number
* @param string $delimiter
*
* @return string
*/
public function formatDelimitedNational($number, $delimiter = '-')
{
$national = $this->format($number, PhoneNumberFormat::NATIONAL);
$national = str_replace([') ', '-'], $delimiter, $national);

return preg_replace('/[^0-9'.$delimiter.']/', '', $national);
}
}
32 changes: 21 additions & 11 deletions app/bundles/LeadBundle/EventListener/TimelineEventLogTrait.php
Expand Up @@ -36,7 +36,7 @@ trait TimelineEventLogTrait
* @param null $object
* @param null $action
*/
private function addEvents(LeadTimelineEvent $event, $eventType, $eventTypeName, $icon, $bundle = null, $object = null, $action = null)
private function addEvents(LeadTimelineEvent $event, $eventType, $eventTypeName, $icon, $bundle = null, $object = null, $action = null, $contentTemplate = null)
{
$eventTypeName = $this->translator->trans($eventTypeName);
$event->addEventType($eventType, $eventTypeName);
Expand All @@ -57,7 +57,7 @@ private function addEvents(LeadTimelineEvent $event, $eventType, $eventTypeName,
// Add the logs to the event array
foreach ($events['results'] as $log) {
$event->addEvent(
$this->getEventEntry($log, $eventType, $eventTypeName, $icon)
$this->getEventEntry($log, $eventType, $eventTypeName, $icon, $contentTemplate)
);
}
}
Expand All @@ -67,20 +67,30 @@ private function addEvents(LeadTimelineEvent $event, $eventType, $eventTypeName,
* @param $eventType
* @param $eventTypeName
* @param $icon
* @param $contentTemplate
*
* @return array
*/
private function getEventEntry(array $log, $eventType, $eventTypeName, $icon)
private function getEventEntry(array $log, $eventType, $eventTypeName, $icon, $contentTemplate)
{
return [
'event' => $eventType,
'eventId' => $eventType.$log['id'],
'eventType' => $eventTypeName,
'eventLabel' => $this->getSourceName($log, $eventType),
'timestamp' => $log['date_added'],
'icon' => $icon,
'contactId' => $log['lead_id'],
$properties = json_decode($log['properties'], true);

$entry = [
'event' => $eventType,
'eventId' => $eventType.$log['id'],
'eventType' => $eventTypeName,
'eventLabel' => $this->getSourceName($log, $eventType),
'timestamp' => $log['date_added'],
'icon' => $icon,
'contactId' => $log['lead_id'],
'extra' => $properties,
];

if ($contentTemplate) {
$entry['contentTemplate'] = $contentTemplate;
}

return $entry;
}

/**
Expand Down
8 changes: 7 additions & 1 deletion app/bundles/SmsBundle/Api/AbstractSmsApi.php
Expand Up @@ -15,8 +15,14 @@
use Mautic\CoreBundle\Factory\MauticFactory;
use Mautic\LeadBundle\Entity\Lead;
use Mautic\PageBundle\Model\TrackableModel;
use Mautic\SmsBundle\Sms\TransportInterface;

abstract class AbstractSmsApi
/**
* Class AbstractSmsApi.
*
* @deprecated use TransportInterface instead
*/
abstract class AbstractSmsApi implements TransportInterface
{
/**
* @var MauticFactory
Expand Down
113 changes: 8 additions & 105 deletions app/bundles/SmsBundle/Api/TwilioApi.php
@@ -1,7 +1,7 @@
<?php

/*
* @copyright 2016 Mautic Contributors. All rights reserved
* @copyright 2019 Mautic Contributors. All rights reserved
* @author Mautic
*
* @link http://mautic.org
Expand All @@ -11,110 +11,13 @@

namespace Mautic\SmsBundle\Api;

use libphonenumber\NumberParseException;
use libphonenumber\PhoneNumberFormat;
use libphonenumber\PhoneNumberUtil;
use Mautic\CoreBundle\Helper\PhoneNumberHelper;
use Mautic\LeadBundle\Entity\Lead;
use Mautic\PageBundle\Model\TrackableModel;
use Mautic\PluginBundle\Helper\IntegrationHelper;
use Monolog\Logger;
use Mautic\SmsBundle\Integration\Twilio\TwilioTransport;

class TwilioApi extends AbstractSmsApi
/**
* Class TwilioApi.
*
* @deprecated Use \Mautic\SmsBundle\Integration\Twilio\TwilioTransport instead
*/
class TwilioApi extends TwilioTransport
{
/**
* @var \Services_Twilio
*/
protected $client;

/**
* @var Logger
*/
protected $logger;

/**
* @var string
*/
protected $sendingPhoneNumber;

/**
* TwilioApi constructor.
*
* @param TrackableModel $pageTrackableModel
* @param PhoneNumberHelper $phoneNumberHelper
* @param IntegrationHelper $integrationHelper
* @param Logger $logger
*/
public function __construct(TrackableModel $pageTrackableModel, PhoneNumberHelper $phoneNumberHelper, IntegrationHelper $integrationHelper, Logger $logger)
{
$this->logger = $logger;

$integration = $integrationHelper->getIntegrationObject('Twilio');

if ($integration && $integration->getIntegrationSettings()->getIsPublished()) {
$this->sendingPhoneNumber = $integration->getIntegrationSettings()->getFeatureSettings()['sending_phone_number'];

$keys = $integration->getDecryptedApiKeys();

if (isset($keys['username']) && isset($keys['password'])) {
$this->client = new \Services_Twilio($keys['username'], $keys['password']);
}
}

parent::__construct($pageTrackableModel);
}

/**
* @param $number
*
* @return string
*
* @throws NumberParseException
*/
protected function sanitizeNumber($number)
{
$util = PhoneNumberUtil::getInstance();
$parsed = $util->parse($number, 'US');

return $util->format($parsed, PhoneNumberFormat::E164);
}

/**
* @param Lead $lead
* @param string $content
*
* @return bool|string
*/
public function sendSms(Lead $lead, $content)
{
$number = $lead->getLeadPhoneNumber();

if ($number === null) {
return false;
}

try {
$this->client->account->messages->sendMessage(
$this->sendingPhoneNumber,
$this->sanitizeNumber($number),
$content
);

return true;
} catch (\Services_Twilio_RestException $e) {
$this->logger->addWarning(
$e->getMessage(),
['exception' => $e]
);

return $e->getMessage();
} catch (NumberParseException $e) {
$this->logger->addWarning(
$e->getMessage(),
['exception' => $e]
);

return $e->getMessage();
}
}
}
53 changes: 53 additions & 0 deletions app/bundles/SmsBundle/Callback/CallbackInterface.php
@@ -0,0 +1,53 @@
<?php

/*
* @copyright 2018 Mautic Inc. All rights reserved
* @author Mautic, Inc.
*
* @link https://www.mautic.com
*
* @license GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
*/

namespace Mautic\SmsBundle\Callback;

use Doctrine\Common\Collections\ArrayCollection;
use Mautic\SmsBundle\Exception\NumberNotFoundException;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

interface CallbackInterface
{
/**
* Returns a "transport" string to match the URL path /sms/{transport}/callback.
*
* @return string
*/
public function getTransportName();

/**
* Return all contacts that match whatever identifiers the service provides (likely number).
*
* @param Request $request
*
* @return ArrayCollection
*
* @throws NumberNotFoundException
* @throws BadRequestHttpException
* @throws NotFoundHttpException
*/
public function getContacts(Request $request);

/**
* Extract the message in the reply from the request.
*
* @param Request $request
*
* @return string
*
* @throws BadRequestHttpException
* @throws NotFoundHttpException
*/
public function getMessage(Request $request);
}
46 changes: 46 additions & 0 deletions app/bundles/SmsBundle/Callback/HandlerContainer.php
@@ -0,0 +1,46 @@
<?php

/*
* @copyright 2018 Mautic Inc. All rights reserved
* @author Mautic, Inc.
*
* @link https://www.mautic.com
*
* @license GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
*/

namespace Mautic\SmsBundle\Callback;

use Mautic\SmsBundle\Exception\CallbackHandlerNotFound;

class HandlerContainer
{
/**
* @var CallbackInterface[]
*/
private $handlers;

/**
* @param CallbackInterface $handler
*/
public function registerHandler(CallbackInterface $handler)
{
$this->handlers[$handler->getTransportName()] = $handler;
}

/**
* @param $transportName
*
* @return CallbackInterface
*
* @throws CallbackHandlerNotFound
*/
public function getHandler($transportName)
{
if (!isset($this->handlers[$transportName])) {
throw new CallbackHandlerNotFound("$transportName has not been registered");
}

return $this->handlers[$transportName];
}
}
22 changes: 22 additions & 0 deletions app/bundles/SmsBundle/Callback/ResponseInterface.php
@@ -0,0 +1,22 @@
<?php

/*
* @copyright 2018 Mautic Inc. All rights reserved
* @author Mautic, Inc.
*
* @link https://www.mautic.com
*
* @license GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
*/

namespace Mautic\SmsBundle\Callback;

use Symfony\Component\HttpFoundation\Response;

interface ResponseInterface
{
/**
* @return Response
*/
public function getResponse();
}

0 comments on commit 9d595c8

Please sign in to comment.