UPSTREAM BASELINE: nextcloud/spreed v22.0.12 (без изменений)
Type checking / changes (push) Has been cancelled
Type checking / test (push) Has been cancelled
Type checking / typescript-summary (push) Has been cancelled
Node tests / changes (push) Has been cancelled
Node tests / test (push) Has been cancelled
Node tests / test-summary (push) Has been cancelled

Источник: https://github.com/nextcloud/spreed/archive/refs/tags/v22.0.12.tar.gz
С этого коммита ветка официального Nextcloud Talk отрезана (решение владельца 2026-07-06).
Все дальнейшие изменения — только наши; версии релизов: 22.0.12-f7.N.
This commit is contained in:
2026-07-06 14:07:50 +00:00
commit 01acfa3b40
1716 changed files with 613013 additions and 0 deletions
@@ -0,0 +1,47 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Talk\Federation\Proxy\TalkV1\Notifier;
use OCA\Talk\Events\BeforeRoomDeletedEvent;
use OCA\Talk\Federation\BackendNotifier;
use OCA\Talk\Model\Attendee;
use OCA\Talk\Service\ParticipantService;
use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventListener;
use OCP\Federation\ICloudIdManager;
/**
* @template-implements IEventListener<Event>
*/
class BeforeRoomDeletedListener implements IEventListener {
public function __construct(
protected BackendNotifier $backendNotifier,
protected ParticipantService $participantService,
protected ICloudIdManager $cloudIdManager,
) {
}
#[\Override]
public function handle(Event $event): void {
if (!$event instanceof BeforeRoomDeletedEvent) {
return;
}
$participants = $this->participantService->getParticipantsByActorType($event->getRoom(), Attendee::ACTOR_FEDERATED_USERS);
foreach ($participants as $participant) {
$cloudId = $this->cloudIdManager->resolveCloudId($participant->getAttendee()->getActorId());
$this->backendNotifier->sendRemoteUnShare(
$cloudId->getRemote(),
$participant->getAttendee()->getId(),
$participant->getAttendee()->getAccessToken(),
);
}
}
}
@@ -0,0 +1,41 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Talk\Federation\Proxy\TalkV1\Notifier;
use OCA\Talk\Events\AttendeeRemovedEvent;
use OCA\Talk\Model\Attendee;
use OCA\Talk\Model\RetryNotificationMapper;
use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventListener;
/**
* @template-implements IEventListener<Event>
*/
class CancelRetryOCMListener implements IEventListener {
public function __construct(
protected RetryNotificationMapper $retryNotificationMapper,
) {
}
#[\Override]
public function handle(Event $event): void {
if (!$event instanceof AttendeeRemovedEvent) {
return;
}
$attendee = $event->getAttendee();
if ($attendee->getActorType() !== Attendee::ACTOR_FEDERATED_USERS) {
return;
}
$this->retryNotificationMapper->deleteByProviderId(
(string)$event->getAttendee()->getId()
);
}
}
@@ -0,0 +1,128 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Talk\Federation\Proxy\TalkV1\Notifier;
use OCA\Talk\Chat\ChatManager;
use OCA\Talk\Chat\MessageParser;
use OCA\Talk\Events\AAttendeeRemovedEvent;
use OCA\Talk\Events\ASystemMessageSentEvent;
use OCA\Talk\Events\ChatMessageSentEvent;
use OCA\Talk\Events\SystemMessageSentEvent;
use OCA\Talk\Events\SystemMessagesMultipleSentEvent;
use OCA\Talk\Federation\BackendNotifier;
use OCA\Talk\Model\Attendee;
use OCA\Talk\Model\ProxyCacheMessage;
use OCA\Talk\Service\ParticipantService;
use OCP\Comments\IComment;
use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventListener;
use OCP\Federation\ICloudIdManager;
use OCP\L10N\IFactory;
/**
* @template-implements IEventListener<Event>
*/
class MessageSentListener implements IEventListener {
public function __construct(
protected BackendNotifier $backendNotifier,
protected ParticipantService $participantService,
protected ICloudIdManager $cloudIdManager,
protected MessageParser $messageParser,
protected IFactory $l10nFactory,
protected ChatManager $chatManager,
) {
}
#[\Override]
public function handle(Event $event): void {
if (!$event instanceof ChatMessageSentEvent
&& !$event instanceof SystemMessageSentEvent
&& !$event instanceof SystemMessagesMultipleSentEvent) {
return;
}
// FIXME once we store/cache the info skip this if the room has no federation participant
// if (!$event->getRoom()->hasFederatedParticipants()) {
// return;
// }
// Try to have as neutral as possible messages
$l = $this->l10nFactory->get('spreed', 'en', 'en');
$chatMessage = $this->messageParser->createMessage($event->getRoom(), null, $event->getComment(), $l);
$this->messageParser->parseMessage($chatMessage);
$systemMessage = $chatMessage->getMessageType() === ChatManager::VERB_SYSTEM ? $chatMessage->getMessageRaw() : '';
if ($systemMessage !== 'message_edited'
&& $systemMessage !== 'message_deleted'
&& $event instanceof ASystemMessageSentEvent
&& $event->shouldSkipLastActivityUpdate()) {
return;
}
if (!$chatMessage->getVisibility()) {
return;
}
$expireDate = $event->getComment()->getExpireDate();
$creationDate = $event->getComment()->getCreationDateTime();
$metaData = $event->getComment()->getMetaData() ?? [];
$parent = $event->getParent();
if ($parent instanceof IComment) {
$metaData[ProxyCacheMessage::METADATA_REPLY_TO_ACTOR_TYPE] = $parent->getActorType();
$metaData[ProxyCacheMessage::METADATA_REPLY_TO_ACTOR_ID] = $parent->getActorId();
$metaData[ProxyCacheMessage::METADATA_REPLY_TO_MESSAGE_ID] = (int)$parent->getId();
}
$messageData = [
'remoteMessageId' => (int)$event->getComment()->getId(),
'actorType' => $chatMessage->getActorType(),
'actorId' => $chatMessage->getActorId(),
'actorDisplayName' => $chatMessage->getActorDisplayName(),
'messageType' => $chatMessage->getMessageType(),
'systemMessage' => $systemMessage,
'expirationDatetime' => $expireDate ? $expireDate->format(\DateTime::ATOM) : '',
'message' => $chatMessage->getMessage(),
'messageParameter' => json_encode($chatMessage->getMessageParameters(), JSON_THROW_ON_ERROR),
'creationDatetime' => $creationDate->format(\DateTime::ATOM),
'metaData' => json_encode($metaData, JSON_THROW_ON_ERROR),
];
$participants = $this->participantService->getParticipantsByActorType($event->getRoom(), Attendee::ACTOR_FEDERATED_USERS);
foreach ($participants as $participant) {
$attendee = $participant->getAttendee();
$cloudId = $this->cloudIdManager->resolveCloudId($attendee->getActorId());
$lastReadMessage = $attendee->getLastReadMessage();
$lastMention = $attendee->getLastMentionMessage();
$lastMentionDirect = $attendee->getLastMentionDirect();
$unreadInfo = [
'lastReadMessage' => $lastReadMessage,
'unreadMessages' => $this->chatManager->getUnreadCount($event->getRoom(), $lastReadMessage),
'unreadMention' => $lastMention !== 0 && $lastReadMessage < $lastMention,
'unreadMentionDirect' => $lastMentionDirect !== 0 && $lastReadMessage < $lastMentionDirect,
];
$success = $this->backendNotifier->sendMessageUpdate(
$cloudId->getRemote(),
$participant->getAttendee()->getId(),
$participant->getAttendee()->getAccessToken(),
$event->getRoom()->getToken(),
$messageData,
$unreadInfo,
);
if ($success === null) {
$this->participantService->removeAttendee($event->getRoom(), $participant, AAttendeeRemovedEvent::REASON_LEFT);
}
}
}
}
@@ -0,0 +1,72 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Talk\Federation\Proxy\TalkV1\Notifier;
use OCA\Talk\Events\AAttendeeRemovedEvent;
use OCA\Talk\Events\AParticipantModifiedEvent;
use OCA\Talk\Events\ParticipantModifiedEvent;
use OCA\Talk\Federation\BackendNotifier;
use OCA\Talk\Model\Attendee;
use OCA\Talk\Participant;
use OCA\Talk\Service\ParticipantService;
use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventListener;
use OCP\Federation\ICloudId;
use OCP\Federation\ICloudIdManager;
/**
* @template-implements IEventListener<Event>
*/
class ParticipantModifiedListener implements IEventListener {
public function __construct(
protected BackendNotifier $backendNotifier,
protected ParticipantService $participantService,
protected ICloudIdManager $cloudIdManager,
) {
}
#[\Override]
public function handle(Event $event): void {
if (!$event instanceof ParticipantModifiedEvent) {
return;
}
$participant = $event->getParticipant();
if ($participant->getAttendee()->getActorType() !== Attendee::ACTOR_FEDERATED_USERS) {
return;
}
if (!in_array($event->getProperty(), [
AParticipantModifiedEvent::PROPERTY_PERMISSIONS,
AParticipantModifiedEvent::PROPERTY_RESEND_CALL,
], true)) {
return;
}
// For modifying participants we only notify the affected participant's server
$cloudId = $this->cloudIdManager->resolveCloudId($participant->getAttendee()->getActorId());
$success = $this->notifyParticipantModified($cloudId, $participant, $event);
if ($success === null) {
$this->participantService->removeAttendee($event->getRoom(), $participant, AAttendeeRemovedEvent::REASON_LEFT);
}
}
private function notifyParticipantModified(ICloudId $cloudId, Participant $participant, AParticipantModifiedEvent $event): ?bool {
return $this->backendNotifier->sendParticipantModifiedUpdate(
$cloudId->getRemote(),
$participant->getAttendee()->getId(),
$participant->getAttendee()->getAccessToken(),
$event->getRoom()->getToken(),
$event->getProperty(),
$event->getNewValue(),
$event->getOldValue(),
);
}
}
@@ -0,0 +1,154 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Talk\Federation\Proxy\TalkV1\Notifier;
use OCA\Talk\Events\AAttendeeRemovedEvent;
use OCA\Talk\Events\ALobbyModifiedEvent;
use OCA\Talk\Events\AParticipantModifiedEvent;
use OCA\Talk\Events\ARoomModifiedEvent;
use OCA\Talk\Events\CallEndedEvent;
use OCA\Talk\Events\CallEndedForEveryoneEvent;
use OCA\Talk\Events\CallStartedEvent;
use OCA\Talk\Events\LobbyModifiedEvent;
use OCA\Talk\Events\RoomModifiedEvent;
use OCA\Talk\Federation\BackendNotifier;
use OCA\Talk\Model\Attendee;
use OCA\Talk\Participant;
use OCA\Talk\Service\ParticipantService;
use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventListener;
use OCP\Federation\ICloudId;
use OCP\Federation\ICloudIdManager;
/**
* @template-implements IEventListener<Event>
*/
class RoomModifiedListener implements IEventListener {
public function __construct(
protected BackendNotifier $backendNotifier,
protected ParticipantService $participantService,
protected ICloudIdManager $cloudIdManager,
) {
}
#[\Override]
public function handle(Event $event): void {
if (!$event instanceof CallStartedEvent
&& !$event instanceof CallEndedEvent
&& !$event instanceof CallEndedForEveryoneEvent
&& !$event instanceof LobbyModifiedEvent
&& !$event instanceof RoomModifiedEvent) {
return;
}
if (!in_array($event->getProperty(), [
ARoomModifiedEvent::PROPERTY_ACTIVE_SINCE,
ARoomModifiedEvent::PROPERTY_AVATAR,
ARoomModifiedEvent::PROPERTY_CALL_RECORDING,
ARoomModifiedEvent::PROPERTY_DEFAULT_PERMISSIONS,
ARoomModifiedEvent::PROPERTY_DESCRIPTION,
ARoomModifiedEvent::PROPERTY_IN_CALL,
ARoomModifiedEvent::PROPERTY_LOBBY,
ARoomModifiedEvent::PROPERTY_MENTION_PERMISSIONS,
ARoomModifiedEvent::PROPERTY_MESSAGE_EXPIRATION,
ARoomModifiedEvent::PROPERTY_NAME,
ARoomModifiedEvent::PROPERTY_READ_ONLY,
ARoomModifiedEvent::PROPERTY_RECORDING_CONSENT,
ARoomModifiedEvent::PROPERTY_SIP_ENABLED,
ARoomModifiedEvent::PROPERTY_TYPE,
], true)) {
return;
}
if ($event->getRoom()->isFederatedConversation()) {
return;
}
$participants = $this->participantService->getParticipantsByActorType($event->getRoom(), Attendee::ACTOR_FEDERATED_USERS);
foreach ($participants as $participant) {
$cloudId = $this->cloudIdManager->resolveCloudId($participant->getAttendee()->getActorId());
if ($event instanceof CallStartedEvent) {
$success = $this->notifyCallStarted($cloudId, $participant, $event);
} elseif ($event instanceof CallEndedEvent || $event instanceof CallEndedForEveryoneEvent) {
$success = $this->notifyCallEnded($cloudId, $participant, $event);
} elseif ($event instanceof ALobbyModifiedEvent) {
$success = $this->notifyLobbyModified($cloudId, $participant, $event);
} else {
$success = $this->notifyRoomModified($cloudId, $participant, $event);
}
if ($success === null) {
$this->participantService->removeAttendee($event->getRoom(), $participant, AAttendeeRemovedEvent::REASON_LEFT);
}
}
}
private function notifyCallStarted(ICloudId $cloudId, Participant $participant, CallStartedEvent $event) {
$details = [];
if ($event->getDetail(AParticipantModifiedEvent::DETAIL_IN_CALL_SILENT)) {
$details = [AParticipantModifiedEvent::DETAIL_IN_CALL_SILENT => true];
}
return $this->backendNotifier->sendCallStarted(
$cloudId->getRemote(),
$participant->getAttendee()->getId(),
$participant->getAttendee()->getAccessToken(),
$event->getRoom()->getToken(),
$event->getProperty(),
$event->getNewValue(),
$event->getCallFlag(),
$details,
);
}
private function notifyCallEnded(ICloudId $cloudId, Participant $participant, CallEndedEvent|CallEndedForEveryoneEvent $event) {
$details = [];
if ($event instanceof CallEndedForEveryoneEvent) {
$details = [AParticipantModifiedEvent::DETAIL_IN_CALL_END_FOR_EVERYONE => true];
}
return $this->backendNotifier->sendCallEnded(
$cloudId->getRemote(),
$participant->getAttendee()->getId(),
$participant->getAttendee()->getAccessToken(),
$event->getRoom()->getToken(),
ARoomModifiedEvent::PROPERTY_ACTIVE_SINCE,
null,
Participant::FLAG_DISCONNECTED,
$details,
);
}
private function notifyLobbyModified(ICloudId $cloudId, Participant $participant, ALobbyModifiedEvent $event) {
return $this->backendNotifier->sendRoomModifiedLobbyUpdate(
$cloudId->getRemote(),
$participant->getAttendee()->getId(),
$participant->getAttendee()->getAccessToken(),
$event->getRoom()->getToken(),
$event->getProperty(),
$event->getNewValue(),
$event->getOldValue(),
$event->getLobbyTimer(),
$event->isTimerReached(),
);
}
private function notifyRoomModified(ICloudId $cloudId, Participant $participant, ARoomModifiedEvent $event) {
return $this->backendNotifier->sendRoomModifiedUpdate(
$cloudId->getRemote(),
$participant->getAttendee()->getId(),
$participant->getAttendee()->getAccessToken(),
$event->getRoom()->getToken(),
$event->getProperty(),
$event->getNewValue(),
$event->getOldValue(),
);
}
}