|
@@ -0,0 +1,144 @@
|
|
|
|
+<?php
|
|
|
|
+
|
|
|
|
+namespace App\Jobs\StoryPipeline;
|
|
|
|
+
|
|
|
|
+use Cache, Log;
|
|
|
|
+use App\Story;
|
|
|
|
+use Illuminate\Bus\Queueable;
|
|
|
|
+use Illuminate\Contracts\Queue\ShouldQueue;
|
|
|
|
+use Illuminate\Foundation\Bus\Dispatchable;
|
|
|
|
+use Illuminate\Queue\InteractsWithQueue;
|
|
|
|
+use Illuminate\Queue\SerializesModels;
|
|
|
|
+use App\Util\ActivityPub\Helpers;
|
|
|
|
+use App\Services\FollowerService;
|
|
|
|
+use App\Util\Lexer\Bearcap;
|
|
|
|
+use Illuminate\Support\Facades\Http;
|
|
|
|
+use Illuminate\Http\Client\RequestException;
|
|
|
|
+use Illuminate\Http\Client\ConnectionException;
|
|
|
|
+use App\Util\ActivityPub\Validator\StoryValidator;
|
|
|
|
+use App\Services\StoryService;
|
|
|
|
+use App\Services\MediaPathService;
|
|
|
|
+use Illuminate\Support\Str;
|
|
|
|
+use Illuminate\Http\File;
|
|
|
|
+use Illuminate\Support\Facades\Storage;
|
|
|
|
+
|
|
|
|
+class StoryFetch implements ShouldQueue
|
|
|
|
+{
|
|
|
|
+ use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
|
|
|
+
|
|
|
|
+ protected $activity;
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Create a new job instance.
|
|
|
|
+ *
|
|
|
|
+ * @return void
|
|
|
|
+ */
|
|
|
|
+ public function __construct($activity)
|
|
|
|
+ {
|
|
|
|
+ $this->activity = $activity;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Execute the job.
|
|
|
|
+ *
|
|
|
|
+ * @return void
|
|
|
|
+ */
|
|
|
|
+ public function handle()
|
|
|
|
+ {
|
|
|
|
+ $activity = $this->activity;
|
|
|
|
+ $activityId = $activity['id'];
|
|
|
|
+ $activityActor = $activity['actor'];
|
|
|
|
+
|
|
|
|
+ if(parse_url($activityId, PHP_URL_HOST) !== parse_url($activityActor, PHP_URL_HOST)) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ $bearcap = Bearcap::decode($activity['object']['object']);
|
|
|
|
+
|
|
|
|
+ if(!$bearcap) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ $url = $bearcap['url'];
|
|
|
|
+ $token = $bearcap['token'];
|
|
|
|
+
|
|
|
|
+ if(parse_url($activityId, PHP_URL_HOST) !== parse_url($url, PHP_URL_HOST)) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ $version = config('pixelfed.version');
|
|
|
|
+ $appUrl = config('app.url');
|
|
|
|
+ $headers = [
|
|
|
|
+ 'Accept' => 'application/json',
|
|
|
|
+ 'Authorization' => 'Bearer ' . $token,
|
|
|
|
+ 'User-Agent' => "(Pixelfed/{$version}; +{$appUrl})",
|
|
|
|
+ ];
|
|
|
|
+
|
|
|
|
+ try {
|
|
|
|
+ $res = Http::withHeaders($headers)
|
|
|
|
+ ->timeout(30)
|
|
|
|
+ ->get($url);
|
|
|
|
+ } catch (RequestException $e) {
|
|
|
|
+ return false;
|
|
|
|
+ } catch (ConnectionException $e) {
|
|
|
|
+ return false;
|
|
|
|
+ } catch (\Exception $e) {
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ $payload = $res->json();
|
|
|
|
+
|
|
|
|
+ if(StoryValidator::validate($payload) == false) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if(Helpers::validateUrl($payload['attachment']['url']) == false) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ $type = $payload['attachment']['type'] == 'Image' ? 'photo' : 'video';
|
|
|
|
+
|
|
|
|
+ $profile = Helpers::profileFetch($payload['attributedTo']);
|
|
|
|
+
|
|
|
|
+ $ext = pathinfo($payload['attachment']['url'], PATHINFO_EXTENSION);
|
|
|
|
+ $storagePath = MediaPathService::story($profile);
|
|
|
|
+ $fileName = Str::random(random_int(2, 12)) . '_' . Str::random(random_int(32, 35)) . '_' . Str::random(random_int(1, 14)) . '.' . $ext;
|
|
|
|
+ $contextOptions = [
|
|
|
|
+ 'ssl' => [
|
|
|
|
+ 'verify_peer' => false,
|
|
|
|
+ 'verify_peername' => false
|
|
|
|
+ ]
|
|
|
|
+ ];
|
|
|
|
+ $ctx = stream_context_create($contextOptions);
|
|
|
|
+ $data = file_get_contents($payload['attachment']['url'], false, $ctx);
|
|
|
|
+ $tmpBase = storage_path('app/remcache/');
|
|
|
|
+ $tmpPath = $profile->id . '-' . $fileName;
|
|
|
|
+ $tmpName = $tmpBase . $tmpPath;
|
|
|
|
+ file_put_contents($tmpName, $data);
|
|
|
|
+ $disk = Storage::disk(config('filesystems.default'));
|
|
|
|
+ $path = $disk->putFileAs($storagePath, new File($tmpName), $fileName, 'public');
|
|
|
|
+ $size = filesize($tmpName);
|
|
|
|
+ unlink($tmpName);
|
|
|
|
+
|
|
|
|
+ $story = new Story;
|
|
|
|
+ $story->profile_id = $profile->id;
|
|
|
|
+ $story->object_id = $payload['id'];
|
|
|
|
+ $story->size = $size;
|
|
|
|
+ $story->mime = $payload['attachment']['mediaType'];
|
|
|
|
+ $story->duration = $payload['duration'];
|
|
|
|
+ $story->media_url = $payload['attachment']['url'];
|
|
|
|
+ $story->type = $type;
|
|
|
|
+ $story->public = false;
|
|
|
|
+ $story->local = false;
|
|
|
|
+ $story->active = true;
|
|
|
|
+ $story->path = $path;
|
|
|
|
+ $story->view_count = 0;
|
|
|
|
+ $story->can_reply = $payload['can_reply'];
|
|
|
|
+ $story->can_react = $payload['can_react'];
|
|
|
|
+ $story->created_at = now()->parse($payload['published']);
|
|
|
|
+ $story->expires_at = now()->parse($payload['expiresAt']);
|
|
|
|
+ $story->save();
|
|
|
|
+
|
|
|
|
+ StoryService::delLatest($story->profile_id);
|
|
|
|
+ }
|
|
|
|
+}
|