Initial commit: CloudOps infrastructure platform
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user