-
Notifications
You must be signed in to change notification settings - Fork 316
Expand file tree
/
Copy pathSubmitContentJob.php
More file actions
171 lines (146 loc) Β· 5.4 KB
/
SubmitContentJob.php
File metadata and controls
171 lines (146 loc) Β· 5.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Mail\BackgroundJob\ContextChat;
use OCA\Mail\ContextChat\ContextChatProvider;
use OCA\Mail\Db\MailboxMapper;
use OCA\Mail\Db\Message;
use OCA\Mail\Db\MessageMapper;
use OCA\Mail\Exception\ServiceException;
use OCA\Mail\Exception\SmimeDecryptException;
use OCA\Mail\IMAP\IMAPClientFactory;
use OCA\Mail\Service\AccountService;
use OCA\Mail\Service\ContextChat\TaskService;
use OCA\Mail\Service\MailManager;
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\AppFramework\Db\MultipleObjectsReturnedException;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\BackgroundJob\IJob;
use OCP\BackgroundJob\TimedJob;
use OCP\ContextChat\ContentItem;
use OCP\ContextChat\IContentManager;
use OCP\DB\Exception;
use Psr\Log\LoggerInterface;
class SubmitContentJob extends TimedJob {
public function __construct(
ITimeFactory $time,
private TaskService $taskService,
private AccountService $accountService,
private MailManager $mailManager,
private MessageMapper $messageMapper,
private IMAPClientFactory $clientFactory,
private ContextChatProvider $contextChatProvider,
private IContentManager $contentManager,
private LoggerInterface $logger,
private MailboxMapper $mailboxMapper,
) {
parent::__construct($time);
$this->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 ($items !== []) {
$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]);
}
}
}