/** * @param $consoleCommand SyncCommandBase * @param $grooveTicket array * @return array * @throws ValidationException * @internal param array $helpscoutUsers */ private static function retrieveThreadsForGrooveTicket($consoleCommand, $grooveTicket) { $pageNumber = 1; $helpscoutThreads = array(); $grooveTicketNumber = $grooveTicket['number']; do { /* @var $grooveMessages array */ $grooveMessages = $consoleCommand->makeRateLimitedRequest(GROOVE, function () use($consoleCommand, $pageNumber, $grooveTicket) { return $consoleCommand->getGrooveClient()->messages()->list(['page' => $pageNumber, 'per_page' => 50, 'ticket_number' => $grooveTicket['number']]); }); foreach ($grooveMessages['messages'] as $grooveMessage) { $authorEmailAddress = null; try { list($authorEmailAddress, $addressType) = self::extractEmailAddressFromGrooveLink($grooveMessage['links']['author']['href'], 'author'); // only agents/users can create private notes in HelpScout // this addresses the Groove issue where agents could forward tickets to customers and customers could leave notes $isPrivateNote = $grooveMessage['note']; if ($customerEmails = explode(',', $consoleCommand->option('customerEmails'))) { foreach ($customerEmails as $customerEmail) { if (strcasecmp($customerEmail, $authorEmailAddress) === 0) { $isPrivateNote = false; break; } } } /* @var $thread AbstractThread */ $thread = null; if ($isPrivateNote) { $thread = new Note(); $thread->setType('note'); } else { $thread = new Customer(); $thread->setType('customer'); } $thread->setBody($grooveMessage['body']); $datetime = new DateTime($grooveMessage['created_at']); $thread->setCreatedAt($datetime->format('c')); // There is no particular status for a single message in Groove // Assume the status is the same as the ticket's $status = APIHelper::getHelpScoutStatusForGrooveState($grooveTicket['state']); if ($status) { $thread->setStatus($status); } else { $consoleCommand->error("Unknown state provided for Groove ticket #{$grooveTicketNumber}: " . $grooveTicket['state']); } // CreatedBy is a PersonRef - type must be 'user' for messages or notes // Type must be 'customer' for customer threads // Chat or phone types can be either 'user' or 'customer' // 'user' types require an ID field // 'customer' types require either an ID or email $id = null; $personRef = new PersonRef(); if (strcasecmp($addressType, 'customer') === 0 && !$isPrivateNote) { /* @var $response Collection */ $helpscoutCustomer = $consoleCommand->makeRateLimitedRequest(HELPSCOUT, function () use($consoleCommand, $authorEmailAddress) { return $consoleCommand->getHelpScoutClient()->searchCustomersByEmail($authorEmailAddress); }); if ($helpscoutCustomer->getCount() > 0) { /* @var $firstItem \HelpScout\model\Customer */ $firstItem = $helpscoutCustomer->getItems()[0]; $id = $firstItem->getId(); } else { // the customer could be blank - we need to fetch extra details from Groove // perhaps the sync-customers was not run? $consoleCommand->warn('Warning: Could not find HelpScout customer for ' . $authorEmailAddress . " (Groove ticket #{$grooveTicketNumber}). Was sync-customers command run?"); try { $grooveCustomer = $consoleCommand->makeRateLimitedRequest(GROOVE, function () use($consoleCommand, $grooveMessage) { // we need to make a raw curl request because the current version of the // Groove/Guzzle API client does not support disabling urlencoding in URL parameters // this is apparently a Groove API requirement $url = $grooveMessage['links']['author']['href'] . '?access_token=' . config('services.groove.key'); $jsonData = json_decode(file_get_contents($url), true); return $jsonData['customer']; }); list($firstName, $lastName) = APIHelper::extractFirstAndLastNameFromFullName($grooveCustomer['name'], $consoleCommand); $personRef->setFirstName($firstName); $personRef->setLastName($lastName); } catch (Exception $e) { $errorMessage = "Groove customer could not be retrieved for ticket #{$grooveTicketNumber} \"" . $grooveTicket['summary'] . "\""; $consoleCommand->error($errorMessage . ": " . $e->getMessage()); } } } else { // person is an agent/user $matchingUser = APIHelper::findMatchingUserWithEmail($authorEmailAddress); if (!$matchingUser) { throw new ValidationException("No corresponding user found for: {$authorEmailAddress} (Groove ticket #{$grooveTicketNumber})"); } // set ID only on notes if ($isPrivateNote) { $id = $matchingUser->getId(); } $personRef->setFirstName($matchingUser->getFirstName()); $personRef->setLastName($matchingUser->getLastName()); } $personRef->setType($isPrivateNote ? 'user' : 'customer'); $personRef->setEmail($authorEmailAddress); $personRef->setId($id); $thread->setCreatedBy($personRef); // To field if (isset($grooveMessage['links']['recipient'])) { list($recipientEmailAddress, $addressType) = self::extractEmailAddressFromGrooveLink($grooveMessage['links']['recipient']['href'], 'recipient'); if ($recipientEmailAddress) { $thread->setToList(array($recipientEmailAddress)); } } list($attachments, $failedAttachmentNotes) = self::retrieveAttachmentsForGrooveMessage($consoleCommand, $grooveMessage, $status, $grooveTicket); $thread->setAttachments($attachments); $helpscoutThreads[] = $thread; if (count($failedAttachmentNotes) > 0) { $helpscoutThreads = array_merge($helpscoutThreads, $failedAttachmentNotes); } } catch (ApiException $e) { $consoleCommand->error("Failed to create HelpScout thread for Groove message (" . $grooveMessage['href'] . " created by {$authorEmailAddress} at " . $grooveMessage['created_at'] . ", ticket #{$grooveTicketNumber}). Message was: \n" . APIHelper::formatApiExceptionArray($e)); } } $pageNumber++; } while ($pageNumber < $grooveMessages['meta']['pagination']['total_pages']); return $helpscoutThreads; }