Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(activity): Add federated participants to activity and call summary #12874

Merged
merged 5 commits into from
Aug 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 19 additions & 6 deletions docs/events.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,30 @@ See the general [Nextcloud Developers - Events](https://docs.nextcloud.com/serve
* After event: `OCA\Talk\Events\LobbyModifiedEvent`
* Since: 18.0.0

### Call started

* Before event: `OCA\Talk\Events\BeforeCallStartedEvent`
* After event: `OCA\Talk\Events\CallStartedEvent`
The after event might be skipped if the request lost the race to update the database.
A parallel request will have triggered the before and after events in the meantime.
* Since: 20.0.0

### Call ended for everyone

* Before event: `OCA\Talk\Events\BeforeCallEndedForEveryoneEvent`
* After event: `OCA\Talk\Events\CallEndedForEveryoneEvent`
* Since: 18.0.0
* Since: 20.0.0 Extends the abstract `ACallEndedEvent`

### Call ended

When the last participant is leaving the call, the session expired or the participant was removed.

* Before event: `OCA\Talk\Events\BeforeCallEndedEvent`
* After event: `OCA\Talk\Events\CallEndedEvent`
The after event might be skipped if the request lost the race to update the database.
A parallel request will have triggered the before and after events in the meantime.
* Since: 20.0.0

### Conversation password verify

Expand All @@ -47,12 +66,6 @@ Allows to verify a password and set a redirect URL for the invalid case
* Event: `OCA\Talk\Events\RoomPasswordVerifyEvent`
* Since: 18.0.0

### Active since modified

* Before event: `OCA\Talk\Events\BeforeActiveSinceModifiedEvent`
* After event: `OCA\Talk\Events\ActiveSinceModifiedEvent`
* Since: 20.0.0

## Participant related events

### Attendees added
Expand Down
96 changes: 21 additions & 75 deletions lib/Activity/Listener.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,18 @@
namespace OCA\Talk\Activity;

use OCA\Talk\Chat\ChatManager;
use OCA\Talk\Events\AParticipantModifiedEvent;
use OCA\Talk\Events\ACallEndedEvent;
use OCA\Talk\Events\ARoomEvent;
use OCA\Talk\Events\AttendeeRemovedEvent;
use OCA\Talk\Events\AttendeesAddedEvent;
use OCA\Talk\Events\BeforeCallEndedForEveryoneEvent;
use OCA\Talk\Events\ParticipantModifiedEvent;
use OCA\Talk\Events\SessionLeftRoomEvent;
use OCA\Talk\Events\CallEndedEvent;
use OCA\Talk\Events\CallEndedForEveryoneEvent;
use OCA\Talk\Model\Attendee;
use OCA\Talk\Participant;
use OCA\Talk\Room;
use OCA\Talk\Service\ParticipantService;
use OCA\Talk\Service\RecordingService;
use OCA\Talk\Service\RoomService;
use OCP\Activity\Exceptions\InvalidValueException;
use OCP\Activity\IManager;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\EventDispatcher\Event;
Expand Down Expand Up @@ -53,85 +52,32 @@ public function handle(Event $event): void {
}

match (get_class($event)) {
BeforeCallEndedForEveryoneEvent::class => $this->generateCallActivity($event->getRoom(), true, $event->getActor()),
SessionLeftRoomEvent::class,
AttendeeRemovedEvent::class => $this->generateCallActivity($event->getRoom()),
ParticipantModifiedEvent::class => $this->handleParticipantModified($event),
CallEndedEvent::class,
CallEndedForEveryoneEvent::class => $this->generateCallActivity($event),
AttendeesAddedEvent::class => $this->generateInvitationActivity($event->getRoom(), $event->getAttendees()),
};
}

protected function setActive(ParticipantModifiedEvent $event): void {
if ($event->getProperty() !== AParticipantModifiedEvent::PROPERTY_IN_CALL) {
return;
}

if ($event->getOldValue() !== Participant::FLAG_DISCONNECTED
|| $event->getNewValue() === Participant::FLAG_DISCONNECTED) {
return;
}

$participant = $event->getParticipant();
$this->roomService->setActiveSince(
$event->getRoom(),
$this->timeFactory->getDateTime(),
$participant->getSession() ? $participant->getSession()->getInCall() : Participant::FLAG_DISCONNECTED
);
}

