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,122 @@
<?php
namespace MauticPlugin\MauticSocialBundle\Helper;
use Mautic\AssetBundle\Helper\TokenHelper as AssetTokenHelper;
use Mautic\LeadBundle\Entity\Lead;
use Mautic\LeadBundle\Helper\TokenHelper;
use Mautic\PageBundle\Entity\Trackable;
use Mautic\PageBundle\Helper\TokenHelper as PageTokenHelper;
use Mautic\PageBundle\Model\TrackableModel;
use Mautic\PluginBundle\Helper\IntegrationHelper;
use MauticPlugin\MauticSocialBundle\Model\TweetModel;
class CampaignEventHelper
{
/**
* @var array
*/
protected $clickthrough = [];
public function __construct(
protected IntegrationHelper $integrationHelper,
protected TrackableModel $trackableModel,
protected PageTokenHelper $pageTokenHelper,
protected AssetTokenHelper $assetTokenHelper,
protected TweetModel $tweetModel,
) {
}
/**
* @return array|false
*/
public function sendTweetAction(Lead $lead, array $event)
{
$tweetSent = false;
$tweetEntity = $this->tweetModel->getEntity($event['channelId']);
if (!$tweetEntity) {
return ['failed' => 1, 'response' => 'Tweet entity '.$event['channelId'].' not found'];
}
/** @var \MauticPlugin\MauticSocialBundle\Integration\TwitterIntegration $twitterIntegration */
$twitterIntegration = $this->integrationHelper->getIntegrationObject('Twitter');
// Setup clickthrough for URLs in tweet
$this->clickthrough = [
'source' => ['campaign', $event['campaign']['id']],
];
$leadArray = $lead->getProfileFields();
if (empty($leadArray['twitter'])) {
return false;
}
$tweetText = $tweetEntity->getText();
$tweetText = $this->parseTweetText($tweetText, $leadArray, $tweetEntity->getId());
$tweetUrl = $twitterIntegration->getApiUrl('statuses/update');
$status = ['status' => $tweetText];
// fire the tweet
$sendResponse = $twitterIntegration->makeRequest($tweetUrl, $status, 'POST', ['append_callback' => false]);
// verify the tweet was sent by checking for a tweet id
if (is_array($sendResponse) && array_key_exists('id_str', $sendResponse)) {
$tweetSent = true;
}
if ($tweetSent) {
$this->tweetModel->registerSend($tweetEntity, $lead, $sendResponse, 'campaign.event', $event['id']);
return ['timeline' => $tweetText, 'response' => $sendResponse];
}
$response = ['failed' => 1, 'response' => $sendResponse];
if (!empty($sendResponse['error']['message'])) {
$response['reason'] = $sendResponse['error']['message'];
}
return $response;
}
/**
* PreParse the twitter message and replace placeholders with values.
*
* @param string $text
* @param array $lead
* @param int $channelId
*
* @return string|string[]
*/
protected function parseTweetText($text, $lead, $channelId = -1): array|string
{
$tweetHandle = $lead['twitter'];
$tokens = [
'{twitter_handle}' => (str_contains($tweetHandle, '@')) ? $tweetHandle : "@$tweetHandle",
];
$tokens = array_merge(
$tokens,
TokenHelper::findLeadTokens($text, $lead),
$this->pageTokenHelper->findPageTokens($text, $this->clickthrough),
$this->assetTokenHelper->findAssetTokens($text, $this->clickthrough)
);
[$text, $trackables] = $this->trackableModel->parseContentForTrackables(
$text,
$tokens,
'social_twitter',
$channelId
);
/**
* @var string $token
* @var Trackable $trackable
*/
foreach ($trackables as $token => $trackable) {
$tokens[$token] = $this->trackableModel->generateTrackableUrl($trackable, array_merge($this->clickthrough, ['lead' => $lead['id']]));
}
return str_replace(array_keys($tokens), array_values($tokens), $text);
}
}

View File

