Browse Source

Update AP Inbox

Daniel Supernault 3 years ago
parent
commit
3c8c23a143
2 changed files with 302 additions and 15 deletions
  1. 4 1
      app/Util/ActivityPub/Helpers.php
  2. 298 14
      app/Util/ActivityPub/Inbox.php

+ 4 - 1
app/Util/ActivityPub/Helpers.php

@@ -563,9 +563,12 @@ class Helpers {
 
 			$profile = Profile::whereRemoteUrl($res['id'])->first();
 			if(!$profile) {
-				Instance::firstOrCreate([
+				$instance = Instance::firstOrCreate([
 					'domain' => $domain
 				]);
+				if($instance->wasRecentlyCreated == true) {
+					\App\Jobs\InstancePipeline\FetchNodeinfoPipeline::dispatch($instance)->onQueue('low');
+				}
 				$profileLockKey = 'helpers:profile-lock:' . hash('sha256', $res['id']);
 				$profile = Cache::lock($profileLockKey)->get(function () use($domain, $webfinger, $res, $runJobs) {
 					return DB::transaction(function() use($domain, $webfinger, $res, $runJobs) {

+ 298 - 14
app/Util/ActivityPub/Inbox.php

@@ -2,7 +2,7 @@
 
 namespace App\Util\ActivityPub;
 
-use Cache, DB, Log, Purify, Redis, Validator;
+use Cache, DB, Log, Purify, Redis, Storage, Validator;
 use App\{
 	Activity,
 	DirectMessage,
@@ -14,6 +14,8 @@ use App\{
 	Profile,
 	Status,
 	StatusHashtag,
+	Story,
+	StoryView,
 	UserFilter
 };
 use Carbon\Carbon;
@@ -22,6 +24,8 @@ use Illuminate\Support\Str;
 use App\Jobs\LikePipeline\LikePipeline;
 use App\Jobs\FollowPipeline\FollowPipeline;
 use App\Jobs\DeletePipeline\DeleteRemoteProfilePipeline;
+use App\Jobs\StoryPipeline\StoryExpire;
+use App\Jobs\StoryPipeline\StoryFetch;
 
 use App\Util\ActivityPub\Validator\Accept as AcceptValidator;
 use App\Util\ActivityPub\Validator\Add as AddValidator;
@@ -31,6 +35,7 @@ use App\Util\ActivityPub\Validator\Like as LikeValidator;
 use App\Util\ActivityPub\Validator\UndoFollow as UndoFollowValidator;
 
 use App\Services\PollService;
+use App\Services\FollowerService;
 
 class Inbox
 {
@@ -49,16 +54,7 @@ class Inbox
 	public function handle()
 	{
 		$this->handleVerb();
-
-		// if(!Activity::where('data->id', $this->payload['id'])->exists()) {
-		//     (new Activity())->create([
-		//         'to_id' => $this->profile->id,
-		//         'data' => json_encode($this->payload)
-		//     ]);
-		// }
-
 		return;
-
 	}
 
 	public function handleVerb()
@@ -107,6 +103,18 @@ class Inbox
 				$this->handleUndoActivity();
 				break;
 
+			case 'View':
+				$this->handleViewActivity();
+				break;
+
+			case 'Story:Reaction':
+				$this->handleStoryReactionActivity();
+				break;
+
+			case 'Story:Reply':
+				$this->handleStoryReplyActivity();
+				break;
+
 			default:
 				// TODO: decide how to handle invalid verbs.
 				break;
@@ -138,6 +146,30 @@ class Inbox
 	public function handleAddActivity()
 	{
 		// stories ;)
+
+		if(!isset(
+			$this->payload['actor'],
+			$this->payload['object']
+		)) {
+			return;
+		}
+
+		$actor = $this->payload['actor'];
+		$obj = $this->payload['object'];
+
+		if(!Helpers::validateUrl($actor)) {
+			return;
+		}
+
+		if(!isset($obj['type'])) {
+			return;
+		}
+
+		switch($obj['type']) {
+			case 'Story':
+				StoryFetch::dispatch($this->payload)->onQueue('story');
+			break;
+		}
 	}
 
 	public function handleCreateActivity()
@@ -212,8 +244,8 @@ class Inbox
 		if( isset($activity['inReplyTo']) &&
 			isset($activity['name']) &&
 			!isset($activity['content']) &&
-			!isset($activity['attachment'] &&
-			Helpers::validateLocalUrl($activity['inReplyTo']))
+			!isset($activity['attachment']) &&
+			Helpers::validateLocalUrl($activity['inReplyTo'])
 		) {
 			$this->handlePollVote();
 			return;
@@ -496,7 +528,6 @@ class Inbox
 
 	public function handleAcceptActivity()
 	{
-
 		$actor = $this->payload['object']['actor'];
 		$obj = $this->payload['object']['object'];
 		$type = $this->payload['object']['type'];
@@ -556,7 +587,7 @@ class Inbox
 			return;
 		} else {
 			$type = $this->payload['object']['type'];
-			$typeCheck = in_array($type, ['Person', 'Tombstone']);
+			$typeCheck = in_array($type, ['Person', 'Tombstone', 'Story']);
 			if(!Helpers::validateUrl($actor) || !Helpers::validateUrl($obj['id']) || !$typeCheck) {
 				return;
 			}
@@ -596,6 +627,13 @@ class Inbox
 						return;
 					break;
 
+				case 'Story':
+					$story = Story::whereObjectId($id)
+						->first();
+					if($story) {
+						StoryExpire::dispatch($story)->onQueue('story');
+					}
+
 				default:
 					return;
 					break;
@@ -705,4 +743,250 @@ class Inbox
 		}
 		return;
 	}
+
+	public function handleViewActivity()
+	{
+		if(!isset(
+			$this->payload['actor'],
+			$this->payload['object']
+		)) {
+			return;
+		}
+
+		$actor = $this->payload['actor'];
+		$obj = $this->payload['object'];
+
+		if(!Helpers::validateUrl($actor)) {
+			return;
+		}
+
+		if(!$obj || !is_array($obj)) {
+			return;
+		}
+
+		if(!isset($obj['type']) || !isset($obj['object']) || $obj['type'] != 'Story') {
+			return;
+		}
+
+		if(!Helpers::validateLocalUrl($obj['object'])) {
+			return;
+		}
+
+		$profile = Helpers::profileFetch($actor);
+		$storyId = Str::of($obj['object'])->explode('/')->last();
+
+		$story = Story::whereActive(true)
+			->whereLocal(true)
+			->find($storyId);
+
+		if(!$story) {
+			return;
+		}
+
+		if(!FollowerService::follows($profile->id, $story->profile_id)) {
+			return;
+		}
+
+		$view = StoryView::firstOrCreate([
+			'story_id' => $story->id,
+			'profile_id' => $profile->id
+		]);
+
+		if($view->wasRecentlyCreated == true) {
+			$story->view_count++;
+			$story->save();
+		}
+	}
+
+	public function handleStoryReactionActivity()
+	{
+		if(!isset(
+			$this->payload['actor'],
+			$this->payload['id'],
+			$this->payload['inReplyTo'],
+			$this->payload['content']
+		)) {
+			return;
+		}
+
+		$id = $this->payload['id'];
+		$actor = $this->payload['actor'];
+		$storyUrl = $this->payload['inReplyTo'];
+		$to = $this->payload['to'];
+		$text = Purify::clean($this->payload['content']);
+
+		if(parse_url($id, PHP_URL_HOST) !== parse_url($actor, PHP_URL_HOST)) {
+			return;
+		}
+
+		if(!Helpers::validateUrl($id) || !Helpers::validateUrl($actor)) {
+			return;
+		}
+
+		if(!Helpers::validateLocalUrl($storyUrl)) {
+			return;
+		}
+
+		if(!Helpers::validateLocalUrl($to)) {
+			return;
+		}
+
+		if(Status::whereObjectUrl($id)->exists()) {
+			return;
+		}
+
+		$storyId = Str::of($storyUrl)->explode('/')->last();
+		$targetProfile = Helpers::profileFetch($to);
+
+		$story = Story::whereProfileId($targetProfile->id)
+			->find($storyId);
+
+		if(!$story) {
+			return;
+		}
+
+		if($story->can_react == false) {
+			return;
+		}
+
+		$actorProfile = Helpers::profileFetch($actor);
+
+		if(!FollowerService::follows($actorProfile->id, $targetProfile->id)) {
+			return;
+		}
+
+		$status = new Status;
+		$status->profile_id = $actorProfile->id;
+		$status->type = 'story:reaction';
+		$status->caption = $text;
+		$status->rendered = $text;
+		$status->scope = 'direct';
+		$status->visibility = 'direct';
+		$status->in_reply_to_profile_id = $story->profile_id;
+		$status->entities = json_encode([
+			'story_id' => $story->id,
+			'reaction' => $text
+		]);
+		$status->save();
+
+		$dm = new DirectMessage;
+		$dm->to_id = $story->profile_id;
+		$dm->from_id = $actorProfile->id;
+		$dm->type = 'story:react';
+		$dm->status_id = $status->id;
+		$dm->meta = json_encode([
+			'story_username' => $targetProfile->username,
+			'story_actor_username' => $actorProfile->username,
+			'story_id' => $story->id,
+			'story_media_url' => url(Storage::url($story->path)),
+			'reaction' => $text
+		]);
+		$dm->save();
+
+		$n = new Notification;
+		$n->profile_id = $dm->to_id;
+		$n->actor_id = $dm->from_id;
+		$n->item_id = $dm->id;
+		$n->item_type = 'App\DirectMessage';
+		$n->action = 'story:react';
+		$n->message = "{$actorProfile->username} reacted to your story";
+		$n->rendered = "{$actorProfile->username} reacted to your story";
+		$n->save();
+	}
+
+	public function handleStoryReplyActivity()
+	{
+		if(!isset(
+			$this->payload['actor'],
+			$this->payload['id'],
+			$this->payload['inReplyTo'],
+			$this->payload['content']
+		)) {
+			return;
+		}
+
+		$id = $this->payload['id'];
+		$actor = $this->payload['actor'];
+		$storyUrl = $this->payload['inReplyTo'];
+		$to = $this->payload['to'];
+		$text = Purify::clean($this->payload['content']);
+
+		if(parse_url($id, PHP_URL_HOST) !== parse_url($actor, PHP_URL_HOST)) {
+			return;
+		}
+
+		if(!Helpers::validateUrl($id) || !Helpers::validateUrl($actor)) {
+			return;
+		}
+
+		if(!Helpers::validateLocalUrl($storyUrl)) {
+			return;
+		}
+
+		if(!Helpers::validateLocalUrl($to)) {
+			return;
+		}
+
+		if(Status::whereObjectUrl($id)->exists()) {
+			return;
+		}
+
+		$storyId = Str::of($storyUrl)->explode('/')->last();
+		$targetProfile = Helpers::profileFetch($to);
+
+		$story = Story::whereProfileId($targetProfile->id)
+			->find($storyId);
+
+		if(!$story) {
+			return;
+		}
+
+		if($story->can_react == false) {
+			return;
+		}
+
+		$actorProfile = Helpers::profileFetch($actor);
+
+		if(!FollowerService::follows($actorProfile->id, $targetProfile->id)) {
+			return;
+		}
+
+		$status = new Status;
+		$status->profile_id = $actorProfile->id;
+		$status->type = 'story:reply';
+		$status->caption = $text;
+		$status->rendered = $text;
+		$status->scope = 'direct';
+		$status->visibility = 'direct';
+		$status->in_reply_to_profile_id = $story->profile_id;
+		$status->entities = json_encode([
+			'story_id' => $story->id,
+			'caption' => $text
+		]);
+		$status->save();
+
+		$dm = new DirectMessage;
+		$dm->to_id = $story->profile_id;
+		$dm->from_id = $actorProfile->id;
+		$dm->type = 'story:comment';
+		$dm->status_id = $status->id;
+		$dm->meta = json_encode([
+			'story_username' => $targetProfile->username,
+			'story_actor_username' => $actorProfile->username,
+			'story_id' => $story->id,
+			'story_media_url' => url(Storage::url($story->path)),
+			'caption' => $text
+		]);
+		$dm->save();
+
+		$n = new Notification;
+		$n->profile_id = $dm->to_id;
+		$n->actor_id = $dm->from_id;
+		$n->item_id = $dm->id;
+		$n->item_type = 'App\DirectMessage';
+		$n->action = 'story:comment';
+		$n->message = "{$actorProfile->username} commented on story";
+		$n->rendered = "{$actorProfile->username} commented on story";
+		$n->save();
+	}
 }