Esempio n. 1
0
 private function getJoke() : \Generator
 {
     /** @var HttpResponse $response */
     $response = (yield $this->httpClient->request(self::API_URL));
     $result = json_try_decode($response->getBody(), true);
     if (!isset($result['type']) && $result['type'] !== 'success') {
         throw new \RuntimeException('Invalid response format');
     }
     return htmlspecialchars_decode($result['value']['joke']);
 }
Esempio n. 2
0
 private function executeAction(Action $action)
 {
     $exceptionClass = $action->getExceptionClassName();
     if (!$action->isValid()) {
         $action->fail(new $exceptionClass('Action no longer valid at point of execution'));
         return;
     }
     $attempt = 0;
     while ($attempt++ < $action->getMaxAttempts()) {
         /** @var HttpResponse $response */
         $this->logger->log(Level::DEBUG, 'Post attempt ' . $attempt);
         $response = (yield $this->httpClient->request($action->getRequest()));
         $this->logger->log(Level::DEBUG, 'Got response from server: ' . $response->getBody());
         if ($response->getStatus() === 409) {
             try {
                 $delay = $this->getBackOffDelay($response->getBody());
             } catch (\InvalidArgumentException $e) {
                 $errStr = 'Got a 409 response to an Action request that could not be decoded as a back-off delay';
                 $this->logger->log(Level::ERROR, $errStr, $response->getBody());
                 $action->fail(new $exceptionClass($errStr, $e->getCode(), $e));
                 return;
             }
             $this->logger->log(Level::DEBUG, "Backing off chat action execution for {$delay}ms");
             (yield new Pause($delay));
             continue;
         }
         if ($response->getStatus() !== 200) {
             $errStr = 'Got a ' . $response->getStatus() . ' response to an Action request';
             $this->logger->log(Level::ERROR, $errStr, [$action->getRequest(), $response]);
             $action->fail(new $exceptionClass($errStr));
             return;
         }
         try {
             $decoded = json_try_decode($response->getBody(), true);
         } catch (JSONDecodeErrorException $e) {
             $errStr = 'A response that could not be decoded as JSON was received' . ' (JSON decode error: ' . $e->getMessage() . ')';
             $this->logger->log(Level::ERROR, $errStr, $response->getBody());
             $action->fail(new $exceptionClass($errStr, $e->getCode(), $e));
             return;
         }
         $result = $action->processResponse($decoded, $attempt);
         if ($result < 1) {
             return;
         }
         if ($attempt >= $action->getMaxAttempts()) {
             break;
         }
         $this->logger->log(Level::DEBUG, "Backing off chat action execution for {$result}ms");
         (yield new Pause($result));
     }
     $this->logger->log(Level::ERROR, 'Executing an action failed after ' . $action->getMaxAttempts() . ' attempts and I know when to quit');
 }
Esempio n. 3
0
 public function getAccessToken(string $clientID, string $clientSecret) : Promise
 {
     $body = (new FormBody())->addField('grant_type', 'client_credentials')->addField('scope', self::BASE_URL)->addField('client_id', $clientID)->addField('client_secret', $clientSecret);
     $request = (new HttpRequest())->setMethod('POST')->setUri(self::AUTH_URL)->setBody($body);
     return resolve(function () use($request) {
         /** @var HttpResponse $response */
         $response = (yield $this->httpClient->request($request));
         $decoded = json_try_decode($response->getBody());
         if (!empty($decoded->error)) {
             throw new \RuntimeException($decoded->error_description);
         }
         return $decoded->access_token;
     });
 }
