Initial commit: CloudOps infrastructure platform

This commit is contained in:
root
2026-04-09 19:58:57 +02:00
commit 1166a52f26
7762 changed files with 839452 additions and 0 deletions

View File

@@ -0,0 +1,94 @@
<?php
namespace Mautic\SmsBundle\Integration\Twilio;
use Mautic\PluginBundle\Helper\IntegrationHelper;
use Twilio\Exceptions\ConfigurationException;
class Configuration
{
/**
* @var string
*/
private $messagingServiceSid;
/**
* @var string
*/
private $accountSid;
/**
* @var string
*/
private $authToken;
public function __construct(
private IntegrationHelper $integrationHelper,
) {
}
/**
* @return string
*
* @throws ConfigurationException
*/
public function getMessagingServiceSid()
{
$this->setConfiguration();
return $this->messagingServiceSid;
}
/**
* @return string
*
* @throws ConfigurationException
*/
public function getAccountSid()
{
$this->setConfiguration();
return $this->accountSid;
}
/**
* @return string
*
* @throws ConfigurationException
*/
public function getAuthToken()
{
$this->setConfiguration();
return $this->authToken;
}
/**
* @throws ConfigurationException
*/
private function setConfiguration(): void
{
if ($this->accountSid) {
return;
}
$integration = $this->integrationHelper->getIntegrationObject('Twilio');
if (!$integration || !$integration->getIntegrationSettings()->getIsPublished()) {
throw new ConfigurationException();
}
$this->messagingServiceSid = $integration->getIntegrationSettings()->getFeatureSettings()['messaging_service_sid'];
if (empty($this->messagingServiceSid)) {
throw new ConfigurationException();
}
$keys = $integration->getDecryptedApiKeys();
if (empty($keys['username']) || empty($keys['password'])) {
throw new ConfigurationException();
}
$this->accountSid = $keys['username'];
$this->authToken = $keys['password'];
}
}

View File

@@ -0,0 +1,75 @@
<?php
namespace Mautic\SmsBundle\Integration\Twilio;
use Mautic\SmsBundle\Callback\CallbackInterface;
use Mautic\SmsBundle\Exception\NumberNotFoundException;
use Mautic\SmsBundle\Helper\ContactHelper;
use Symfony\Component\HttpFoundation\InputBag;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Twilio\Exceptions\ConfigurationException;
class TwilioCallback implements CallbackInterface
{
public function __construct(
private ContactHelper $contactHelper,
private Configuration $configuration,
) {
}
public function getTransportName(): string
{
return 'twilio';
}
/**
* @throws NumberNotFoundException
*/
public function getContacts(Request $request): \Doctrine\Common\Collections\ArrayCollection
{
$this->validateRequest($request->request);
$number = $request->get('From');
return $this->contactHelper->findContactsByNumber($number);
}
public function getMessage(Request $request): string
{
$this->validateRequest($request->request);
return trim($request->get('Body'));
}
/**
* @param InputBag<bool|float|int|string> $request
*/
private function validateRequest(InputBag $request): void
{
try {
$accountSid = $this->configuration->getAccountSid();
} catch (ConfigurationException) {
// Not published or not configured
throw new NotFoundHttpException();
}
// Validate this is a request from Twilio
if ($accountSid !== $request->get('AccountSid')) {
throw new BadRequestHttpException();
}
// Who is the message from?
$number = $request->get('From');
if (empty($number)) {
throw new BadRequestHttpException();
}
// What did they say?
$message = trim($request->get('Body'));
if (empty($message)) {
throw new BadRequestHttpException();
}
}
}

View File

