From fa43ec2333eddecfede8daa6e40eb1e071709e5e Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Sun, 30 Oct 2022 23:41:20 -0600 Subject: [PATCH 1/2] Add migration --- .../ActivityPub/ProfileTransformer.php | 3 -- ...tors_last_synced_at_to_instances_table.php | 32 +++++++++++++++++++ 2 files changed, 32 insertions(+), 3 deletions(-) create mode 100644 database/migrations/2022_10_31_043257_add_actors_last_synced_at_to_instances_table.php diff --git a/app/Transformer/ActivityPub/ProfileTransformer.php b/app/Transformer/ActivityPub/ProfileTransformer.php index 76d398ce1..29f53425c 100644 --- a/app/Transformer/ActivityPub/ProfileTransformer.php +++ b/app/Transformer/ActivityPub/ProfileTransformer.php @@ -23,14 +23,11 @@ class ProfileTransformer extends Fractal\TransformerAbstract 'followers' => $profile->permalink('/followers'), 'inbox' => $profile->permalink('/inbox'), 'outbox' => $profile->permalink('/outbox'), - //'featured' => $profile->permalink('/collections/featured'), 'preferredUsername' => $profile->username, 'name' => $profile->name, 'summary' => $profile->bio, 'url' => $profile->url(), 'manuallyApprovesFollowers' => (bool) $profile->is_private, - // 'follower_count' => $profile->followers()->count(), - // 'following_count' => $profile->following()->count(), 'publicKey' => [ 'id' => $profile->permalink().'#main-key', 'owner' => $profile->permalink(), diff --git a/database/migrations/2022_10_31_043257_add_actors_last_synced_at_to_instances_table.php b/database/migrations/2022_10_31_043257_add_actors_last_synced_at_to_instances_table.php new file mode 100644 index 000000000..3a0c309c6 --- /dev/null +++ b/database/migrations/2022_10_31_043257_add_actors_last_synced_at_to_instances_table.php @@ -0,0 +1,32 @@ +timestamp('actors_last_synced_at')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('instances', function (Blueprint $table) { + $table->dropColumn('actors_last_synced_at'); + }); + } +}; From 896e6023bdaecb99ef6bb1d9613c3d3d69afdbcc Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Mon, 31 Oct 2022 00:05:17 -0600 Subject: [PATCH 2/2] Add command --- app/Console/Commands/SendUpdateActor.php | 149 +++++++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 app/Console/Commands/SendUpdateActor.php diff --git a/app/Console/Commands/SendUpdateActor.php b/app/Console/Commands/SendUpdateActor.php new file mode 100644 index 000000000..ea0c0d5eb --- /dev/null +++ b/app/Console/Commands/SendUpdateActor.php @@ -0,0 +1,149 @@ +count(); + $totalInstanceCount = Instance::count(); + $this->info('Found ' . $totalUserCount . ' local accounts and ' . $totalInstanceCount . ' remote instances'); + + $task = $this->choice( + 'What do you want to do?', + [ + 'View top instances', + 'Send updates to an instance' + ], + 0 + ); + + if($task === 'View top instances') { + $this->table( + ['domain', 'user_count', 'last_synced'], + Instance::orderByDesc('user_count')->take(20)->get(['domain', 'user_count', 'actors_last_synced_at'])->toArray() + ); + return Command::SUCCESS; + } else { + $domain = $this->anticipate('Enter the instance domain', function ($input) { + return Instance::where('domain', 'like', '%' . $input . '%')->pluck('domain')->toArray(); + }); + if(!$this->confirm('Are you sure you want to send actor updates to ' . $domain . '?')) { + return; + } + if($cur = Instance::whereDomain($domain)->whereNotNull('actors_last_synced_at')->first()) { + if(!$this->option('force')) { + $this->error('ERROR: Cannot re-sync this instance, it was already synced on ' . $cur->actors_last_synced_at); + return; + } + } + $this->line(' '); + $this->error('Keep this window open during this process or it will not complete!'); + $sharedInbox = Profile::whereDomain($domain)->whereNotNull('sharedInbox')->first(); + if(!$sharedInbox) { + $this->error('ERROR: Cannot find the sharedInbox of ' . $domain); + return; + } + $url = $sharedInbox->sharedInbox; + $this->line(' '); + $this->info('Found sharedInbox: ' . $url); + $bar = $this->output->createProgressBar($totalUserCount); + $bar->start(); + User::chunk(50, function($users) use($bar, $url) { + foreach($users as $user) { + $profile = Profile::find($user->profile_id); + if(!$profile) { + continue; + } + $body = $this->updateObject($profile); + Helpers::sendSignedObject($profile, $url, $body); + usleep(500); + $bar->advance(); + } + }); + $bar->finish(); + $this->line(' '); + $instance = Instance::whereDomain($domain)->firstOrFail(); + $instance->actors_last_synced_at = now(); + $instance->save(); + $this->info('Finished!'); + return Command::SUCCESS; + } + + return Command::SUCCESS; + } + + protected function updateObject($profile) + { + return [ + '@context' => [ + 'https://w3id.org/security/v1', + 'https://www.w3.org/ns/activitystreams', + [ + 'manuallyApprovesFollowers' => 'as:manuallyApprovesFollowers', + ], + ], + 'id' => $profile->permalink('#updates/' . time()), + 'actor' => $profile->permalink(), + 'type' => 'Update', + 'object' => $this->actorObject($profile) + ]; + } + + protected function actorObject($profile) + { + $permalink = $profile->permalink(); + return [ + 'id' => $permalink, + 'type' => 'Person', + 'following' => $permalink . '/following', + 'followers' => $permalink . '/followers', + 'inbox' => $permalink . '/inbox', + 'outbox' => $permalink . '/outbox', + 'preferredUsername' => $profile->username, + 'name' => $profile->name, + 'summary' => $profile->bio, + 'url' => $profile->url(), + 'manuallyApprovesFollowers' => (bool) $profile->is_private, + 'publicKey' => [ + 'id' => $permalink . '#main-key', + 'owner' => $permalink, + 'publicKeyPem' => $profile->public_key, + ], + 'icon' => [ + 'type' => 'Image', + 'mediaType' => 'image/jpeg', + 'url' => $profile->avatarUrl(), + ], + 'endpoints' => [ + 'sharedInbox' => config('app.url') . '/f/inbox' + ] + ]; + } +}