Esempio n. 4
0
 public function search(Command $command) : \Generator
 {
     if (!$command->hasParameters()) {
         return new Success();
     }
     /** @var HttpResponse $response */
     $response = (yield $this->makeAPIRequest(['titles' => implode(' ', $command->getParameters())]));
     $result = json_try_decode($response->getBody(), true);
     $firstHit = reset($result['query']['pages']);
     if (!isset($firstHit['pageid'])) {
         return $this->chatClient->postReply($command, 'Sorry I couldn\'t find that page.');
     }
     $response = (yield $this->makeAPIRequest(['prop' => 'info', 'inprop' => 'url', 'pageids' => $firstHit['pageid']]));
     $page = json_try_decode($response->getBody(), true);
     return $this->chatClient->postMessage($command->getRoom(), $page['query']['pages'][$firstHit['pageid']]['canonicalurl']);
 }
Esempio n. 5
0
 private function refreshGlobalJokesIfNecessary()
 {
     $refreshNeeded = !(yield $this->storage->exists('jokes')) || !(yield $this->storage->exists('refreshtime')) || (yield $this->storage->get('refreshtime')) < time();
     if (!$refreshNeeded) {
         return;
     }
     /** @var HttpResponse $response */
     $response = (yield $this->httpClient->request(self::JOKE_URL));
     if (!preg_match('#JOKES=(\\[[^\\]]+])#', $response->getBody(), $match)) {
         return;
     }
     $jokesJson = preg_replace('#((?<={)setup|(?<=,)punchline)#', '"$1"', $match[1]);
     $jokesJson = preg_replace_callback('#(?<=:|,|{)\'(.*?)\'(?=:|,|\\))#', function ($match) {
         return json_encode($match[1]);
     }, $jokesJson);
     (yield $this->storage->set('jokes', json_try_decode($jokesJson, true)));
     (yield $this->storage->set('refreshtime', time() + 86400));
 }
Esempio n. 6
0
 public function search(Command $command) : \Generator
 {
     $info = explode('/', implode('/', $command->getParameters()), 2);
     if (count($info) !== 2) {
         return $this->chatClient->postReply($command, "Usage: `!!packagist vendor package`");
     }
     list($vendor, $package) = $info;
     $url = 'https://packagist.org/packages/' . urlencode($vendor) . '/' . urldecode($package) . '.json';
     try {
         /** @var HttpResponse $response */
         $response = (yield $this->httpClient->request($url));
         if ($response->getStatus() !== 200) {
             $response = (yield from $this->getResultFromSearchFallback($vendor, $package));
         }
         $data = json_try_decode($response->getBody());
         return $this->chatClient->postMessage($command->getRoom(), sprintf("[ [%s](%s) ] %s", $data->package->name, $data->package->repository, $data->package->description));
     } catch (\Throwable $e) {
         return $this->chatClient->postReply($command, 'No matching packages found');
     }
 }
Esempio n. 7
0
 private function getJsonRequestBody(AerysRequest $request, AerysResponse $response) : \Generator
 {
     if ($request->getHeader('Content-Type') !== 'application/json') {
         $this->respondWithError($response, 400, 'Type of request body must be application/json');
         return null;
     }
     $body = $request->getBody();
     $bodyText = '';
     while ((yield $body->valid())) {
         $bodyText .= $body->consume();
     }
     try {
         $json = json_try_decode($bodyText, true);
     } catch (JSONDecodeErrorException $e) {
         $this->respondWithError($response, 400, 'Unable to decode request body as application/json');
         return null;
     }
     if (!is_array($json)) {
         $this->respondWithError($response, 400, 'Invalid request data structure');
         return null;
     }
     return $json;
 }