protected function handleParticipantModified(ParticipantModifiedEvent $event): void {
if ($event->getProperty() !== AParticipantModifiedEvent::PROPERTY_IN_CALL) {
return;
}

if ($event->getOldValue() === Participant::FLAG_DISCONNECTED
|| $event->getNewValue() !== Participant::FLAG_DISCONNECTED) {
$this->setActive($event);
return;
}

if ($event->getDetail(AParticipantModifiedEvent::DETAIL_IN_CALL_END_FOR_EVERYONE)) {
// The call activity was generated already if the call is ended
// for everyone
return;
}

$this->generateCallActivity($event->getRoom());
}

/**
* Call activity: "You attended a call with {user1} and {user2}"
*
* @param Room $room
* @param bool $endForEveryone
* @param Participant|null $actor
* @return bool True if activity was generated, false otherwise
*/
protected function generateCallActivity(Room $room, bool $endForEveryone = false, ?Participant $actor = null): bool {
$activeSince = $room->getActiveSince();
if (!$activeSince instanceof \DateTime || (!$endForEveryone && $this->participantService->hasActiveSessionsInCall($room))) {
return false;
}
protected function generateCallActivity(ACallEndedEvent $event): void {
$room = $event->getRoom();
$actor = $event->getActor();
$activeSince = $event->getOldValue();

$duration = $this->timeFactory->getTime() - $activeSince->getTimestamp();
$userIds = $this->participantService->getParticipantUserIds($room, $activeSince);
$cloudIds = $this->participantService->getParticipantActorIdsByActorType($room, [Attendee::ACTOR_FEDERATED_USERS], $activeSince);
$numGuests = $this->participantService->getGuestCount($room, $activeSince);

$message = 'call_ended';
if ($endForEveryone) {
if ($event instanceof CallEndedForEveryoneEvent) {
$message = 'call_ended_everyone';
} elseif (($room->getType() === Room::TYPE_ONE_TO_ONE || $room->getType() === Room::TYPE_ONE_TO_ONE_FORMER) && \count($userIds) === 1) {
$message = 'call_missed';
}

if (!$this->roomService->resetActiveSince($room)) {
// Race-condition, the room was already reset.
return false;
}

if ($room->getCallRecording() !== Room::RECORDING_NONE && $room->getCallRecording() !== Room::RECORDING_FAILED) {
$this->recordingService->stop($room);
}
if ($actor instanceof Participant) {
$actorId = $actor->getAttendee()->getActorId();
$actorType = $actor->getAttendee()->getActorType();
Expand All @@ -143,43 +89,43 @@ protected function generateCallActivity(Room $room, bool $endForEveryone = false
'message' => $message,
'parameters' => [
'users' => $userIds,
'cloudIds' => $cloudIds,
'guests' => $numGuests,
'duration' => $duration,
],
]), $this->timeFactory->getDateTime(), false);

if (empty($userIds)) {
return false;
return;
}

$event = $this->activityManager->generateEvent();
$activity = $this->activityManager->generateEvent();
try {
$event->setApp('spreed')
$activity->setApp('spreed')
->setType('spreed')
->setAuthor('')
->setObject('room', $room->getId())
->setTimestamp($this->timeFactory->getTime())
->setSubject('call', [
'room' => $room->getId(),
'users' => $userIds,
'cloudIds' => $cloudIds,
'guests' => $numGuests,
'duration' => $duration,
]);
} catch (\InvalidArgumentException $e) {
} catch (InvalidValueException $e) {
$this->logger->error($e->getMessage(), ['exception' => $e]);
return false;
return;
}

foreach ($userIds as $userId) {
try {
$event->setAffectedUser($userId);
$this->activityManager->publish($event);
$activity->setAffectedUser($userId);
$this->activityManager->publish($activity);
} catch (\Throwable $e) {
$this->logger->error($e->getMessage(), ['exception' => $e]);
}
}

return true;
}

/**
Expand Down
23 changes: 23 additions & 0 deletions lib/Activity/Provider/Base.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,17 @@
namespace OCA\Talk\Activity\Provider;

use OCA\Talk\Config;
use OCA\Talk\Exceptions\ParticipantNotFoundException;
use OCA\Talk\Manager;
use OCA\Talk\Model\Attendee;
use OCA\Talk\Room;
use OCA\Talk\Service\AvatarService;
use OCA\Talk\Service\ParticipantService;
use OCP\Activity\Exceptions\UnknownActivityException;
use OCP\Activity\IEvent;
use OCP\Activity\IManager;
use OCP\Activity\IProvider;
use OCP\Federation\ICloudIdManager;
use OCP\IL10N;
use OCP\IURLGenerator;
use OCP\IUser;
Expand All @@ -30,6 +34,8 @@ public function __construct(
protected Config $config,
protected IManager $activityManager,
protected IUserManager $userManager,
protected ICloudIdManager $cloudIdManager,
protected ParticipantService $participantService,
protected AvatarService $avatarService,
protected Manager $manager,
) {
Expand Down Expand Up @@ -117,4 +123,21 @@ protected function getUser(string $uid): array {
'name' => $this->userManager->getDisplayName($uid) ?? $uid,
];
}

protected function getRemoteUser(Room $room, string $federationId): array {
$cloudId = $this->cloudIdManager->resolveCloudId($federationId);
$displayName = $cloudId->getDisplayId();
try {
$participant = $this->participantService->getParticipantByActor($room, Attendee::ACTOR_FEDERATED_USERS, $federationId);
$displayName = $participant->getAttendee()->getDisplayName();
} catch (ParticipantNotFoundException) {
}

return [
'type' => 'user',
'id' => $cloudId->getUser(),
'name' => $displayName,
'server' => $cloudId->getRemote(),
];
}
}
42 changes: 31 additions & 11 deletions lib/Activity/Provider/Call.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

namespace OCA\Talk\Activity\Provider;

use OCA\Talk\Exceptions\RoomNotFoundException;
use OCA\Talk\Room;
use OCP\Activity\Exceptions\UnknownActivityException;
use OCP\Activity\IEvent;
use OCP\IL10N;
Expand All @@ -28,16 +30,15 @@ public function parse($language, IEvent $event, ?IEvent $previousEvent = null):
$l = $this->languageFactory->get('spreed', $language);
$parameters = $event->getSubjectParameters();

// $roomParameter = $this->getFormerRoom($l, (int) $parameters['room']);
// try {
// $room = $this->manager->getRoomById((int) $parameters['room']);
// $roomParameter = $this->getRoom($l, $room);
// } catch (RoomNotFoundException $e) {
// }
try {
$room = $this->manager->getRoomForUser((int) $parameters['room'], $this->activityManager->getCurrentUserId());
} catch (RoomNotFoundException) {
$room = null;
}

$result = $this->parseCall($event, $l);
$result = $this->parseCall($room, $event, $l);
$result['subject'] .= ' ' . $this->getDuration($l, (int) $parameters['duration']);
// $result['params']['call'] = $roomParameter;
// $result['params']['call'] = $roomParameter;
$this->setSubjects($event, $result['subject'], $result['params']);
} else {
throw new UnknownActivityException('subject');
Expand All @@ -61,7 +62,7 @@ protected function getDuration(IL10N $l, int $seconds): string {
return $l->t('(Duration %s)', $duration);
}

protected function parseCall(IEvent $event, IL10N $l): array {
protected function parseCall(?Room $room, IEvent $event, IL10N $l): array {
$parameters = $event->getSubjectParameters();

$currentUser = array_search($this->activityManager->getCurrentUserId(), $parameters['users'], true);
Expand All @@ -71,8 +72,23 @@ protected function parseCall(IEvent $event, IL10N $l): array {
unset($parameters['users'][$currentUser]);
sort($parameters['users']);

$numUsers = \count($parameters['users']);
if (!isset($parameters['cloudIds'])) {
// Compatibility with old messages
$parameters['cloudIds'] = [];
}
sort($parameters['users']);
sort($parameters['cloudIds']);

$numUsers = $numRealUsers = count($parameters['users']);

// Without room, we can not resolve cloudIds, so we list them as guests instead
if (!$room instanceof Room) {
$numUsers += count($parameters['cloudIds']);
} else {
$parameters['guests'] += count($parameters['cloudIds']);
}
$displayedUsers = $numUsers;

switch ($numUsers) {
case 0:
$subject = $l->t('You attended a call with {user1}');
Expand Down Expand Up @@ -124,7 +140,11 @@ protected function parseCall(IEvent $event, IL10N $l): array {

$params = [];
for ($i = 1; $i <= $displayedUsers; $i++) {
$params['user' . $i] = $this->getUser($parameters['users'][$i - 1]);
if ($i <= $numRealUsers) {
$params['user' . $i] = $this->getUser($parameters['users'][$i - 1]);
} else {
$params['user' . $i] = $this->getRemoteUser($room, $parameters['cloudIds'][$i - $numRealUsers - 1]);
}
}

return [
Expand Down
Loading
Loading