Spaces:
No application file
No application file
| 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{string, string} | |
| */ | |
| private function splitName($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); | |
| } | |
| } | |