* @copyright 2018, 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\Service; use daita\MySmallPhpTools\Exceptions\MalformedArrayException; use daita\MySmallPhpTools\Traits\TArrayTools; use OCA\Social\Db\FollowsRequest; use OCA\Social\Exceptions\CacheActorDoesNotExistException; use OCA\Social\Exceptions\FollowDoesNotExistException; use OCA\Social\Exceptions\FollowSameAccountException; use OCA\Social\Exceptions\InvalidOriginException; use OCA\Social\Exceptions\InvalidResourceException; use OCA\Social\Exceptions\RedundancyLimitException; use OCA\Social\Exceptions\Request410Exception; use OCA\Social\Exceptions\RequestException; use OCA\Social\Exceptions\SocialAppConfigException; use OCA\Social\Exceptions\UnknownItemException; use OCA\Social\Exceptions\UrlCloudException; use OCA\Social\Model\ActivityPub\Activity\Follow; use OCA\Social\Model\ActivityPub\Activity\Undo; use OCA\Social\Model\ActivityPub\Actor\Person; use OCA\Social\Model\ActivityPub\OrderedCollection; use OCA\Social\Model\InstancePath; class FollowService { use TArrayTools; /** @var FollowsRequest */ private $followsRequest; /** @var ActivityService */ private $activityService; /** @var CacheActorService */ private $cacheActorService; /** @var ConfigService */ private $configService; /** @var MiscService */ private $miscService; /** @var string */ private $viewerId = ''; /** * FollowService constructor. * * @param FollowsRequest $followsRequest * @param ActivityService $activityService * @param CacheActorService $cacheActorService * @param ConfigService $configService * @param MiscService $miscService */ public function __construct( FollowsRequest $followsRequest, ActivityService $activityService, CacheActorService $cacheActorService, ConfigService $configService, MiscService $miscService ) { $this->followsRequest = $followsRequest; $this->activityService = $activityService; $this->cacheActorService = $cacheActorService; $this->configService = $configService; $this->miscService = $miscService; } /** * @param string $viewerId */ public function setViewerId(string $viewerId) { $this->viewerId = $viewerId; $this->followsRequest->setViewerId($viewerId); } public function getViewerId(): string { return $this->viewerId; } /** * @param Person $actor * @param string $account * * @throws CacheActorDoesNotExistException * @throws FollowSameAccountException * @throws InvalidResourceException * @throws MalformedArrayException * @throws RedundancyLimitException * @throws Request410Exception * @throws RequestException * @throws SocialAppConfigException * @throws UrlCloudException * @throws UnknownItemException * @throws InvalidOriginException * @throws \Exception */ public function followAccount(Person $actor, string $account) { $remoteActor = $this->cacheActorService->getFromAccount($account); if ($remoteActor->getId() === $actor->getId()) { throw new FollowSameAccountException("Don't follow yourself, be your own lead"); } $follow = new Follow(); $follow->setUrlCloud($this->configService->getCloudAddress()); $follow->generateUniqueId(); $follow->setActorId($actor->getId()); $follow->setObjectId($remoteActor->getId()); $follow->setFollowId($remoteActor->getFollowers()); try { $this->followsRequest->getByPersons($actor->getId(), $remoteActor->getId()); } catch (FollowDoesNotExistException $e) { $this->followsRequest->save($follow); // TODO - Remove this auto-accepted. $this->followsRequest->accepted($follow); $follow->addInstancePath( new InstancePath( $remoteActor->getInbox(), InstancePath::TYPE_INBOX, InstancePath::PRIORITY_TOP ) ); $this->activityService->request($follow); } } /** * @param Person $actor * @param string $account * * @throws CacheActorDoesNotExistException * @throws InvalidResourceException * @throws MalformedArrayException * @throws RedundancyLimitException * @throws Request410Exception * @throws RequestException * @throws SocialAppConfigException * @throws UnknownItemException * @throws \Exception */ public function unfollowAccount(Person $actor, string $account) { $remoteActor = $this->cacheActorService->getFromAccount($account); try { $follow = $this->followsRequest->getByPersons($actor->getId(), $remoteActor->getId()); $this->followsRequest->delete($follow); $undo = new Undo(); $follow->setParent($undo); $undo->setObject($follow); $undo->setActorId($actor->getId()); $undo->addInstancePath( new InstancePath( $remoteActor->getInbox(), InstancePath::TYPE_INBOX, InstancePath::PRIORITY_TOP ) ); $this->activityService->request($undo); } catch (FollowDoesNotExistException $e) { } } /** * @param Person $local * @param Person $actor * * @return array */ public function getLinksBetweenPersons(Person $local, Person $actor): array { $links = [ 'follower' => false, 'following' => false ]; try { $this->followsRequest->getByPersons($local->getId(), $actor->getId()); $links['following'] = true; } catch (FollowDoesNotExistException $e) { } try { $this->followsRequest->getByPersons($actor->getId(), $local->getId()); $links['follower'] = true; } catch (FollowDoesNotExistException $e) { } return $links; } /** * @param Person $actor * * @return Person[] */ public function getFollowers(Person $actor): array { return $this->followsRequest->getFollowersByActorId($actor->getId()); } /** * @param Person $actor * * @return OrderedCollection */ public function getFollowersCollection(Person $actor): OrderedCollection { $collection = new OrderedCollection(); $collection->setId($actor->getFollowers()); $collection->setTotalItems(20); $collection->setFirst('...'); return $collection; } /** * @param Person $actor * * @return Person[] */ public function getFollowing(Person $actor): array { return $this->followsRequest->getFollowingByActorId($actor->getId()); } /** * @param Person $actor * * @return OrderedCollection */ public function getFollowingCollection(Person $actor): OrderedCollection { $collection = new OrderedCollection(); // $collection->setId($actor->getFollowers()); // $collection->setTotalItems(20); // $collection->setFirst('...'); return $collection; } }