setAllowParallelRuns(false); $this->setInterval(ContextChatProvider::CONTEXT_CHAT_JOB_INTERVAL); $this->setTimeSensitivity(IJob::TIME_INSENSITIVE); } #[\Override] protected function run($argument): void { if (!$this->contentManager->isContextChatAvailable()) { return; } try { $task = $this->taskService->findNext(); } catch (Exception $e) { $this->logger->warning('Exception occurred when trying to fetch next task', ['exception' => $e]); return; } catch (DoesNotExistException $e) { // nothing to be done, let's defer to the next iteration of this job return; } catch (MultipleObjectsReturnedException $e) { $this->logger->warning('Multiple tasks found for context chat. This is unexpected.', ['exception' => $e]); return; } try { $mailbox = $this->mailboxMapper->findById($task->getMailboxId()); } catch (ServiceException $e) { $this->logger->warning('Multiple mailboxes found for context chat task, but only one expected. ERROR!', ['exception' => $e]); return; } catch (DoesNotExistException $e) { // mailbox does not exist, lets wait for this task to be removed return; } $processMailsAfter = $this->time->getTime() - ContextChatProvider::CONTEXT_CHAT_MESSAGE_MAX_AGE; $messageIds = $this->messageMapper->findIdsAfter($mailbox, $task->getLastMessageId(), $processMailsAfter, ContextChatProvider::CONTEXT_CHAT_IMPORT_MAX_ITEMS); if (empty($messageIds)) { try { $this->taskService->delete($task->getId()); } catch (MultipleObjectsReturnedException|Exception $e) { $this->logger->warning('Exception occurred when trying to delete task', ['exception' => $e]); } return; } try { $account = $this->accountService->findById($mailbox->getAccountId()); } catch (DoesNotExistException $e) { // well, what do you know. Then let's just skip this and wait for the next iteration of this job. tasks should be cascade deleted anyway return; } $messages = $this->messageMapper->findByIds($account->getUserId(), $messageIds, 'asc', 'id'); if (empty($messages)) { try { $this->taskService->delete($task->getId()); } catch (MultipleObjectsReturnedException|Exception $e) { $this->logger->warning('Exception occurred when trying to delete task', ['exception' => $e]); } return; } $client = $this->clientFactory->getClient($account); $items = []; try { $startTime = $this->time->getTime(); foreach ($messages as $message) { if ($this->time->getTime() - $startTime > ContextChatProvider::CONTEXT_CHAT_JOB_INTERVAL) { break; } try { $imapMessage = $this->mailManager->getImapMessage($client, $account, $mailbox, $message->getUid(), true); } catch (ServiceException $e) { // couldn't load message, let's skip it. Retrying would be too costly continue; } catch (SmimeDecryptException $e) { // encryption problem, skip this message continue; } // Skip encrypted messages if ($imapMessage->isEncrypted()) { continue; } $fullMessage = $imapMessage->getFullMessage($imapMessage->getUid(), true); $items[] = new ContentItem( $mailbox->getId() . ':' . $message->getId(), $this->contextChatProvider->getId(), $imapMessage->getSubject(), $fullMessage['body'] ?? '', 'E-Mail', $imapMessage->getSentDate(), [$account->getUserId()], ); } } catch (\Throwable $e) { $this->logger->warning('Exception occurred when trying to fetch messages for context chat', ['exception' => $e]); } finally { try { $client->close(); } catch (\Horde_Imap_Client_Exception $e) { $this->logger->debug('Failed to close IMAP client', ['exception' => $e]); } } if (count($items) > 0) { $this->contentManager->submitContent($this->contextChatProvider->getAppId(), $items); } try { $this->taskService->setLastMessage($task->getMailboxId(), $message?->getId() ?? $messageIds[0]); } catch (MultipleObjectsReturnedException|Exception $e) { $this->logger->warning('Exception occurred when trying to update task', ['exception' => $e]); } } }