diff --git a/appinfo/routes.php b/appinfo/routes.php index fcff5d9a8c..1178af7b11 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -410,6 +410,11 @@ 'url' => '/api/thread/{id}/summary', 'verb' => 'GET' ], + [ + 'name' => 'thread#generateEventData', + 'url' => '/api/thread/{id}/eventdata', + 'verb' => 'GET' + ], [ 'name' => 'outbox#send', 'url' => '/api/outbox/{id}', diff --git a/lib/Controller/ThreadController.php b/lib/Controller/ThreadController.php index 5c0d7a5c68..d9992813e1 100755 --- a/lib/Controller/ThreadController.php +++ b/lib/Controller/ThreadController.php @@ -183,6 +183,29 @@ public function summarize(int $id): JSONResponse { return new JSONResponse(['data' => $summary]); } + public function generateEventData(int $id): JSONResponse { + try { + $message = $this->mailManager->getMessage($this->currentUserId, $id); + $mailbox = $this->mailManager->getMailbox($this->currentUserId, $message->getMailboxId()); + $account = $this->accountService->find($this->currentUserId, $mailbox->getAccountId()); + } catch (DoesNotExistException $e) { + return new JSONResponse([], Http::STATUS_FORBIDDEN); + } + if (empty($message->getThreadRootId())) { + return new JSONResponse([], Http::STATUS_NOT_FOUND); + } + $thread = $this->mailManager->getThread($account, $message->getThreadRootId()); + $data = $this->aiIntergrationsService->generateEventData( + $account, + $mailbox, + $message->getThreadRootId(), + $thread, + $this->currentUserId, + ); + + return new JSONResponse(['data' => $data]); + } + /** * @NoAdminRequired * diff --git a/lib/Model/EventData.php b/lib/Model/EventData.php new file mode 100644 index 0000000000..6d9bf58c18 --- /dev/null +++ b/lib/Model/EventData.php @@ -0,0 +1,52 @@ + + * + * @author 2024 Christoph Wurst + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +namespace OCA\Mail\Model; + +use JsonSerializable; +use ReturnTypeWillChange; + +class EventData implements JsonSerializable { + + public function __construct(private string $summary, + private string $description) { + } + + public function getSummary(): string { + return $this->summary; + } + + public function getDescription(): string { + return $this->description; + } + + #[ReturnTypeWillChange] + public function jsonSerialize() { + return [ + 'summary' => $this->summary, + 'description' => $this->description, + ]; + } +} diff --git a/lib/Service/AiIntegrations/AiIntegrationsService.php b/lib/Service/AiIntegrations/AiIntegrationsService.php index f59aaa3203..722c1ca411 100644 --- a/lib/Service/AiIntegrations/AiIntegrationsService.php +++ b/lib/Service/AiIntegrations/AiIntegrationsService.php @@ -23,19 +23,25 @@ namespace OCA\Mail\Service\AiIntegrations; +use JsonException; use OCA\Mail\Account; use OCA\Mail\Contracts\IMailManager; use OCA\Mail\Db\Mailbox; use OCA\Mail\Db\Message; use OCA\Mail\Exception\ServiceException; use OCA\Mail\IMAP\IMAPClientFactory; +use OCA\Mail\Model\EventData; use OCA\Mail\Model\IMAPMessage; use OCP\TextProcessing\FreePromptTaskType; use OCP\TextProcessing\IManager; use OCP\TextProcessing\SummaryTaskType; use OCP\TextProcessing\Task; +use Psr\Container\ContainerExceptionInterface; use Psr\Container\ContainerInterface; use function array_map; +use function implode; +use function in_array; +use function json_decode; class AiIntegrationsService { @@ -51,6 +57,12 @@ class AiIntegrationsService { /** @var IMailManager */ private IMailManager $mailManager; + private const EVENT_DATA_PROMPT_PREAMBLE = <<container = $container; @@ -110,6 +122,50 @@ public function summarizeThread(Account $account, Mailbox $mailbox, string $thre } } + /** + * @param Message[] $messages + */ + public function generateEventData(Account $account, Mailbox $mailbox, string $threadId, array $messages, string $currentUserId): ?EventData { + try { + /** @var IManager $manager */ + $manager = $this->container->get(IManager::class); + } catch (ContainerExceptionInterface $e) { + return null; + } + if (!in_array(FreePromptTaskType::class, $manager->getAvailableTaskTypes(), true)) { + return null; + } + $client = $this->clientFactory->getClient($account); + try { + $messageBodies = array_map(function ($message) use ($client, $account, $mailbox) { + $imapMessage = $this->mailManager->getImapMessage( + $client, + $account, + $mailbox, + $message->getUid(), true + ); + return $imapMessage->getPlainBody(); + }, $messages); + } finally { + $client->logout(); + } + + $task = new Task( + FreePromptTaskType::class, + self::EVENT_DATA_PROMPT_PREAMBLE . implode("\n\n---\n\n", $messageBodies), + "mail", + $currentUserId, + "event_data_$threadId", + ); + $result = $manager->runTask($task); + try { + $decoded = json_decode($result, true, 512, JSON_THROW_ON_ERROR); + return new EventData($decoded['title'], $decoded['agenda']); + } catch (JsonException $e) { + return null; + } + } + /** * @return string[] */ diff --git a/src/components/EventModal.vue b/src/components/EventModal.vue index ee033d0ab6..2710027c17 100644 --- a/src/components/EventModal.vue +++ b/src/components/EventModal.vue @@ -3,7 +3,7 @@