diff --git a/appinfo/routes/routesChatController.php b/appinfo/routes/routesChatController.php index d6933d1ca6d..4eb83b7e946 100644 --- a/appinfo/routes/routesChatController.php +++ b/appinfo/routes/routesChatController.php @@ -21,6 +21,8 @@ 'ocs' => [ /** @see \OCA\Talk\Controller\ChatController::receiveMessages() */ ['name' => 'Chat#receiveMessages', 'url' => '/api/{apiVersion}/chat/{token}', 'verb' => 'GET', 'requirements' => $requirements], + /** @see \OCA\Talk\Controller\ChatController::summarizeChat() */ + ['name' => 'Chat#summarizeChat', 'url' => '/api/{apiVersion}/chat/{token}/summarize', 'verb' => 'GET', 'requirements' => $requirements], /** @see \OCA\Talk\Controller\ChatController::sendMessage() */ ['name' => 'Chat#sendMessage', 'url' => '/api/{apiVersion}/chat/{token}', 'verb' => 'POST', 'requirements' => $requirements], /** @see \OCA\Talk\Controller\ChatController::clearHistory() */ diff --git a/lib/Controller/ChatController.php b/lib/Controller/ChatController.php index e598b0e9e26..f4f8f01ad29 100644 --- a/lib/Controller/ChatController.php +++ b/lib/Controller/ChatController.php @@ -8,6 +8,7 @@ namespace OCA\Talk\Controller; +use OCA\Talk\AppInfo\Application; use OCA\Talk\Chat\AutoComplete\SearchPlugin; use OCA\Talk\Chat\AutoComplete\Sorter; use OCA\Talk\Chat\ChatManager; @@ -15,6 +16,7 @@ use OCA\Talk\Chat\Notifier; use OCA\Talk\Chat\ReactionManager; use OCA\Talk\Exceptions\CannotReachRemoteException; +use OCA\Talk\Exceptions\ChatSummaryException; use OCA\Talk\Federation\Authenticator; use OCA\Talk\GuestManager; use OCA\Talk\MatterbridgeManager; @@ -62,14 +64,20 @@ use OCP\IRequest; use OCP\IUserManager; use OCP\RichObjectStrings\InvalidObjectExeption; +use OCP\RichObjectStrings\IRichTextFormatter; use OCP\RichObjectStrings\IValidator; use OCP\Security\ITrustedDomainHelper; use OCP\Security\RateLimiting\IRateLimitExceededException; use OCP\Share\Exceptions\ShareNotFound; use OCP\Share\IShare; +use OCP\TaskProcessing\Exception\Exception; +use OCP\TaskProcessing\IManager as ITaskProcessingManager; +use OCP\TaskProcessing\Task; +use OCP\TaskProcessing\TaskTypes\TextToTextSummary; use OCP\User\Events\UserLiveStatusEvent; use OCP\UserStatus\IManager as IUserStatusManager; use OCP\UserStatus\IUserStatus; +use Psr\Log\LoggerInterface; /** * @psalm-import-type TalkChatMentionSuggestion from ResponseDefinitions @@ -114,6 +122,9 @@ public function __construct( protected Authenticator $federationAuthenticator, protected ProxyCacheMessageService $pcmService, protected Notifier $notifier, + protected IRichTextFormatter $richTextFormatter, + protected ITaskProcessingManager $taskProcessingManager, + protected LoggerInterface $logger, ) { parent::__construct($appName, $request); } @@ -489,6 +500,115 @@ public function receiveMessages(int $lookIntoFuture, return $this->prepareCommentsAsDataResponse($comments, $lastCommonReadId); } + /** + * Summarize next bunch of chat messages from a given offset + * + * @param positive-int $fromMessageId Offset from where on the summary should be generated + * @return DataResponse|DataResponse|DataResponse, array{}> + * @throws \InvalidArgumentException + * + * 201: Summary was scheduled + * 204: No messages found to summarize + * 400: No AI provider available or summarizing failed + */ + #[PublicPage] + #[RequireModeratorOrNoLobby] + #[RequireParticipant] + public function summarizeChat( + int $fromMessageId, + ): DataResponse { + $fromMessageId = max(0, $fromMessageId); + + $supportedTaskTypes = $this->taskProcessingManager->getAvailableTaskTypes(); + if (!isset($supportedTaskTypes[TextToTextSummary::ID])) { + return new DataResponse([ + 'error' => ChatSummaryException::REASON_AI_ERROR, + ], Http::STATUS_BAD_REQUEST); + } + + // if ($this->room->isFederatedConversation()) { + // /** @var \OCA\Talk\Federation\Proxy\TalkV1\Controller\ChatController $proxy */ + // $proxy = \OCP\Server::get(\OCA\Talk\Federation\Proxy\TalkV1\Controller\ChatController::class); + // return $proxy->summarizeChat( + // $this->room, + // $this->participant, + // $fromMessageId, + // ); + // } + + $currentUser = $this->userManager->get($this->userId); + $comments = $this->chatManager->waitForNewMessages($this->room, $fromMessageId, 500, 0, $currentUser, true, false); + $this->preloadShares($comments); + + $messages = []; + $nextOffset = 0; + foreach ($comments as $comment) { + $message = $this->messageParser->createMessage($this->room, $this->participant, $comment, $this->l); + $this->messageParser->parseMessage($message); + + if (!$message->getVisibility()) { + continue; + } + + $parsedMessage = $this->richTextFormatter->richToParsed( + $message->getMessage(), + $message->getMessageParameters(), + ); + + $displayName = $message->getActorDisplayName(); + if (in_array($message->getActorType(), [ + Attendee::ACTOR_GUESTS, + Attendee::ACTOR_EMAILS, + ], true)) { + if ($displayName === '') { + $displayName = $this->l->t('Guest'); + } else { + $displayName = $this->l->t('%s (guest)', $displayName); + } + } + + if ($comment->getParentId() !== '0') { + // FIXME should add something? + } + + $messages[] = $displayName . ': ' . $parsedMessage; + $nextOffset = (int)$comment->getId(); + } + + if (empty($messages)) { + return new DataResponse([], Http::STATUS_NO_CONTENT); + } + + $task = new Task( + TextToTextSummary::ID, + ['input' => implode("\n\n", $messages)], + Application::APP_ID, + $this->userId, + 'summary/' . $this->room->getToken(), + ); + + try { + $this->taskProcessingManager->scheduleTask($task); + } catch (Exception $e) { + $this->logger->error('An error occurred while trying to summarize unread messages', ['exception' => $e]); + return new DataResponse([ + 'error' => ChatSummaryException::REASON_AI_ERROR, + ], Http::STATUS_BAD_REQUEST); + } + + $taskId = $task->getId(); + if ($taskId === null) { + return new DataResponse([ + 'error' => ChatSummaryException::REASON_AI_ERROR, + ], Http::STATUS_BAD_REQUEST); + } + + return new DataResponse([ + 'taskId' => $taskId, + 'nextOffset' => $nextOffset, + ], Http::STATUS_CREATED); + } + /** * @return DataResponse */ diff --git a/lib/Exceptions/ChatSummaryException.php b/lib/Exceptions/ChatSummaryException.php new file mode 100644 index 00000000000..170e304b984 --- /dev/null +++ b/lib/Exceptions/ChatSummaryException.php @@ -0,0 +1,30 @@ +reason; + } +}