@@ -0,0 +1,114 @@
<?php
namespace Mautic\SmsBundle\Integration\Twilio;
use libphonenumber\NumberParseException;
use libphonenumber\PhoneNumberFormat;
use libphonenumber\PhoneNumberUtil;
use Mautic\LeadBundle\Entity\Lead;
use Mautic\SmsBundle\Sms\TransportInterface;
use Psr\Log\LoggerInterface;
use Twilio\Exceptions\ConfigurationException;
use Twilio\Exceptions\TwilioException;
use Twilio\Rest\Client;
class TwilioTransport implements TransportInterface
{
private ?Client $client = null;
public function __construct(
private Configuration $configuration,
private LoggerInterface $logger,
) {
}
/**
* @param string $content
*
* @return bool|string
*/
public function sendSms(Lead $lead, $content)
{
$number = $lead->getLeadPhoneNumber();
if (null === $number) {
return false;
}
try {
$messagingServiceSid = $this->configuration->getMessagingServiceSid();
$this->configureClient();
$this->client->messages->create(
$this->sanitizeNumber($number),
$this->createPayload($messagingServiceSid, $content)
);
return true;
} catch (NumberParseException $numberParseException) {
$this->logger->warning(
$numberParseException->getMessage(),
['exception' => $numberParseException]
);
return $numberParseException->getMessage();
} catch (ConfigurationException $configurationException) {
$message = $configurationException->getMessage() ?: 'mautic.sms.transport.twilio.not_configured';
$this->logger->warning(
$message,
['exception' => $configurationException]
);
return $message;
} catch (TwilioException $twilioException) {
$this->logger->warning(
$twilioException->getMessage(),
['exception' => $twilioException]
);
return $twilioException->getMessage();
}
}
/**
* @param string $number
*
* @return string
*
* @throws NumberParseException
*/
private function sanitizeNumber($number)
{
$util = PhoneNumberUtil::getInstance();
$parsed = $util->parse($number, 'US');
return $util->format($parsed, PhoneNumberFormat::E164);
}
/**
* @return mixed[]
*/
private function createPayload(string $messagingServiceSid, string $content): array
{
return [
'messagingServiceSid' => $messagingServiceSid,
'body' => $content,
];
}
/**
* @throws ConfigurationException
*/
private function configureClient(): void
{
if ($this->client) {
// Already configured
return;
}
$this->client = new Client(
$this->configuration->getAccountSid(),
$this->configuration->getAuthToken()
);
}
}

View File

@@ -0,0 +1,93 @@
<?php
namespace Mautic\SmsBundle\Integration;
use Mautic\PluginBundle\Integration\AbstractIntegration;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\NumberType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
class TwilioIntegration extends AbstractIntegration
{
protected bool $coreIntegration = true;
public function getName(): string
{
return 'Twilio';
}
public function getIcon(): string
{
return 'app/bundles/SmsBundle/Assets/img/Twilio.png';
}
public function getSecretKeys(): array
{
return ['password'];
}
/**
* @return array<string, string>
*/
public function getRequiredKeyFields(): array
{
return [
'username' => 'mautic.sms.config.form.sms.username',
'password' => 'mautic.sms.config.form.sms.password',
];
}
public function getAuthenticationType(): string
{
return 'none';
}
/**
* @param \Mautic\PluginBundle\Integration\Form|FormBuilder $builder
* @param array $data
* @param string $formArea
*/
public function appendToForm(&$builder, $data, $formArea): void
{
if ('features' == $formArea) {
$builder->add(
'messaging_service_sid',
TextType::class,
[
'label' => 'mautic.sms.config.form.sms.messaging_service_sid',
'label_attr' => ['class' => 'control-label'],
'required' => false,
'attr' => [
'class' => 'form-control',
'tooltip' => 'mautic.sms.config.form.sms.messaging_service_sid.tooltip',
],
]
);
$builder->add('frequency_number', NumberType::class,
[
'scale' => 0,
'label' => 'mautic.sms.list.frequency.number',
'label_attr' => ['class' => 'control-label'],
'required' => false,
'attr' => [
'class' => 'form-control frequency',
],
]);
$builder->add('frequency_time', ChoiceType::class,
[
'choices' => [
'day' => 'DAY',
'week' => 'WEEK',
'month' => 'MONTH',
],
'label' => 'mautic.lead.list.frequency.times',
'label_attr' => ['class' => 'control-label'],
'required' => false,
'multiple' => false,
'attr' => [
'class' => 'form-control frequency',
],
]);
}
}
}