Initial commit: CloudOps infrastructure platform
This commit is contained in:
@@ -0,0 +1,145 @@
|
||||
<?php
|
||||
|
||||
namespace MauticPlugin\MauticSocialBundle\Command;
|
||||
|
||||
use MauticPlugin\MauticSocialBundle\Entity\MonitoringRepository;
|
||||
use MauticPlugin\MauticSocialBundle\Model\MonitoringModel;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\ArrayInput;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
#[AsCommand(
|
||||
name: 'mautic:social:monitoring',
|
||||
description: 'Looks at the records of monitors and iterates through them.'
|
||||
)]
|
||||
class MauticSocialMonitoringCommand extends Command
|
||||
{
|
||||
public function __construct(
|
||||
private MonitoringModel $monitoringModel,
|
||||
) {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->addOption('mid', 'i', InputOption::VALUE_OPTIONAL, 'The id of a specific monitor record to process')
|
||||
->addOption(
|
||||
'batch-size',
|
||||
null,
|
||||
InputOption::VALUE_REQUIRED,
|
||||
'The maximum number of iterations the cron runs per cycle. This value gets distributed by the number of monitor records published'
|
||||
)
|
||||
->addOption('query-count', null, InputOption::VALUE_OPTIONAL, 'The number of records to search for per iteration. Default is 100.', 100);
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
// get the mid from the cli
|
||||
$batchSize = $input->getOption('batch-size');
|
||||
|
||||
// monitor record
|
||||
$monitorId = $input->getOption('mid');
|
||||
$monitorList = $this->getMonitors($monitorId);
|
||||
|
||||
// no mid found, quit now
|
||||
if (!$monitorList->count()) {
|
||||
$output->writeln('No published monitors found. Make sure the id you supplied is published');
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
|
||||
if (!is_numeric($batchSize)) {
|
||||
$output->writeln('batch-size is not number.');
|
||||
|
||||
return self::FAILURE;
|
||||
}
|
||||
|
||||
// max iterations
|
||||
$maxPerIterations = ceil((int) $batchSize / count($monitorList));
|
||||
|
||||
foreach ($monitorList as $monitor) {
|
||||
$output->writeln('Executing Monitor Item '.$monitor->getId());
|
||||
$resultCode = $this->processMonitorListItem($monitor, $maxPerIterations, $input, $output);
|
||||
$output->writeln('Result Code: '.$resultCode);
|
||||
}
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Doctrine\ORM\Tools\Pagination\Paginator
|
||||
*/
|
||||
protected function getMonitors($id = null)
|
||||
{
|
||||
$filter = [
|
||||
'start' => 0,
|
||||
'limit' => 100,
|
||||
];
|
||||
|
||||
/** @var MonitoringRepository $repository */
|
||||
$repository = $this->monitoringModel->getRepository();
|
||||
|
||||
if (null !== $id) {
|
||||
$filter['filter'] = [
|
||||
'force' => [
|
||||
[
|
||||
'column' => $repository->getTableAlias().'.id',
|
||||
'expr' => 'eq',
|
||||
'value' => (int) $id,
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
return $repository->getPublishedEntities($filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool|int
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function processMonitorListItem($listItem, float $maxPerIterations, InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
// @todo set this up to use the command type per-monitor record.
|
||||
$networkType = $listItem->getNetworkType();
|
||||
|
||||
$commandName = '';
|
||||
|
||||
// hashtag command
|
||||
if ('twitter_hashtag' == $networkType) {
|
||||
$commandName = 'social:monitor:twitter:hashtags';
|
||||
}
|
||||
|
||||
// mention command
|
||||
if ('twitter_handle' == $networkType) {
|
||||
$commandName = 'social:monitor:twitter:mentions';
|
||||
}
|
||||
|
||||
if ('' == $commandName) {
|
||||
$output->writeln('Matching command not found.');
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
// monitor hash command
|
||||
$command = $this->getApplication()->find($commandName);
|
||||
|
||||
// create command options
|
||||
$cliArgs = [
|
||||
'command' => $commandName,
|
||||
'--mid' => $listItem->getId(),
|
||||
'--max-runs' => $maxPerIterations,
|
||||
'--query-count' => $input->getOption('query-count'),
|
||||
];
|
||||
|
||||
// execute the command
|
||||
$returnCode = $command->run(new ArrayInput($cliArgs), $output);
|
||||
|
||||
return $returnCode;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,300 @@
|
||||
<?php
|
||||
|
||||
namespace MauticPlugin\MauticSocialBundle\Command;
|
||||
|
||||
use Mautic\CoreBundle\Helper\CoreParametersHelper;
|
||||
use Mautic\CoreBundle\Translation\Translator;
|
||||
use Mautic\PluginBundle\Helper\IntegrationHelper;
|
||||
use MauticPlugin\MauticSocialBundle\Entity\Monitoring;
|
||||
use MauticPlugin\MauticSocialBundle\Event\SocialMonitorEvent;
|
||||
use MauticPlugin\MauticSocialBundle\Helper\TwitterCommandHelper;
|
||||
use MauticPlugin\MauticSocialBundle\Integration\TwitterIntegration;
|
||||
use MauticPlugin\MauticSocialBundle\SocialEvents;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
|
||||
abstract class MonitorTwitterBaseCommand extends Command
|
||||
{
|
||||
/**
|
||||
* @var TwitterIntegration
|
||||
*/
|
||||
protected $twitter;
|
||||
|
||||
/**
|
||||
* @var InputInterface
|
||||
*/
|
||||
protected $input;
|
||||
|
||||
/**
|
||||
* @var OutputInterface
|
||||
*/
|
||||
protected $output;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $maxRuns = 5;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $runCount = 0;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $queryCount = 100;
|
||||
|
||||
public function __construct(
|
||||
protected EventDispatcherInterface $dispatcher,
|
||||
protected Translator $translator,
|
||||
protected IntegrationHelper $integrationHelper,
|
||||
private TwitterCommandHelper $twitterCommandHelper,
|
||||
CoreParametersHelper $coreParametersHelper,
|
||||
) {
|
||||
$this->translator->setLocale($coreParametersHelper->get('locale', 'en_US'));
|
||||
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Command configuration. Set the name, description, and options here.
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->addOption(
|
||||
'mid',
|
||||
'i',
|
||||
InputOption::VALUE_REQUIRED,
|
||||
'The id of the monitor record'
|
||||
)
|
||||
->addOption(
|
||||
'max-runs',
|
||||
null,
|
||||
InputOption::VALUE_REQUIRED,
|
||||
'The maximum number of recursive iterations permitted',
|
||||
5
|
||||
)
|
||||
->addOption(
|
||||
'query-count',
|
||||
null,
|
||||
InputOption::VALUE_OPTIONAL,
|
||||
'The number of records to search for per iteration.',
|
||||
100
|
||||
)
|
||||
->addOption(
|
||||
'show-posts',
|
||||
null,
|
||||
InputOption::VALUE_NONE,
|
||||
'Use this option to display the posts retrieved'
|
||||
)
|
||||
->addOption(
|
||||
'show-stats',
|
||||
null,
|
||||
InputOption::VALUE_NONE,
|
||||
'Use this option to display the stats of the tweets fetched'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used in various areas to set name of the network being searched.
|
||||
*
|
||||
* @return string twitter|facebook etc..
|
||||
*/
|
||||
abstract public function getNetworkName();
|
||||
|
||||
/**
|
||||
* Search for tweets by creating your own search criteria.
|
||||
*
|
||||
* @param Monitoring $monitor
|
||||
*
|
||||
* @return array The results of makeRequest
|
||||
*/
|
||||
abstract protected function getTweets($monitor);
|
||||
|
||||
/**
|
||||
* Main execution method. Gets the integration settings, processes the search criteria.
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$this->input = $input;
|
||||
$this->output = $output;
|
||||
$this->maxRuns = $this->input->getOption('max-runs');
|
||||
$this->queryCount = $this->input->getOption('query-count');
|
||||
$twitterIntegration = $this->integrationHelper->getIntegrationObject('Twitter');
|
||||
|
||||
if (false === $twitterIntegration || false === $twitterIntegration->getIntegrationSettings()->getIsPublished()) {
|
||||
$this->output->writeln($this->translator->trans('mautic.social.monitoring.twitter.not.published'));
|
||||
|
||||
return Command::FAILURE;
|
||||
}
|
||||
|
||||
\assert($twitterIntegration instanceof TwitterIntegration);
|
||||
$this->twitter = $twitterIntegration;
|
||||
|
||||
if (!$this->twitter->isAuthorized()) {
|
||||
$this->output->writeln($this->translator->trans('mautic.social.monitoring.twitter.not.configured'));
|
||||
|
||||
return Command::FAILURE;
|
||||
}
|
||||
|
||||
// get the mid from the cli
|
||||
$mid = (int) $input->getOption('mid');
|
||||
|
||||
if (!$mid) {
|
||||
$this->output->writeln($this->translator->trans('mautic.social.monitoring.twitter.mid.empty'));
|
||||
|
||||
return Command::FAILURE;
|
||||
}
|
||||
|
||||
$this->twitterCommandHelper->setOutput($output);
|
||||
|
||||
$monitor = $this->twitterCommandHelper->getMonitor($mid);
|
||||
|
||||
if (!$monitor || !$monitor->getId()) {
|
||||
$this->output->writeln($this->translator->trans('mautic.social.monitoring.twitter.monitor.does.not.exist', ['%id%' => $mid]));
|
||||
|
||||
return Command::FAILURE;
|
||||
}
|
||||
|
||||
// process the monitor
|
||||
$this->processMonitor($monitor);
|
||||
|
||||
$this->dispatcher->dispatch(
|
||||
new SocialMonitorEvent($this->getNetworkName(), $monitor, $this->twitterCommandHelper->getManipulatedLeads(), $this->twitterCommandHelper->getNewLeadsCount(), $this->twitterCommandHelper->getUpdatedLeadsCount()),
|
||||
SocialEvents::MONITOR_POST_PROCESS
|
||||
);
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the monitor record.
|
||||
*
|
||||
* @Note: Keeping this method here instead of in the twitterCommandHelper
|
||||
* so that the hashtag and mention commands can easily extend it.
|
||||
*
|
||||
* @param Monitoring $monitor
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function processMonitor($monitor)
|
||||
{
|
||||
$results = $this->getTweets($monitor);
|
||||
|
||||
if (false === $results || !isset($results['statuses'])) {
|
||||
$this->output->writeln('No statuses found');
|
||||
|
||||
if (!empty($results['errors'])) {
|
||||
foreach ($results['errors'] as $error) {
|
||||
$this->output->writeln($error['code'].': '.$error['message']);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (count($results['statuses'])) {
|
||||
$this->twitterCommandHelper->createLeadsFromStatuses($results['statuses'], $monitor);
|
||||
} else {
|
||||
$this->output->writeln($this->translator->trans('mautic.social.monitoring.twitter.no.new.tweets'));
|
||||
}
|
||||
|
||||
$this->twitterCommandHelper->setMonitorStats($monitor, $results['search_metadata']);
|
||||
$this->printInformation($monitor, $results);
|
||||
|
||||
// get stats after being updated
|
||||
$stats = $monitor->getStats();
|
||||
|
||||
++$this->runCount;
|
||||
|
||||
// if we have stats and a next results request, process it here
|
||||
// @todo add a check for max iterations
|
||||
if (is_array($stats) && array_key_exists('max_id_str', $stats)
|
||||
&& ($this->runCount < $this->maxRuns)
|
||||
&& count($results['statuses'])
|
||||
) {
|
||||
// recursive
|
||||
$this->processMonitor($monitor);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints all the returned tweets.
|
||||
*
|
||||
* @param array $statuses
|
||||
*/
|
||||
protected function printTweets($statuses)
|
||||
{
|
||||
if (!$this->input->getOption('show-posts') && $this->output->getVerbosity() < OutputInterface::VERBOSITY_VERY_VERBOSE) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($statuses as $status) {
|
||||
$this->output->writeln('-- tweet -- ');
|
||||
$this->output->writeln('ID: '.$status['id']);
|
||||
$this->output->writeln('Message: '.$status['text']);
|
||||
$this->output->writeln('Handle: '.$status['user']['screen_name']);
|
||||
$this->output->writeln('Name: '.$status['user']['name']);
|
||||
$this->output->writeln('Location: '.$status['user']['location']);
|
||||
$this->output->writeln('Profile Img: '.$status['user']['profile_image_url']);
|
||||
$this->output->writeln('Profile Description: '.$status['user']['description']);
|
||||
$this->output->writeln('// tweet // ');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints the search query metadata from twitter.
|
||||
* Only shows stats if explicitly requested or if we're in verbose mode.
|
||||
*
|
||||
* @param array $metadata
|
||||
*/
|
||||
protected function printQueryMetadata($metadata)
|
||||
{
|
||||
if (!$this->input->getOption('show-stats') && $this->output->getVerbosity() < OutputInterface::VERBOSITY_VERBOSE) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->output->writeln('-- search meta -- ');
|
||||
$this->output->writeln('max_id_str: '.$metadata['max_id_str']);
|
||||
$this->output->writeln('since_id_str: '.$metadata['since_id_str']);
|
||||
$this->output->writeln('Page Count: '.$metadata['count']);
|
||||
$this->output->writeln('query: '.$metadata['query']);
|
||||
|
||||
if (array_key_exists('next_results', $metadata)) {
|
||||
$this->output->writeln('next results: '.$metadata['next_results']);
|
||||
}
|
||||
|
||||
$this->output->writeln('// search meta // ');
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints a summary of the search query.
|
||||
* Only shows stats if explicitly requested or if we're in verbose mode.
|
||||
*
|
||||
* @param Monitoring $monitor
|
||||
* @param array $results
|
||||
*/
|
||||
protected function printInformation($monitor, $results)
|
||||
{
|
||||
if (!$this->input->getOption('show-stats') && $this->output->getVerbosity() < OutputInterface::VERBOSITY_VERBOSE) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->output->writeln('------------------------');
|
||||
$this->output->writeln($monitor->getTitle());
|
||||
$this->output->writeln('Published '.$monitor->isPublished());
|
||||
$this->output->writeln($monitor->getNetworkType());
|
||||
$this->output->writeln('New Leads '.$this->twitterCommandHelper->getNewLeadsCount());
|
||||
$this->output->writeln('Updated Leads '.$this->twitterCommandHelper->getUpdatedLeadsCount());
|
||||
$this->printQueryMetadata($results['search_metadata']);
|
||||
$this->printTweets($results['statuses']);
|
||||
$this->output->writeln('------------------------');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace MauticPlugin\MauticSocialBundle\Command;
|
||||
|
||||
use MauticPlugin\MauticSocialBundle\Entity\Monitoring;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
|
||||
#[AsCommand(
|
||||
name: 'social:monitor:twitter:hashtags',
|
||||
description: 'Looks at our monitoring records and finds hashtags'
|
||||
)]
|
||||
class MonitorTwitterHashtagsCommand extends MonitorTwitterBaseCommand
|
||||
{
|
||||
/**
|
||||
* Search for tweets by hashtag.
|
||||
*
|
||||
* @param Monitoring $monitor
|
||||
*
|
||||
* @return bool|array False if missing the hashtag, otherwise the array response from Twitter
|
||||
*/
|
||||
protected function getTweets($monitor)
|
||||
{
|
||||
$params = $monitor->getProperties();
|
||||
$stats = $monitor->getStats();
|
||||
|
||||
if (!array_key_exists('hashtag', $params)) {
|
||||
$this->output->writeln('No hashtag was found!');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$searchUrl = $this->twitter->getApiUrl('search/tweets');
|
||||
$requestQuery = [
|
||||
'q' => '#'.$params['hashtag'],
|
||||
'count' => $this->queryCount,
|
||||
];
|
||||
|
||||
// if we have a max id string use it here
|
||||
if (is_array($stats) && array_key_exists('max_id_str', $stats) && $stats['max_id_str']) {
|
||||
$requestQuery['since_id'] = $stats['max_id_str'];
|
||||
}
|
||||
|
||||
return $this->twitter->makeRequest($searchUrl, $requestQuery);
|
||||
}
|
||||
|
||||
public function getNetworkName(): string
|
||||
{
|
||||
return 'twitter';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace MauticPlugin\MauticSocialBundle\Command;
|
||||
|
||||
use MauticPlugin\MauticSocialBundle\Entity\Monitoring;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
|
||||
#[AsCommand(
|
||||
name: 'social:monitor:twitter:mentions',
|
||||
description: 'Searches for mentioned tweets'
|
||||
)]
|
||||
class MonitorTwitterMentionsCommand extends MonitorTwitterBaseCommand
|
||||
{
|
||||
/**
|
||||
* Search for tweets by mention.
|
||||
*
|
||||
* @param Monitoring $monitor
|
||||
*
|
||||
* @return bool|array False if missing the twitter handle, otherwise the array response from Twitter
|
||||
*/
|
||||
protected function getTweets($monitor)
|
||||
{
|
||||
$params = $monitor->getProperties();
|
||||
$stats = $monitor->getStats();
|
||||
|
||||
if (!array_key_exists('handle', $params)) {
|
||||
$this->output->writeln('No twitter handle was found!');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$mentionsUrl = $this->twitter->getApiUrl('search/tweets');
|
||||
$requestQuery = [
|
||||
'q' => '@'.$params['handle'],
|
||||
'count' => $this->queryCount,
|
||||
];
|
||||
|
||||
// if we have a max id string use it here
|
||||
if (is_array($stats) && array_key_exists('max_id_str', $stats) && $stats['max_id_str']) {
|
||||
$requestQuery['since_id'] = $stats['max_id_str'];
|
||||
}
|
||||
|
||||
return $this->twitter->makeRequest($mentionsUrl, $requestQuery);
|
||||
}
|
||||
|
||||
public function getNetworkName(): string
|
||||
{
|
||||
return 'twitter';
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user