/** * Checks and evaluates condition * @param string $condition condition string (ex: "$a >= 17") * @param array $variables set of variables to substitute * @return bool */ public function isConditionMet($condition, array $variables) { preg_match('/^\\s*(\\$[a-zA-Z\\_]+)\\s*([\\!\\<\\=\\>\\%]+)\\s*(.+)$/', $condition, $matches); if (4 !== count($matches)) { throw new \LogicException('Error parsing loop condition'); } $variableValue = (int) Ar::get($variables, $matches[1]); $result = (int) $matches[3]; switch ($matches[2]) { case '=': return $variableValue == $result; case '==': return $variableValue == $result; case '>': return $variableValue > $result; case '>=': return $variableValue >= $result; case '<': return $variableValue < $result; case '<=': return $variableValue <= $result; case '<>': return $variableValue != $result; case '!=': return $variableValue != $result; case '%': return ($variableValue + $result) % $result === 0; case '!%': return ($variableValue + $result) % $result !== 0; default: throw new \LogicException('Error parsing comparison operator'); } }
public function resolve($message) { preg_match_all('/[DGCU]{1}[0-9A-Z]{8}/', $message, $matches); if (!is_array(Ar::get($matches, 0)) || 0 === count($matches[0])) { return $message; } foreach ($matches[0] as $match) { $replace = null; switch ($match[0]) { case 'U': $replace = $this->slackFacade->getUserNameById($match); break; case 'C': $replace = $this->slackFacade->getChannelById($match)['name']; if (null !== $replace) { $replace = '#' . $replace; } break; case 'G': $replace = $this->slackFacade->getGroupById($match)['name']; break; case 'D': $replace = $this->slackFacade->getUserInfoByDmId($match)['name']; if (null !== $replace) { $replace = '@' . $replace; } break; } if (null !== $replace) { $message = str_replace($match, $replace, $message); } } return $message; }
public function send($type, $message, $placeholders = []) { if (count($placeholders) > 0) { $placeholders = array_merge([$message], $placeholders); $message = call_user_func_array('sprintf', $placeholders); } if (true === $this->shouldResolveNames && self::TYPE_RAW !== $type) { $message = $this->resolveNamesInMessage($message); } Ar::each($this->handlers, function ($handler) use($type, $message) { /** @var HandlerInterface $handler */ $handler->send($type, $message); }); }
/** * Returns array unnamed section bu its subkey * @param string $path * @param string $searchCriteria * @return array */ public function getSectionFromArray($path, $searchCriteria) { $data = $this->getEntry($path); if (!is_array($data)) { return []; } $searchCriteriaData = explode('=', $searchCriteria); $fieldName = Ar::get($searchCriteriaData, 0); $fieldValue = Ar::get($searchCriteriaData, 1); if (null === $fieldName || null === $fieldValue) { throw new \LogicException('Invalid config entry search criteria'); } foreach ($data as $item) { if (Ar::get($item, $fieldName) === $fieldValue) { return $item; } } return []; }
public function processCommand(array $args, $channel) { /** @var Container $container */ $container = Registry::get('container'); /** @var SlackFacade $slackFacade */ $slackFacade = $container['slack_facade']; $currentUserId = $slackFacade->getMyId(); $this->postMessage($channel, sprintf('I\'m `@%s` (ID `%s`), started %s', $slackFacade->getMyName(), $currentUserId, (new TimeAgoHelper())->format($container['started']))); $channels = Ar::map($slackFacade->getUserChannels($currentUserId), function ($channel) { return [Ar::get($channel, 'id') => ['name' => Ar::get($channel, 'name'), 'members' => count(Ar::get($channel, 'members'))]]; }); $groups = Ar::map($slackFacade->getUserGroups($currentUserId), function ($group) { return [Ar::get($group, 'id') => ['name' => Ar::get($group, 'name'), 'members' => count(Ar::get($group, 'members'))]]; }); $channelsOutput = (new Tabler())->setRenderer(new MysqlStyleRenderer())->setHeaders(['name' => 'Channel', 'members' => '# of members'])->setData($channels)->render(); $groupsOutput = (new Tabler())->setRenderer(new MysqlStyleRenderer())->setHeaders(['name' => 'Group', 'members' => '# of members'])->setData($groups)->render(); $this->postMessage($channel, sprintf('My channels (%d): ```%s```', count($channels), $channelsOutput)); $this->postMessage($channel, sprintf('My groups (%d): ```%s```', count($groups), $groupsOutput)); }
/** * @param RequestHandlerInterface $handler * @param RequestDto $dto * @return bool */ public function shouldExecute(RequestHandlerInterface $handler, RequestDto $dto) { $section = $this->config->getSection('custom'); if (empty($section)) { return true; } $dtoChannel = $dto->getChannel(); if (null === $dtoChannel) { return true; } $handlerId = $handler->getId(); $handlerConfig = $this->config->getEntry('custom.' . $handlerId); if (empty($handlerConfig)) { return false; } $dtoChannelType = $dtoChannel[0]; if ('D' === $dtoChannelType) { return Ar::get($handlerConfig, 'dm') ?: true; } if ('C' === $dtoChannelType) { $dtoChannelInfo = $this->slackApi->channelsInfo($dtoChannel); $dtoChannelName = '#' . Ar::get($dtoChannelInfo, 'channel.name'); } else { $dtoChannelInfo = $this->slackApi->groupsInfo($dtoChannel); $dtoChannelName = Ar::get($dtoChannelInfo, 'group.name'); } $defaultBehavior = $this->config->getEntry('custom.' . $handlerId . '.default'); $handlerChannels = $this->config->getEntry('custom.' . $handlerId . '.channels'); if (empty($handlerChannels)) { return 'allow' === $defaultBehavior ? true : false; } $handlerChannelConfig = $this->config->getSectionFromArray('custom.' . $handlerId . '.channels', 'name=' . $dtoChannelName); if (empty($handlerChannelConfig)) { return 'allow' === $defaultBehavior ? true : false; } $this->params = Ar::get($handlerChannelConfig, 'params') ?: []; return true; }
/** * @param InputInterface $input * @param OutputInterface $output * @return int */ protected function execute(InputInterface $input, OutputInterface $output) { $this->logPath = $input->getOption('log'); $cronInfoUrl = sprintf('http://%s:%d/info/cron/', $input->getOption('host'), $input->getOption('port')); $response = json_decode($this->curlRequest->getCurlResult($cronInfoUrl, [CURLOPT_TIMEOUT_MS => 100000])['body'], true); if (!is_array($response)) { $this->log('Core connection failed, exiting'); throw new \RuntimeException('Error connecting to core server'); } foreach ($response as $cronItem) { $this->cronExpression->setExpression(Ar::get($cronItem, 'time')); if ($this->cronExpression->isDue()) { $this->log('Executing: ' . Ar::get($cronItem, 'type')); switch (Ar::get($cronItem, 'type')) { case 'playbook': $url = sprintf('http://%s:%d/playbook/run/', $input->getOption('host'), $input->getOption('port')); $playbookFile = Ar::get($cronItem, 'playbook'); $playbook = $this->fileLoader->load($playbookFile); $this->curlRequest->getCurlResult($url, [CURLOPT_POST => true, CURLOPT_POSTFIELDS => ['playbook' => urlencode($playbook), 'filename' => $playbookFile], CURLOPT_TIMEOUT_MS => 100000]); $this->log('Executed playbook: ' . $playbookFile); break; case 'command': $url = sprintf('http://%s:%d/command/run/', $input->getOption('host'), $input->getOption('port')); $command = Ar::get($cronItem, 'command'); if (null === $command) { break; } $this->curlRequest->getCurlResult($url, [CURLOPT_POST => true, CURLOPT_POSTFIELDS => ['command' => urlencode($command)], CURLOPT_TIMEOUT_MS => 100000]); $this->log('Executed command: ' . $command); break; case 'curl': break; } } } }
protected function checkSlackConnection() { /** @var Container $container */ $container = Registry::get('container'); /** @var SlackApi $slackApi */ $slackApi = $container['slack_api']; $this->testResponse = $slackApi->apiTest(); return (bool) Ar::get($this->testResponse, 'ok'); }
public function get($key) { return Ar::get(self::$data, $key); }
public function getUserGroups($userId) { $groups = Ar::get($this->slackApi->groupsList(), 'groups'); return Ar::filter($groups, function ($group) use($userId) { return in_array($userId, Ar::get($group, 'members') ?: []); }); }
/** * Fires request to Slack server and returns response * @param string $method Slack API method to call * @param array $data request data * @return array * @throws \Exception */ private function processRequest($method, $data = []) { $methodsToFilter = ['api.test', 'rtm.start', 'chat.postMessage']; $method = $this->getApiMethodName($method); $url = self::BASE_URL . $method; $data['token'] = $this->token; if (true !== Ar::get($data, 'in_logger')) { Logger::get()->raw("➡️ %s", $url, json_encode($data)); } /** @var ApiCache $cache */ $cache = Registry::get('container')['api_cache']; $cachedResponse = $cache->get($url, $data); if (null !== $cachedResponse && !in_array($method, $methodsToFilter)) { if (true !== Ar::get($data, 'in_logger')) { Logger::get()->raw("⬅️ %s: cache hit", $url); } return $cachedResponse; } $result = $this->curlRequest->getCurlResult($url, [CURLOPT_POST => true, CURLOPT_POSTFIELDS => $data])['body']; if (true !== Ar::get($data, 'in_logger')) { Logger::get()->raw("⬅️ %s", $url, $result); } if (!in_array($method, $methodsToFilter)) { $cache->set($url, $data, json_decode($result, true)); } return json_decode($result, true); }
/** * Processes !-command * @param RequestDto $dto */ public function processCommand(RequestDto $dto) { $words = preg_split('/\\s+/is', $dto->getText()); $command = Ar::get($words, 0); if (null === $command || '!' !== substr($command, 0, 1)) { return; } $command = substr($command, 1); Logger::get()->info("id: %s, %s is executing command \"%s\" at %s", $dto->getId(), $dto->getUser(), $dto->getText(), $dto->getChannel()); /** @var BaseCommandHandler $commandHandler */ foreach ($this->commandHandlers as $commandHandler) { $commandName = $commandHandler->getName(); if (is_array($commandName)) { if (!in_array($command, $commandName)) { continue; } } else { if ($commandName !== $command) { continue; } } $allowed = $this->checkAccess($dto, $commandHandler); if (!$allowed) { $this->slackFacade->multiSendMessage([$dto->getChannel()], sprintf('SYSTEM: access to command !%s denied', $commandHandler->getName())); Logger::get()->warning("id: %s, access denied for %s trying to run \"%s\" at %s", $dto->getId(), $dto->getUser(), $dto->getText(), $dto->getChannel()); return; } unset($words[0]); if ($commandHandler->canProcessCommand($words, $dto->getChannel())) { /** @var SlackFacade $slackFacade */ $slackFacade = Registry::get('container')['slack_facade']; $commandHandler->setCallerId($dto->getUser()); $commandHandler->setCallerName($slackFacade->getUserNameById($dto->getUser())); Logger::get()->debug("id: %s, starting %s command handler", $dto->getId(), $commandHandler->getName()); $commandHandler->processCommand($words, $dto->getChannel()); Logger::get()->debug("id: %s, finished %s command handler", $dto->getId(), $commandHandler->getName()); } } }
/** * Returns variable value * @param string $name * @return string|null */ public static function get($name) { return Ar::get(Registry::get('variables'), $name); }
/** * @param string $field field to get * @return mixed */ public function get($field) { return Ar::get($this->data, $field); }
/** * Returns named param or null * @param string $name * @return null */ public function get($name) { return Ar::get($this->args, $name); }
/** * @param RequestDto $dto * @return null */ public function handler(RequestDto $dto) { // 4a. Timeout, exiting if ($this->coreProcessor->isMessageTimedOut($this->handlerId)) { $this->slackFacade->getSlackApi()->chatPostMessage($this->recipientId, 'Sorry, response timed out'); $this->coreProcessor->removeTimedMessageHandler($this->handlerId); return; } // 4b. Processed. Setting variable. if ($this->coreProcessor->isMessageHandled($this->handlerId)) { $dto = $this->coreProcessor->getTimedMessageHandleResult($this->handlerId); if (null === $dto) { return; } $this->coreProcessor->removeTimedMessageHandler($this->handlerId); $response = $dto->get('text'); Variables::set(Ar::get($this->dtoData, 'variable'), $response); // 5. Send "after" message $afterMessage = Ar::get($this->dtoData, 'messages.after'); if (null !== $afterMessage) { $this->slackFacade->getSlackApi()->chatPostMessage($this->recipientId, $afterMessage); } // 6. Processing other actions. $afterActions = Ar::get($this->dtoData, 'after'); if (0 === count($afterActions)) { return; } foreach ($afterActions as $afterAction) { $actionDto = $this->createActionDto(); $this->populateActionDto($actionDto, $afterAction); $this->coreProcessor->processAction($actionDto); } } }
public function buildLogger(Container $container) { $logger = new Logger($container['names_resolver']); /** @var Config $config */ $config = $container['config']; $loggingConfig = $config->getSection('logging'); $shouldResolve = Ar::get($loggingConfig, 'resolve') ?: false; $logger->setResolveNames($shouldResolve); $loggingEntries = Ar::get($loggingConfig, 'handlers') ?: []; Ar::each($loggingEntries, function ($loggingEntry) use($logger, $container) { $filter = Ar::get($loggingEntry, 'filter') ?: 255; $handlerName = Ar::get($loggingEntry, 'handler'); switch ($handlerName) { case 'slack': $channels = Ar::get($loggingEntry, 'channels') ?: []; $handler = (new SlackHandler($container['slack_facade']))->setChannels($channels)->setFilter($filter); break; case 'console': $handler = (new ConsoleOutputHandler())->setFilter($filter); break; default: return; } $logger->addHandler($handler); }); $container['logger'] = $logger; }
/** * Main execution loop * @throws \Exception */ protected function processLoop() { while (true) { try { $data = $this->client->receive(); $parsedData = json_decode($data, true); if ('message' === Ar::get($parsedData, 'type') && 'bot_message' !== Ar::get($parsedData, 'subtype')) { echo sprintf("[INFO] Got message: '%s' from %s in %s\n", Ar::get($parsedData, 'text') ?: '<nothing>', Ar::get($parsedData, 'user') ?: 'bot', Ar::get($parsedData, 'channel') ?: 'unknown channel'); try { $this->curlRequest->getCurlResult($this->serverUrl, [CURLOPT_POST => true, CURLOPT_POSTFIELDS => ['message' => $data]]); } catch (\Exception $e) { } } } catch (Exception $e) { $result = $this->curlRequest->getCurlResult($this->authUrl); $result = json_decode($result['body'], true); $this->socketUrl = $result['url']; $this->client = $this->createClient(); } } }
/** * @test * @dataProvider arrayMapDataProvider * @param array $arrayToMap * @param callable $mapperCallback * @param mixed $sampler */ public function shouldReturnRightArrayMapValues($arrayToMap, $mapperCallback, $sampler) { $result = Ar::map($arrayToMap, $mapperCallback); $this->assertEquals($sampler, $result); }
/** * @param $type * @param $message * @return void */ public function send($type, $message) { if (!$this->isFiltered($type)) { echo sprintf("[%s] %s\n", strtoupper(Ar::get(Logger::TYPE_NAMES, $type)) ?: '?', $message); } }