@@ -0,0 +1,366 @@
<?php
namespace MauticPlugin\MauticSocialBundle\Helper;
use Doctrine\ORM\EntityManagerInterface;
use Mautic\CoreBundle\Helper\CoreParametersHelper;
use Mautic\CoreBundle\Translation\Translator;
use Mautic\LeadBundle\Entity\Lead;
use Mautic\LeadBundle\Model\FieldModel;
use Mautic\LeadBundle\Model\LeadModel;
use MauticPlugin\MauticSocialBundle\Entity\Monitoring;
use MauticPlugin\MauticSocialBundle\Exception\ExitMonitorException;
use MauticPlugin\MauticSocialBundle\Model\MonitoringModel;
use MauticPlugin\MauticSocialBundle\Model\PostCountModel;
use Symfony\Component\Console\Output\OutputInterface;
class TwitterCommandHelper
{
private ?OutputInterface $output = null;
private int $updatedLeads = 0;
private int $newLeads = 0;
private array $manipulatedLeads = [];
/**
* @var string
*/
private $twitterHandleField;
public function __construct(
private LeadModel $leadModel,
private FieldModel $fieldModel,
private MonitoringModel $monitoringModel,
private PostCountModel $postCountModel,
private Translator $translator,
private EntityManagerInterface $em,
CoreParametersHelper $coreParametersHelper,
) {
$this->translator->setLocale($coreParametersHelper->get('locale', 'en_US'));
$this->twitterHandleField = $coreParametersHelper->get('twitter_handle_field', 'twitter');
}
public function getNewLeadsCount(): int
{
return $this->newLeads;
}
public function getUpdatedLeadsCount(): int
{
return $this->updatedLeads;
}
/**
* @return array
*/
public function getManipulatedLeads()
{
return $this->manipulatedLeads;
}
public function setOutput(OutputInterface $output): void
{
$this->output = $output;
}
/**
* @param string $message
* @param bool $newLine
*/
private function output($message, $newLine = true): void
{
if ($newLine) {
$this->output->writeln($message);
} else {
$this->output->write($message);
}
}
/**
* Processes a list of tweets and creates / updates leads in Mautic.
*
* @param array $statusList
* @param Monitoring $monitor
*/
public function createLeadsFromStatuses($statusList, $monitor): int
{
$leadField = $this->fieldModel->getRepository()->findOneBy(['alias' => $this->twitterHandleField]);
if (!$leadField) {
// Field has been deleted or something
$this->output($this->translator->trans('mautic.social.monitoring.twitter.field.not.found'));
return 0;
}
$handleFieldGroup = $leadField->getGroup();
// Just a means to let any LeadEvents listeners know that many leads are likely coming in case that matters to their logic
defined('MASS_LEADS_MANIPULATION') or define('MASS_LEADS_MANIPULATION', 1);
defined('SOCIAL_MONITOR_IMPORT') or define('SOCIAL_MONITOR_IMPORT', 1);
// Get a list of existing leads to tone down on queries
$usersByHandles = [];
$usersByName = ['firstnames' => [], 'lastnames' => []];
$expr = $this->leadModel->getRepository()->createQueryBuilder('f')->expr();
$monitorProperties = $monitor->getProperties();
if (!array_key_exists('checknames', $monitorProperties)) {
$monitorProperties['checknames'] = 0;
}
foreach ($statusList as $i => $status) {
// If we don't have a screen_name, the rest is irrelevant. Remove from further processing
if (empty($status['user']['screen_name'])) {
unset($statusList[$i]);
continue;
}
$usersByHandles[] = $expr->literal($status['user']['screen_name']);
// Split the twitter user's name into its parts if we're matching to contacts by name
if ($monitorProperties['checknames'] && $status['user']['name'] && str_contains($status['user']['name'], ' ')) {
[$firstName, $lastName] = $this->splitName($status['user']['name']);
if (!empty($firstName) && !empty($lastName)) {
$usersByName['firstnames'][] = $expr->literal($firstName);
$usersByName['lastnames'][] = $expr->literal($lastName);
}
unset($firstName, $lastName);
}
}
unset($expr);
if (!empty($usersByHandles)) {
$leads = $this->leadModel->getRepository()->getEntities(
[
'filter' => [
'force' => [
[
'column' => 'l.'.$this->twitterHandleField,
'expr' => 'in',
'value' => $usersByHandles,
],
],
],
]
);
// Key by twitter handle
$twitterLeads = [];
foreach ($leads as $lead) {
$fields = $lead->getFields();
$twitterHandle = strtolower($fields[$handleFieldGroup][$this->twitterHandleField]['value']);
$twitterLeads[$twitterHandle] = $lead;
}
unset($leads);
}
if ($monitorProperties['checknames']) {
// Fetch existing contacts who have an unknown twitter
// handle in Mautic but are found during monitoring.
$leadsByName = $this->leadModel->getRepository()->getEntities(
[
'filter' => [
'force' => [
[
'column' => 'l.firstname',
'expr' => 'in',
'value' => $usersByName['firstnames'],
],
[
'column' => 'l.lastname',
'expr' => 'in',
'value' => $usersByName['lastnames'],
],
[
'column' => 'l.'.$this->twitterHandleField,
'expr' => 'isNull',
],
],
],
]
);
// key by name
$namedLeads = [];
/** @var Lead $lead */
foreach ($leadsByName as $lead) {
$firstName = $lead->getFirstname();
$lastName = $lead->getLastname();
$namedLeads[$firstName.' '.$lastName] = $lead;
}
unset($leadsByName, $firstName, $lastName);
}
$processedLeads = [];
foreach ($statusList as $status) {
$handle = strtolower($status['user']['screen_name']);
/* @var \Mautic\LeadBundle\Entity\Lead $leadEntity */
if (!isset($processedLeads[$handle])) {
$processedLeads[$handle] = 1;
$lastActive = new \DateTime($status['created_at']);
if (isset($namedLeads[$status['user']['name']])) {
++$this->updatedLeads;
$isNew = false;
$leadEntity = $namedLeads[$status['user']['name']];
$fields = [
$this->twitterHandleField => $handle,
];
$this->leadModel->setFieldValues($leadEntity, $fields, false);
$this->output('Updating existing lead ID #'.$leadEntity->getId().' ('.$handle.'). Matched by first and last names.');
} elseif (isset($twitterLeads[$handle])) {
++$this->updatedLeads;
$isNew = false;
$leadEntity = $twitterLeads[$handle];
$this->output('Updating existing lead ID #'.$leadEntity->getId().' ('.$handle.')');
} else {
++$this->newLeads;
$this->output('Creating new lead');
$isNew = true;
$leadEntity = new Lead();
$leadEntity->setNewlyCreated(true);
[$firstName, $lastName] = $this->splitName($status['user']['name']);
// build new lead fields
$fields = [
$this->twitterHandleField => $handle,
'firstname' => $firstName,
'lastname' => $lastName,
'country' => $status['user']['location'],
];
$this->leadModel->setFieldValues($leadEntity, $fields, false);
// mark as identified just to be sure
$leadEntity->setDateIdentified(new \DateTime());
}
$leadEntity->setPreferredProfileImage('Twitter');
// save the lead now
$leadEntity->setLastActive($lastActive->format('Y-m-d H:i:s'));
try {
// save the lead entity
$this->leadModel->saveEntity($leadEntity);
// Note lead ids
$this->manipulatedLeads[$leadEntity->getId()] = 1;
// add lead entity to the lead list
$this->leadModel->addToLists($leadEntity, $monitor->getLists());
if ($isNew) {
$this->setMonitorLeadStat($monitor, $leadEntity);
}
} catch (ExitMonitorException $e) {
$this->output($e->getMessage());
return 0;
} catch (\Exception $e) {
$this->output($e->getMessage());
continue;
}
}
// Increment the post count
$this->incrementPostCount($monitor, $status);
}
unset($processedLeads);
return 1;
}
/**
* Set the monitor's stat record with the metadata.
*
* @param array $searchMeta
*/
public function setMonitorStats(Monitoring $monitor, $searchMeta): void
{
$monitor->setStats($searchMeta);
$this->monitoringModel->saveEntity($monitor);
}
/**
* Get monitor record entity.
*
* @param int $mid
*/
public function getMonitor($mid): ?Monitoring
{
return $this->monitoringModel->getEntity($mid);
}
/**
* handles splitting a string handle into first / last name based on a space.
*
* @param string $name Space separated first & last name. Supports multiple first names
*
* @return array{0: string, 1?: string}
*/
private function splitName(string $name): array
{
// array the entire name
$nameParts = explode(' ', $name);
// last part of the array is our last
$lastName = array_pop($nameParts);
// push the rest of the name into first name
$firstName = implode(' ', $nameParts);
return [$firstName, $lastName];
}
/**
* Add new monitoring_leads record to track leads found via the search.
*
* @param Monitoring $monitor
* @param Lead $lead
*/
private function setMonitorLeadStat($monitor, $lead): void
{
// track the lead in our monitor_leads table
$monitorLead = new \MauticPlugin\MauticSocialBundle\Entity\Lead();
$monitorLead->setMonitor($monitor);
$monitorLead->setLead($lead);
$monitorLead->setDateAdded(new \DateTime());
/* @var \MauticPlugin\MauticSocialBundle\Entity\LeadRepository $monitorRepository */
$monitorRepository = $this->em->getRepository(\MauticPlugin\MauticSocialBundle\Entity\Lead::class);
$monitorRepository->saveEntity($monitorLead);
}
/**
* Increment the post counter.
*
* @param Monitoring $monitor
*/
private function incrementPostCount($monitor, $tweet): void
{
$date = new \DateTime($tweet['created_at']);
$this->postCountModel->updatePostCount($monitor, $date);
}
}