Skip to content

Commit

Permalink
feat(federation): Federate getting messages and the message context
Browse files Browse the repository at this point in the history
Signed-off-by: Joas Schilling <coding@schilljs.com>
  • Loading branch information
nickvergessen committed Feb 22, 2024
1 parent 3c20b50 commit c6a028e
Show file tree
Hide file tree
Showing 3 changed files with 145 additions and 10 deletions.
46 changes: 37 additions & 9 deletions lib/Controller/ChatController.php
Original file line number Diff line number Diff line change
Expand Up @@ -405,11 +405,12 @@ protected function preloadShares(array $comments): void {
* @param 0|1 $includeLastKnown Include the $lastKnownMessageId in the messages when 1 (default 0)
* @param 0|1 $noStatusUpdate When the user status should not be automatically set to online set to 1 (default 0)
* @param 0|1 $markNotificationsAsRead Set to 0 when notifications should not be marked as read (default 1)
* @return DataResponse<Http::STATUS_OK|Http::STATUS_NOT_MODIFIED, TalkChatMessageWithParent[], array{X-Chat-Last-Common-Read?: numeric-string, X-Chat-Last-Given?: string}>
* @return DataResponse<Http::STATUS_OK|Http::STATUS_NOT_MODIFIED, TalkChatMessageWithParent[], array{X-Chat-Last-Common-Read?: numeric-string, X-Chat-Last-Given?: numeric-string}>
*
* 200: Messages returned
* 304: No messages
*/
#[FederationSupported]
#[PublicPage]
#[RequireModeratorOrNoLobby]
#[RequireParticipant]
Expand All @@ -425,6 +426,24 @@ public function receiveMessages(int $lookIntoFuture,
$limit = min(200, $limit);
$timeout = min(30, $timeout);

if ($this->room->getRemoteServer() !== '') {
/** @var \OCA\Talk\Federation\Proxy\TalkV1\Controller\ChatController $proxy */
$proxy = \OCP\Server::get(\OCA\Talk\Federation\Proxy\TalkV1\Controller\ChatController::class);
return $proxy->receiveMessages(
$this->room,
$this->participant,
$lookIntoFuture,
$limit,
$lastKnownMessageId,
$lastCommonReadId,
$timeout,
$setReadMarker,
$includeLastKnown,
$noStatusUpdate,
$markNotificationsAsRead,
);
}

$session = $this->participant->getSession();
if ($noStatusUpdate === 0 && $session instanceof Session) {
// The mobile apps dont do internal signaling unless in a call
Expand Down Expand Up @@ -479,7 +498,7 @@ public function receiveMessages(int $lookIntoFuture,
}

/**
* @return DataResponse<Http::STATUS_OK|Http::STATUS_NOT_MODIFIED, TalkChatMessageWithParent[], array{X-Chat-Last-Common-Read?: numeric-string, X-Chat-Last-Given?: string}>
* @return DataResponse<Http::STATUS_OK|Http::STATUS_NOT_MODIFIED, TalkChatMessageWithParent[], array{X-Chat-Last-Common-Read?: numeric-string, X-Chat-Last-Given?: numeric-string}>
*/
protected function prepareCommentsAsDataResponse(array $comments, int $lastCommonReadId = 0): DataResponse {
if (empty($comments)) {
Expand All @@ -489,7 +508,7 @@ protected function prepareCommentsAsDataResponse(array $comments, int $lastCommo
// Set the status code to 200 so the header is sent to the client.
// As per "section 10.3.5 of RFC 2616" entity headers shall be
// stripped out on 304: https://stackoverflow.com/a/17822709
/** @var array{X-Chat-Last-Common-Read?: numeric-string, X-Chat-Last-Given?: string} $headers */
/** @var array{X-Chat-Last-Common-Read?: numeric-string, X-Chat-Last-Given?: numeric-string} $headers */
$headers = ['X-Chat-Last-Common-Read' => (string) $newLastCommonRead];
return new DataResponse([], Http::STATUS_OK, $headers);
}
Expand Down Expand Up @@ -589,7 +608,7 @@ protected function prepareCommentsAsDataResponse(array $comments, int $lastCommo
$headers = [];
$newLastKnown = end($comments);
if ($newLastKnown instanceof IComment) {
$headers = ['X-Chat-Last-Given' => (string) $newLastKnown->getId()];
$headers = ['X-Chat-Last-Given' => (string) (int) $newLastKnown->getId()];
if ($this->participant->getAttendee()->getReadPrivacy() === Participant::PRIVACY_PUBLIC) {
/**
* This falsely set the read marker on new messages, although you
Expand All @@ -616,11 +635,12 @@ protected function prepareCommentsAsDataResponse(array $comments, int $lastCommo
* @psalm-param non-negative-int $messageId
* @param 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 $limit Number of chat messages to receive in both directions (50 by default, 100 at most, might return 201 messages)
* @psalm-param int<1, 100> $limit
* @return DataResponse<Http::STATUS_OK|Http::STATUS_NOT_MODIFIED, TalkChatMessageWithParent[], array{X-Chat-Last-Common-Read?: numeric-string, X-Chat-Last-Given?: string}>
* @return DataResponse<Http::STATUS_OK|Http::STATUS_NOT_MODIFIED, TalkChatMessageWithParent[], array{X-Chat-Last-Common-Read?: numeric-string, X-Chat-Last-Given?: numeric-string}>
*
* 200: Message context returned
* 304: No messages
*/
#[FederationSupported]
#[PublicPage]
#[RequireModeratorOrNoLobby]
#[RequireParticipant]
Expand All @@ -629,6 +649,12 @@ public function getMessageContext(
int $limit = 50): DataResponse {
$limit = min(100, $limit);

if ($this->room->getRemoteServer() !== '') {
/** @var \OCA\Talk\Federation\Proxy\TalkV1\Controller\ChatController $proxy */
$proxy = \OCP\Server::get(\OCA\Talk\Federation\Proxy\TalkV1\Controller\ChatController::class);
return $proxy->getMessageContext($this->room, $this->participant, $messageId, $limit);
}

$currentUser = $this->userManager->get($this->userId);
$commentsHistory = $this->chatManager->getHistory($this->room, $messageId, $limit, true);
$commentsHistory = array_reverse($commentsHistory);
Expand Down Expand Up @@ -1097,7 +1123,7 @@ public function getObjectsSharedInRoomOverview(int $limit = 7): DataResponse {
* @psalm-param non-negative-int $lastKnownMessageId
* @param 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|172|173|174|175|176|177|178|179|180|181|182|183|184|185|186|187|188|189|190|191|192|193|194|195|196|197|198|199|200 $limit Maximum number of objects
* @psalm-param int<1, 200> $limit
* @return DataResponse<Http::STATUS_OK, TalkChatMessage[], array{X-Chat-Last-Given?: string}>
* @return DataResponse<Http::STATUS_OK, array<int, TalkChatMessage>, array{X-Chat-Last-Given?: numeric-string}>
*
* 200: List of shared objects messages returned
*/
Expand All @@ -1111,14 +1137,16 @@ public function getObjectsSharedInRoom(string $objectType, int $lastKnownMessage
$attachments = $this->attachmentService->getAttachmentsByType($this->room, $objectType, $offset, $limit);
$messageIds = array_map(static fn (Attachment $attachment): int => $attachment->getMessageId(), $attachments);

/** @var array<int, TalkChatMessage> $messages */
$messages = $this->getMessagesForRoom($messageIds);

$headers = [];
if (!empty($messages)) {
$newLastKnown = min(array_keys($messages));
return new DataResponse($messages, Http::STATUS_OK, ['X-Chat-Last-Given' => $newLastKnown]);
$newLastKnown = (string) (int) min(array_keys($messages));
$headers = ['X-Chat-Last-Given' => $newLastKnown];
}

return new DataResponse($messages, Http::STATUS_OK);
return new DataResponse($messages, Http::STATUS_OK, $headers);
}

/**
Expand Down
2 changes: 1 addition & 1 deletion lib/Controller/RoomController.php
Original file line number Diff line number Diff line change
Expand Up @@ -1462,7 +1462,7 @@ public function setPassword(string $password): DataResponse {
#[BruteForceProtection(action: 'talkRoomToken')]
public function joinRoom(string $token, string $password = '', bool $force = true): DataResponse {
$sessionId = $this->session->getSessionForRoom($token);
$isTalkFederation = $this->request->getHeader('X-Nextcloud-Federation');
$isTalkFederation = $this->federationAuthenticator->isFederationRequest();
try {
// The participant is just joining, so enforce to not load any session
if (!$isTalkFederation) {
Expand Down
107 changes: 107 additions & 0 deletions lib/Federation/Proxy/TalkV1/Controller/ChatController.php
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,113 @@ public function sendMessage(Room $room, Participant $participant, string $messag
);
}

/**
* @return DataResponse<Http::STATUS_OK|Http::STATUS_NOT_MODIFIED, TalkChatMessageWithParent[], array{X-Chat-Last-Common-Read?: numeric-string, X-Chat-Last-Given?: numeric-string}>
* @throws CannotReachRemoteException
* @throws RemoteClientException
*
* 200: Messages returned
* 304: No messages
*
* @see \OCA\Talk\Controller\ChatController::getMessageContext()
*/
public function receiveMessages(
Room $room,
Participant $participant,
int $lookIntoFuture,
int $limit,
int $lastKnownMessageId,
int $lastCommonReadId,
int $timeout,
int $setReadMarker,
int $includeLastKnown,
int $noStatusUpdate,
int $markNotificationsAsRead): DataResponse {

// FIXME
// Poor-mans timeout, should later on cancel/trigger earlier,
// when we received a OCM message notifying us about a chat message
sleep($timeout);

$proxy = $this->proxy->get(
$participant->getAttendee()->getInvitedCloudId(),
$participant->getAttendee()->getAccessToken(),
$room->getRemoteServer() . '/ocs/v2.php/apps/spreed/api/v1/chat/' . $room->getRemoteToken(),
[
'lookIntoFuture' => $lookIntoFuture,
'limit' => $limit,
'lastKnownMessageId' => $lastKnownMessageId,
'lastCommonReadId' => $lastCommonReadId,
'timeout' => 0,
'setReadMarker' => $setReadMarker,
'includeLastKnown' => $includeLastKnown,
'noStatusUpdate' => $noStatusUpdate,
'markNotificationsAsRead' => $markNotificationsAsRead,
],
);

if ($proxy->getStatusCode() === Http::STATUS_NOT_MODIFIED) {
return new DataResponse([], Http::STATUS_NOT_MODIFIED);
}


$headers = [];
if ($proxy->getHeader('X-Chat-Last-Common-Read')) {
$headers['X-Chat-Last-Common-Read'] = (string) (int) $proxy->getHeader('X-Chat-Last-Common-Read');
}
if ($proxy->getHeader('X-Chat-Last-Given')) {
$headers['X-Chat-Last-Given'] = (string) (int) $proxy->getHeader('X-Chat-Last-Given');
}

/** @var TalkChatMessageWithParent[] $data */
$data = $this->proxy->getOCSData($proxy);
/** @var TalkChatMessageWithParent[] $data */
$data = $this->userConverter->convertAttendees($room, $data, 'actorType', 'actorId', 'actorDisplayName');

return new DataResponse($data, Http::STATUS_OK, $headers);
}

/**
* @return DataResponse<Http::STATUS_OK|Http::STATUS_NOT_MODIFIED, TalkChatMessageWithParent[], array{X-Chat-Last-Common-Read?: numeric-string, X-Chat-Last-Given?: numeric-string}>
* @throws CannotReachRemoteException
* @throws RemoteClientException
*
* 200: Message context returned
* 304: No messages
*
* @see \OCA\Talk\Controller\ChatController::getMessageContext()
*/
public function getMessageContext(Room $room, Participant $participant, int $messageId, int $limit): DataResponse {
$proxy = $this->proxy->get(
$participant->getAttendee()->getInvitedCloudId(),
$participant->getAttendee()->getAccessToken(),
$room->getRemoteServer() . '/ocs/v2.php/apps/spreed/api/v1/chat/' . $room->getRemoteToken() . '/' . $messageId . '/context',
[
'limit' => $limit,
],
);

if ($proxy->getStatusCode() === Http::STATUS_NOT_MODIFIED) {
return new DataResponse([], Http::STATUS_NOT_MODIFIED);
}


$headers = [];
if ($proxy->getHeader('X-Chat-Last-Common-Read')) {
$headers['X-Chat-Last-Common-Read'] = (string) (int) $proxy->getHeader('X-Chat-Last-Common-Read');
}
if ($proxy->getHeader('X-Chat-Last-Given')) {
$headers['X-Chat-Last-Given'] = (string) (int) $proxy->getHeader('X-Chat-Last-Given');
}

/** @var TalkChatMessageWithParent[] $data */
$data = $this->proxy->getOCSData($proxy);
/** @var TalkChatMessageWithParent[] $data */
$data = $this->userConverter->convertAttendees($room, $data, 'actorType', 'actorId', 'actorDisplayName');

return new DataResponse($data, Http::STATUS_OK, $headers);
}

/**
* @see \OCA\Talk\Controller\ChatController::mentions()
*
Expand Down

0 comments on commit c6a028e

Please sign in to comment.