Esempio n. 8
0
 public function onData(WebsocketMessage $websocketMessage)
 {
     try {
         $rawWebsocketMessage = (yield $websocketMessage);
         $this->logger->log(Level::DEBUG, "Websocket message received on connection to {$this->roomIdentifier}", $rawWebsocketMessage);
         $this->clearTimeoutWatcher();
         $this->setTimeoutWatcher();
         try {
             $data = json_try_decode($rawWebsocketMessage, true);
         } catch (JSONDecodeErrorException $e) {
             $this->logger->log(Level::ERROR, "Error decoding JSON message from server: {$e->getMessage()}");
             return;
         }
         /** @var Event[] $events */
         $events = (yield from $this->eventBuilder->build($data, $this->roomIdentifier));
         $this->logger->log(Level::DEBUG, count($events) . " events targeting {$this->roomIdentifier} to process");
         foreach ($events as $event) {
             (yield $this->eventDispatcher->processWebSocketEvent($event));
         }
     } catch (\Throwable $e) {
         $this->logger->log(Level::DEBUG, "Something went generally wrong while processing events for {$this->roomIdentifier}: {$e}");
     }
 }
Esempio n. 9
0
 private function loadFile(string $filePath) : \Generator
 {
     if (isset($this->loadPromises[$filePath])) {
         (yield $this->loadPromises[$filePath]);
         return $this->dataCache[$filePath];
     }
     $deferred = new Deferred();
     $this->loadPromises[$filePath] = $deferred->promise();
     $this->lockMutexes[$filePath] = new QueuedExclusiveMutex();
     return (yield $this->lockMutexes[$filePath]->withLock(function () use($filePath, $deferred) {
         try {
             // we may have been waiting on a lock and it's been populated by now
             if (!isset($this->dataCache[$filePath])) {
                 $this->dataCache[$filePath] = (yield exists($filePath)) ? json_try_decode((yield get($filePath)), true) : [];
             }
         } catch (\Throwable $e) {
             $this->dataCache[$filePath] = [];
         } finally {
             $deferred->succeed();
             unset($this->loadPromises[$filePath]);
         }
         return $this->dataCache[$filePath];
     }));
 }
Esempio n. 10
0
 /**
  * @param ChatRoom|ChatRoomIdentifier $room
  * @return Promise
  */
 public function getPingableUsers($room) : Promise
 {
     $url = $this->urlResolver->getEndpointURL($room, ChatRoomEndpoint::CHATROOM_INFO_PINGABLE);
     return resolve(function () use($url) {
         /** @var HttpResponse $response */
         $response = (yield $this->httpClient->request($url));
         if ($response->getStatus() !== 200) {
             throw new DataFetchFailureException("Fetching pingable users list failed with response code " . $response->getStatus());
         }
         $result = [];
         foreach (json_try_decode($response->getBody(), true) as $item) {
             $result[] = ['id' => (int) $item[0], 'name' => $item[1], 'pingable' => preg_replace('~\\s+~', '', $item[1])];
         }
         return $result;
     });
 }
Esempio n. 11
0
 private function getWebSocketUri(Identifier $identifier, string $fKey)
 {
     $authBody = (new FormBody())->addField("roomid", $identifier->getId())->addField("fkey", $fKey);
     $historyBody = (new FormBody())->addField('since', 0)->addField('mode', 'Messages')->addField("msgCount", 1)->addField("fkey", $fKey);
     $requests = ['auth' => (new HttpRequest())->setUri($this->urlResolver->getEndpointURL($identifier, Endpoint::CHATROOM_WEBSOCKET_AUTH))->setMethod("POST")->setBody($authBody), 'history' => (new HttpRequest())->setUri($this->urlResolver->getEndpointURL($identifier, Endpoint::CHATROOM_EVENT_HISTORY))->setMethod("POST")->setBody($historyBody)];
     /** @var HttpResponse[] $responses */
     $responses = (yield all($this->httpClient->requestMulti($requests)));
     $authInfo = json_try_decode($responses['auth']->getBody(), true);
     $historyInfo = json_try_decode($responses['history']->getBody(), true);
     if (!isset($authInfo['url'])) {
         throw new \RuntimeException("WebSocket auth did not return URL");
     }
     if (!isset($historyInfo['time'])) {
         throw new \RuntimeException("Could not get time for WebSocket URL");
     }
     return $authInfo['url'] . '?l=' . $historyInfo['time'];
 }