|
@@ -0,0 +1,149 @@
|
|
|
+<?php
|
|
|
+
|
|
|
+namespace App\Console\Commands;
|
|
|
+
|
|
|
+use Illuminate\Console\Command;
|
|
|
+use App\Profile;
|
|
|
+use App\User;
|
|
|
+use App\Instance;
|
|
|
+use App\Util\ActivityPub\Helpers;
|
|
|
+
|
|
|
+class SendUpdateActor extends Command
|
|
|
+{
|
|
|
+ /**
|
|
|
+ * The name and signature of the console command.
|
|
|
+ *
|
|
|
+ * @var string
|
|
|
+ */
|
|
|
+ protected $signature = 'ap:update-actors {--force}';
|
|
|
+
|
|
|
+ /**
|
|
|
+ * The console command description.
|
|
|
+ *
|
|
|
+ * @var string
|
|
|
+ */
|
|
|
+ protected $description = 'Send Update Actor activities to known remote servers to force updates';
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Execute the console command.
|
|
|
+ *
|
|
|
+ * @return int
|
|
|
+ */
|
|
|
+ public function handle()
|
|
|
+ {
|
|
|
+ $totalUserCount = Profile::whereNotNull('user_id')->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'
|
|
|
+ ]
|
|
|
+ ];
|
|
|
+ }
|
|
|
+}
|