diff --git a/.env.example b/.env.example
index 114a68a0..d1a26e82 100644
--- a/.env.example
+++ b/.env.example
@@ -66,3 +66,5 @@ SECURE_SESSION_COOKIE=true
LOG_SLACK_WEBHOOK_URL=
FONT_LINK=
+
+BRIDGY_MASTODON_TOKEN=
diff --git a/app/Jobs/SyndicateNoteToMastodon.php b/app/Jobs/SyndicateNoteToMastodon.php
new file mode 100644
index 00000000..8c75f52e
--- /dev/null
+++ b/app/Jobs/SyndicateNoteToMastodon.php
@@ -0,0 +1,66 @@
+request(
+ 'POST',
+ 'https://brid.gy/micropub',
+ [
+ 'headers' => [
+ 'Authorization' => 'Bearer ' . config('bridgy.mastodon_token'),
+ ],
+ 'json' => [
+ 'type' => ['h-entry'],
+ 'properties' => [
+ 'content' => [$this->note->note],
+ ],
+ ],
+ ]
+ );
+
+ // Parse for syndication URL
+ if ($response->getStatusCode() === 201) {
+ $mastodonUrl = $response->getHeader('Location')[0];
+ $this->note->mastodon_url = $mastodonUrl;
+ $this->note->save();
+ }
+ }
+}
diff --git a/app/Services/NoteService.php b/app/Services/NoteService.php
index 57458389..156acbfb 100644
--- a/app/Services/NoteService.php
+++ b/app/Services/NoteService.php
@@ -5,6 +5,7 @@ declare(strict_types=1);
namespace App\Services;
use App\Jobs\SendWebMentions;
+use App\Jobs\SyndicateNoteToMastodon;
use App\Jobs\SyndicateNoteToTwitter;
use App\Models\Media;
use App\Models\Note;
@@ -58,6 +59,10 @@ class NoteService
dispatch(new SyndicateNoteToTwitter($note));
}
+ if (in_array('mastodon', $this->getSyndicationTargets($request), true)) {
+ dispatch(new SyndicateNoteToMastodon($note));
+ }
+
return $note;
}
@@ -212,6 +217,9 @@ class NoteService
if ($target && $target->service_name === 'Twitter') {
$syndication[] = 'twitter';
}
+ if ($target && $target->service_name === 'Mastodon') {
+ $syndication[] = 'mastodon';
+ }
}
return $syndication;
diff --git a/config/bridgy.php b/config/bridgy.php
new file mode 100644
index 00000000..9717625f
--- /dev/null
+++ b/config/bridgy.php
@@ -0,0 +1,18 @@
+ env('BRIDGY_MASTODON_TOKEN'),
+
+];
diff --git a/database/migrations/2022_10_26_180903_add_mastodon_syndication_url.php b/database/migrations/2022_10_26_180903_add_mastodon_syndication_url.php
new file mode 100644
index 00000000..48c4403e
--- /dev/null
+++ b/database/migrations/2022_10_26_180903_add_mastodon_syndication_url.php
@@ -0,0 +1,32 @@
+string('mastodon_url')->nullable();
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::table('notes', function (Blueprint $table) {
+ $table->dropColumn('mastodon_url');
+ });
+ }
+};
diff --git a/database/seeders/NotesTableSeeder.php b/database/seeders/NotesTableSeeder.php
index 2bf9a82a..4a0a0bec 100644
--- a/database/seeders/NotesTableSeeder.php
+++ b/database/seeders/NotesTableSeeder.php
@@ -137,6 +137,7 @@ class NotesTableSeeder extends Seeder
$noteSyndicated->facebook_url = 'https://www.facebook.com/post/12345789';
$noteSyndicated->swarm_url = 'https://www.swarmapp.com/checking/123456789';
$noteSyndicated->instagram_url = 'https://www.instagram.com/p/aWsEd123Jh';
+ $noteSyndicated->mastodon_url = 'https://mastodon.social/@jonnybarnes/123456789';
$noteSyndicated->save();
DB::table('notes')
->where('id', $noteSyndicated->id)
diff --git a/resources/views/templates/note.blade.php b/resources/views/templates/note.blade.php
index 20309057..ebb2cbe4 100644
--- a/resources/views/templates/note.blade.php
+++ b/resources/views/templates/note.blade.php
@@ -33,12 +33,14 @@
$note->tweet_id ||
$note->facebook_url ||
$note->swarm_url ||
- $note->instagram_url)
+ $note->instagram_url ||
+ $note->mastodon_url)
@include('templates.social-links', [
'tweet_id' => $note->tweet_id,
'facebook_url' => $note->facebook_url,
'swarm_url' => $note->swarm_url,
'instagram_url' => $note->instagram_url,
+ 'mastodon_url' => $note->mastodon_url,
])
@endif
diff --git a/resources/views/templates/social-links.blade.php b/resources/views/templates/social-links.blade.php
index 99e16753..cdfeb619 100644
--- a/resources/views/templates/social-links.blade.php
+++ b/resources/views/templates/social-links.blade.php
@@ -27,3 +27,15 @@
@endif
+@if($mastodon_url !== null)
+
+@endif
diff --git a/tests/Feature/MicropubControllerTest.php b/tests/Feature/MicropubControllerTest.php
index 12604ad3..aa42b0a6 100644
--- a/tests/Feature/MicropubControllerTest.php
+++ b/tests/Feature/MicropubControllerTest.php
@@ -5,6 +5,7 @@ declare(strict_types=1);
namespace Tests\Feature;
use App\Jobs\SendWebMentions;
+use App\Jobs\SyndicateNoteToMastodon;
use App\Jobs\SyndicateNoteToTwitter;
use App\Models\Media;
use App\Models\Note;
@@ -123,7 +124,7 @@ class MicropubControllerTest extends TestCase
}
/** @test */
- public function micropubClientCanRequestTheNewNoteIsSyndicatedToTwitter(): void
+ public function micropubClientCanRequestTheNewNoteIsSyndicatedToTwitterAndMastodon(): void
{
Queue::fake();
@@ -131,6 +132,10 @@ class MicropubControllerTest extends TestCase
'uid' => 'https://twitter.com/jonnybarnes',
'service_name' => 'Twitter',
]);
+ SyndicationTarget::factory()->create([
+ 'uid' => 'https://mastodon.social/@jonnybarnes',
+ 'service_name' => 'Mastodon',
+ ]);
$faker = Factory::create();
$note = $faker->text;
@@ -139,13 +144,17 @@ class MicropubControllerTest extends TestCase
[
'h' => 'entry',
'content' => $note,
- 'mp-syndicate-to' => 'https://twitter.com/jonnybarnes',
+ 'mp-syndicate-to' => [
+ 'https://twitter.com/jonnybarnes',
+ 'https://mastodon.social/@jonnybarnes',
+ ],
],
['HTTP_Authorization' => 'Bearer ' . $this->getToken()]
);
$response->assertJson(['response' => 'created']);
$this->assertDatabaseHas('notes', ['note' => $note]);
Queue::assertPushed(SyndicateNoteToTwitter::class);
+ Queue::assertPushed(SyndicateNoteToMastodon::class);
}
/** @test */
@@ -243,6 +252,10 @@ class MicropubControllerTest extends TestCase
'uid' => 'https://twitter.com/jonnybarnes',
'service_name' => 'Twitter',
]);
+ SyndicationTarget::factory()->create([
+ 'uid' => 'https://mastodon.social/@jonnybarnes',
+ 'service_name' => 'Mastodon',
+ ]);
$faker = Factory::create();
$note = $faker->text;
@@ -255,6 +268,7 @@ class MicropubControllerTest extends TestCase
'in-reply-to' => ['https://aaronpk.localhost'],
'mp-syndicate-to' => [
'https://twitter.com/jonnybarnes',
+ 'https://mastodon.social/@jonnybarnes',
],
'photo' => [config('filesystems.disks.s3.url') . '/test-photo.jpg'],
],
@@ -266,6 +280,7 @@ class MicropubControllerTest extends TestCase
->assertJson(['response' => 'created']);
Queue::assertPushed(SendWebMentions::class);
Queue::assertPushed(SyndicateNoteToTwitter::class);
+ Queue::assertPushed(SyndicateNoteToMastodon::class);
}
/**
diff --git a/tests/Unit/Jobs/SyndicateNoteToMastodonJobTest.php b/tests/Unit/Jobs/SyndicateNoteToMastodonJobTest.php
new file mode 100644
index 00000000..be7bf6d2
--- /dev/null
+++ b/tests/Unit/Jobs/SyndicateNoteToMastodonJobTest.php
@@ -0,0 +1,39 @@
+ 'test']);
+ $faker = Factory::create();
+ $randomNumber = $faker->randomNumber();
+ $mock = new MockHandler([
+ new Response(201, ['Location' => 'https://mastodon.example/@jonny/' . $randomNumber]),
+ ]);
+ $handler = HandlerStack::create($mock);
+ $client = new Client(['handler' => $handler]);
+
+ $note = Note::factory()->create();
+ $job = new SyndicateNoteToMastodon($note);
+ $job->handle($client);
+
+ $this->assertDatabaseHas('notes', [
+ 'mastodon_url' => 'https://mastodon.example/@jonny/' . $randomNumber,
+ ]);
+ }
+}
diff --git a/tests/Unit/NotesTest.php b/tests/Unit/NotesTest.php
index 489673c2..098516b9 100644
--- a/tests/Unit/NotesTest.php
+++ b/tests/Unit/NotesTest.php
@@ -113,8 +113,8 @@ class NotesTest extends TestCase
/** @test */
public function shorturlMethodReturnsExpectedValue(): void
{
- Note::factory(14)->create();
- $note = Note::find(14);
+ $note = Note::factory()->make();
+ $note->id = 14;
$this->assertEquals(config('app.shorturl') . '/notes/E', $note->shorturl);
}