diff options
| author | winter | 2024-12-19 20:59:18 +0000 |
|---|---|---|
| committer | winter | 2024-12-19 20:59:18 +0000 |
| commit | 72b07245f44c73ed41bfcca2465a9ee34426a922 (patch) | |
| tree | 57372703100ad792c95c665a568aabe8a763bfdf /Digitigrade/Model | |
| parent | 1e070a7a0097c74f8ac30678d39267f19c76f9f6 (diff) | |
implement follow relationships (untested)
does NOT implement the actual behaviour behind follows (receiving posts and such)
Diffstat (limited to 'Digitigrade/Model')
| -rw-r--r-- | Digitigrade/Model/Actor.php | 67 | ||||
| -rw-r--r-- | Digitigrade/Model/FetchableModel.php | 6 | ||||
| -rw-r--r-- | Digitigrade/Model/FollowRelation.php | 37 | ||||
| -rw-r--r-- | Digitigrade/Model/FollowRelationStatus.php | 7 |
4 files changed, 112 insertions, 5 deletions
diff --git a/Digitigrade/Model/Actor.php b/Digitigrade/Model/Actor.php index 4f3aab2..fa08463 100644 --- a/Digitigrade/Model/Actor.php +++ b/Digitigrade/Model/Actor.php @@ -1,7 +1,10 @@ <?php namespace Digitigrade\Model; -class Actor extends PushableModel { +use Digitigrade\RpcException; +use Digitigrade\RpcReceiver; + +class Actor extends PushableModel implements RpcReceiver { public ?int $id; public string $uri; public bool $isLocal = false; @@ -83,6 +86,64 @@ class Actor extends PushableModel { return ActorWebfinger::findByAcct($acct, $autoSave, $forceRefetch)->actor; } + public static function findByRequestHeaders(): ?self { + $instance = Instance::findByRequestHeaders(); + if ($instance == null || !isset($_SERVER['HTTP_X_PAWPUB_ACTOR'])) + return null; + $uri = $_SERVER['HTTP_X_PAWPUB_ACTOR']; + if (hostname_from_uri($uri) != $instance->domain) + return null; + return self::findByUri($uri); + } + + protected function rpcCall(string $method, array $args) { + switch ($method) { + case 'follow': // $this is the target, $args[0] is the initiator + $status = $this->requestToFollow ? FollowRelationStatus::PENDING : FollowRelationStatus::ACTIVE; + if (!$this->isLocal) { + $endpoint = $this->endpoints->follow ?? null; + if (!isset($endpoint, $this->endpoints->unfollow)) { + throw new RpcException("Actor `$this->uri` has no (un)follow endpoint"); + } + $result = send_request_authenticated($endpoint, method: 'POST', actingAs: $args[0]); + if (!str_contains($result->headers[0], ' 20')) { + throw new RpcException('remote call did not indicate success: ' . $result->headers[0]); + } + } + FollowRelation::create($args[0], $this, $status); + return $status; + + case 'unfollow': + if (!$this->isLocal) { + $endpoint = $this->endpoints->unfollow ?? null; + if (!isset($endpoint, $this->endpoints->follow)) { + throw new RpcException("Actor `$this->uri` has no (un)follow endpoint"); + } + $result = send_request_authenticated($endpoint, method: 'POST', actingAs: $args[0]); + if (!str_contains($result->headers[0], ' 20')) { + throw new RpcException('remote call did not indicate success: ' . $result->headers[0]); + } + } + FollowRelation::findByActors($args[0], $this)->remove(); + + default: + throw new \RuntimeException("Actor rpc method $method not (yet?) implemented"); + } + } + + /** + * Tries to follow (or send a follow request to) another actor + * @param Actor $target the actor to follow + * @return FollowRelationStatus PENDING if a request was sent or ACTIVE if the follow went through immediately + */ + public function follow(self $target): FollowRelationStatus { + return $target->rpcCall('follow', [$this]); + } + + public function unfollow(self $target) { + $target->rpcCall('unfollow', [$this]); + } + public function jsonSerialize(): array { return [ 'type' => 'actor', @@ -97,7 +158,9 @@ class Actor extends PushableModel { 'automated' => $this->automated, 'endpoints' => [ 'basicFeed' => path_to_uri("/user/$this->handle/basicFeed"), - 'fullFeed' => path_to_uri("/user/$this->handle/fullFeed") + 'fullFeed' => path_to_uri("/user/$this->handle/fullFeed"), + 'follow' => path_to_uri("/user/$this->handle/follow"), + 'unfollow' => path_to_uri("/user/$this->handle/unfollow"), ] ]; } diff --git a/Digitigrade/Model/FetchableModel.php b/Digitigrade/Model/FetchableModel.php index 1120823..5e4d1e8 100644 --- a/Digitigrade/Model/FetchableModel.php +++ b/Digitigrade/Model/FetchableModel.php @@ -55,10 +55,10 @@ abstract class FetchableModel extends Model implements RemoteFetchable { protected static function fetchFromRemote(string $uri, bool $autoSave): ?static { // fetch the object // janky hack: if this is an Instance then avoid recursively fetching itself - $data = @get_remote_object_authenticated($uri, static::class == Instance::class); - if ($data === false) + $result = @send_request_authenticated($uri, static::class == Instance::class); + if (!str_contains($result->headers[0], '200')) return null; - $data = json_decode($data); + $data = json_decode($result->body); if ($data === null) return null; diff --git a/Digitigrade/Model/FollowRelation.php b/Digitigrade/Model/FollowRelation.php new file mode 100644 index 0000000..fc43a6f --- /dev/null +++ b/Digitigrade/Model/FollowRelation.php @@ -0,0 +1,37 @@ +<?php +namespace Digitigrade\Model; + +use Digitigrade\Model; + +class FollowRelation extends Model { + public Actor $subject; + public Actor $object; + public FollowRelationStatus $status; + + /** + * Creates and saves a new follower/following relationship + * @param Actor $subject the follower + * @param Actor $object the followee + * @param FollowRelationStatus $status whether the relationship is pending or active + * @return FollowRelation + */ + public static function create(Actor $subject, Actor $object, FollowRelationStatus $status): self { + $rel = new self(); + $rel->subject = $subject; + $rel->object = $object; + $rel->status = $status; + $rel->save(); + return $rel; + } + + protected function getUpdateWhereClause(\PDO $db): ?string { + if (self::findWhere('subject = ? and object = ?', [$this->subject->id, $this->object->id]) != null) { + return 'subject = ' . $this->subject->id . ' and object = ' . $this->object->id; + } + return null; + } + + public static function findByActors(Actor $subject, Actor $object): ?self { + return self::findWhere('subject = ? and object = ?', [$subject->id, $object->id]); + } +}
\ No newline at end of file diff --git a/Digitigrade/Model/FollowRelationStatus.php b/Digitigrade/Model/FollowRelationStatus.php new file mode 100644 index 0000000..24ba34b --- /dev/null +++ b/Digitigrade/Model/FollowRelationStatus.php @@ -0,0 +1,7 @@ +<?php +namespace Digitigrade\Model; + +enum FollowRelationStatus: string { + case PENDING = 'pending'; + case ACTIVE = 'active'; +}
\ No newline at end of file |
