367 lines
12 KiB
PHP
Executable File
367 lines
12 KiB
PHP
Executable File
<?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);
|
|
}
|
|
}
|