diff --git a/lib/Interfaces/Object/FollowInterface.php b/lib/Interfaces/Object/FollowInterface.php index 648f0c8e..f831acd7 100644 --- a/lib/Interfaces/Object/FollowInterface.php +++ b/lib/Interfaces/Object/FollowInterface.php @@ -185,8 +185,7 @@ class FollowInterface extends AbstractActivityPubInterface implements IActivityP */ private function generateNotification(Follow $follow): void { /** @var SocialAppNotificationInterface $notificationInterface */ - $notificationInterface = - AP::$activityPub->getInterfaceFromType(SocialAppNotification::TYPE); + $notificationInterface = AP::$activityPub->getInterfaceFromType(SocialAppNotification::TYPE); try { $follower = $this->cacheActorService->getFromId($follow->getActorId()); diff --git a/lib/Interfaces/Object/MentionInterface.php b/lib/Interfaces/Object/MentionInterface.php new file mode 100644 index 00000000..5f116eb5 --- /dev/null +++ b/lib/Interfaces/Object/MentionInterface.php @@ -0,0 +1,41 @@ + + * @copyright 2023, Maxence Lange + * @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\Social\Interfaces\Object; + +use OCA\Social\Interfaces\Activity\AbstractActivityPubInterface; +use OCA\Social\Interfaces\IActivityPubInterface; +use OCA\Social\Tools\Traits\TArrayTools; + +class MentionInterface extends AbstractActivityPubInterface implements IActivityPubInterface { + use TArrayTools; + + public function __construct() { + } +} diff --git a/lib/Interfaces/Object/NoteInterface.php b/lib/Interfaces/Object/NoteInterface.php index 646c729f..161a79b6 100644 --- a/lib/Interfaces/Object/NoteInterface.php +++ b/lib/Interfaces/Object/NoteInterface.php @@ -31,25 +31,40 @@ declare(strict_types=1); namespace OCA\Social\Interfaces\Object; +use OCA\Social\AP; +use OCA\Social\Db\CacheActorsRequest; use OCA\Social\Db\StreamRequest; +use OCA\Social\Exceptions\CacheActorDoesNotExistException; use OCA\Social\Exceptions\InvalidOriginException; use OCA\Social\Exceptions\ItemAlreadyExistsException; use OCA\Social\Exceptions\ItemNotFoundException; use OCA\Social\Exceptions\StreamNotFoundException; use OCA\Social\Interfaces\Activity\AbstractActivityPubInterface; use OCA\Social\Interfaces\IActivityPubInterface; +use OCA\Social\Interfaces\Internal\SocialAppNotificationInterface; use OCA\Social\Model\ActivityPub\ACore; use OCA\Social\Model\ActivityPub\Activity\Create; use OCA\Social\Model\ActivityPub\Activity\Delete; +use OCA\Social\Model\ActivityPub\Internal\SocialAppNotification; +use OCA\Social\Model\ActivityPub\Object\Mention; use OCA\Social\Model\ActivityPub\Object\Note; use OCA\Social\Service\PushService; +use OCA\Social\Tools\Traits\TArrayTools; class NoteInterface extends AbstractActivityPubInterface implements IActivityPubInterface { + use TArrayTools; + private StreamRequest $streamRequest; + private CacheActorsRequest $cacheActorsRequest; private PushService $pushService; - public function __construct(StreamRequest $streamRequest, PushService $pushService) { + public function __construct( + StreamRequest $streamRequest, + CacheActorsRequest $cacheActorsRequest, + PushService $pushService + ) { $this->streamRequest = $streamRequest; + $this->cacheActorsRequest = $cacheActorsRequest; $this->pushService = $pushService; } @@ -91,6 +106,7 @@ class NoteInterface extends AbstractActivityPubInterface implements IActivityPub } catch (StreamNotFoundException $e) { $this->streamRequest->save($note); $this->updateDetails($note); + $this->generateNotification($note); $this->pushService->onNewStream($note->getId()); } } @@ -115,4 +131,40 @@ class NoteInterface extends AbstractActivityPubInterface implements IActivityPub } catch (StreamNotFoundException $e) { } } + + private function generateNotification(Note $note): void { + $mentions = $note->getTags('Mention'); + if (empty($mentions)) { + return; + } + + /** @var SocialAppNotificationInterface $notificationInterface */ + $notificationInterface = AP::$activityPub->getInterfaceFromType(SocialAppNotification::TYPE); + $post = $this->streamRequest->getStreamById($note->getId(), false, ACore::FORMAT_LOCAL); + + foreach ($mentions as $mention) { + try { + $recipient = $this->cacheActorsRequest->getFromId($this->get('href', $mention)); + if (!$recipient->isLocal()) { // only interested on local + throw new CacheActorDoesNotExistException(); + } + } catch (CacheActorDoesNotExistException $e) { + continue; + } + + /** @var SocialAppNotification $notification */ + $notification = AP::$activityPub->getItemFromType(SocialAppNotification::TYPE); + $notification->setDetailItem('post', $post); + $notification->addDetail('account', $post->getActor()->getAccount()); + $notification->setAttributedTo($recipient->getId()) + ->setSubType(Mention::TYPE) + ->setId($post->getId() . '/notification+mention') + ->setSummary('{account} mentioned you in a post') + ->setObjectId($post->getId()) + ->setTo($recipient->getId()) + ->setLocal(true); + + $notificationInterface->save($notification); + } + } } diff --git a/lib/Model/ActivityPub/Object/Mention.php b/lib/Model/ActivityPub/Object/Mention.php new file mode 100644 index 00000000..2c5d7d15 --- /dev/null +++ b/lib/Model/ActivityPub/Object/Mention.php @@ -0,0 +1,58 @@ + + * @copyright 2023, Maxence Lange + * @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\Social\Model\ActivityPub\Object; + +use JsonSerializable; +use OCA\Social\Model\ActivityPub\ACore; +use OCA\Social\Model\ActivityPub\Stream; + +class Mention extends Stream implements JsonSerializable { + public const TYPE = 'Mention'; + + public function __construct(ACore $parent = null) { + parent::__construct($parent); + + $this->setType(self::TYPE); + } + + public function import(array $data): void { + parent::import($data); + } + + public function importFromDatabase(array $data): void { + parent::importFromDatabase($data); + } + + public function jsonSerialize(): array { + $result = parent::jsonSerialize(); + + return $result; + } +} diff --git a/lib/Model/ActivityPub/Stream.php b/lib/Model/ActivityPub/Stream.php index 18ca8c53..ca34a237 100644 --- a/lib/Model/ActivityPub/Stream.php +++ b/lib/Model/ActivityPub/Stream.php @@ -43,6 +43,7 @@ use OCA\Social\Model\ActivityPub\Object\Document; use OCA\Social\Model\ActivityPub\Object\Follow; use OCA\Social\Model\ActivityPub\Object\Image; use OCA\Social\Model\ActivityPub\Object\Like; +use OCA\Social\Model\ActivityPub\Object\Mention; use OCA\Social\Model\Client\MediaAttachment; use OCA\Social\Model\StreamAction; use OCA\Social\Tools\IQueryRow; @@ -661,11 +662,19 @@ class Stream extends ACore implements IQueryRow, JsonSerializable { public function exportAsNotification(): array { + // TODO - implements: + // status = Someone you enabled notifications for has posted a status + // follow_request = Someone requested to follow you + // poll = A poll you have voted in or created has ended + // update = A status you boosted with has been edited switch ($this->getSubType()) { case Like::TYPE: $type = 'favourite'; break; case Announce::TYPE: + $type = 'reblog'; + break; + case Mention::TYPE: $type = 'mention'; break; case Follow::TYPE: