diff --git a/.env.example b/.env.example index be3e47d42..e07d8a7b9 100644 --- a/.env.example +++ b/.env.example @@ -33,3 +33,6 @@ TWITTER_ACCESS_SECRET= FLARE_KEY= MIX_FLARE_KEY="${FLARE_KEY}" + +TELEGRAM_BOT_TOKEN= +TELEGRAM_CHANNEL= diff --git a/app/Events/ArticleWasSubmittedForApproval.php b/app/Events/ArticleWasSubmittedForApproval.php new file mode 100644 index 000000000..901096650 --- /dev/null +++ b/app/Events/ArticleWasSubmittedForApproval.php @@ -0,0 +1,18 @@ +article = $article; + } +} diff --git a/app/Jobs/CreateArticle.php b/app/Jobs/CreateArticle.php index d05c030ee..235169ee8 100644 --- a/app/Jobs/CreateArticle.php +++ b/app/Jobs/CreateArticle.php @@ -2,6 +2,7 @@ namespace App\Jobs; +use App\Events\ArticleWasSubmittedForApproval; use App\Http\Requests\ArticleRequest; use App\Models\Article; use App\Models\User; @@ -56,6 +57,10 @@ public function handle(): Article $article->authoredBy($this->author); $article->syncTags($this->tags); + if ($article->isAwaitingApproval()) { + event(new ArticleWasSubmittedForApproval($article)); + } + return $article; } } diff --git a/app/Jobs/UpdateArticle.php b/app/Jobs/UpdateArticle.php index d1612df39..462e26cea 100644 --- a/app/Jobs/UpdateArticle.php +++ b/app/Jobs/UpdateArticle.php @@ -2,6 +2,7 @@ namespace App\Jobs; +use App\Events\ArticleWasSubmittedForApproval; use App\Http\Requests\ArticleRequest; use App\Models\Article; @@ -50,9 +51,15 @@ public function handle(): Article 'body' => $this->body, 'original_url' => $this->originalUrl, 'slug' => $this->title, - 'submitted_at' => $this->shouldUpdateSubmittedAt() ? now() : $this->article->submittedAt(), ]); + if ($this->shouldUpdateSubmittedAt()) { + $this->article->submitted_at = now(); + $this->article->save(); + + event(new ArticleWasSubmittedForApproval($this->article)); + } + $this->article->syncTags($this->tags); return $this->article; diff --git a/app/Listeners/SendNewArticleNotification.php b/app/Listeners/SendNewArticleNotification.php new file mode 100644 index 000000000..b41365245 --- /dev/null +++ b/app/Listeners/SendNewArticleNotification.php @@ -0,0 +1,20 @@ +notifiable->notify(new ArticleSubmitted($event->article)); + } +} diff --git a/app/Notifications/ArticleSubmitted.php b/app/Notifications/ArticleSubmitted.php new file mode 100644 index 000000000..89b4235fa --- /dev/null +++ b/app/Notifications/ArticleSubmitted.php @@ -0,0 +1,45 @@ +article = $article; + } + + public function via($notifiable) + { + return ['telegram']; + } + + public function toTelegram($notifiable) + { + $url = route('articles.show', $this->article->slug()); + + return TelegramMessage::create() + ->to(config('services.telegram-bot-api.channel')) + ->content($this->content()) + ->button('View Article', $url); + } + + private function content(): string + { + $content = "*New Article Submitted!*\n\n"; + $content .= 'Title: '.$this->article->title()."\n"; + $content .= 'By: [@'.$this->article->author()->username().']('.route('profile', $this->article->author()->username()).')'; + + return $content; + } +} diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index a0a24aaff..28d79a29c 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -3,9 +3,11 @@ namespace App\Providers; use App\Events\ArticleWasApproved; +use App\Events\ArticleWasSubmittedForApproval; use App\Events\ReplyWasCreated; use App\Listeners\MarkLastActivity; use App\Listeners\SendArticleApprovedNotification; +use App\Listeners\SendNewArticleNotification; use App\Listeners\SendNewReplyNotification; use App\Listeners\StoreTweetIdentifier; use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider; @@ -23,6 +25,9 @@ class EventServiceProvider extends ServiceProvider MarkLastActivity::class, SendNewReplyNotification::class, ], + ArticleWasSubmittedForApproval::class => [ + SendNewArticleNotification::class, + ], ArticleWasApproved::class => [ SendArticleApprovedNotification::class, ], diff --git a/composer.json b/composer.json index 0bd83e9c6..012a7b270 100644 --- a/composer.json +++ b/composer.json @@ -17,6 +17,7 @@ "guzzlehttp/guzzle": "^7.0.1", "intervention/image": "^2.5", "intervention/imagecache": "^2.5", + "laravel-notification-channels/telegram": "^0.9.0", "laravel-notification-channels/twitter": "^5.0", "laravel/framework": "8.66.0", "laravel/horizon": "^5.2", diff --git a/composer.lock b/composer.lock index 2a88c038e..497ea03ac 100644 --- a/composer.lock +++ b/composer.lock @@ -2384,6 +2384,72 @@ }, "time": "2017-11-25T19:51:26+00:00" }, + { + "name": "laravel-notification-channels/telegram", + "version": "0.9.0", + "source": { + "type": "git", + "url": "https://github.com/laravel-notification-channels/telegram.git", + "reference": "a941130a555908dc16b119b4f8c99b5f3605bf25" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel-notification-channels/telegram/zipball/a941130a555908dc16b119b4f8c99b5f3605bf25", + "reference": "a941130a555908dc16b119b4f8c99b5f3605bf25", + "shasum": "" + }, + "require": { + "ext-json": "*", + "guzzlehttp/guzzle": "^6.2 || ^7.0", + "illuminate/contracts": "^5.5 || ^6.0 || ^7.0 || ^8.0", + "illuminate/notifications": "^5.5 || ^6.0 || ^7.0 || ^8.0", + "illuminate/support": "^5.5 || ^6.0 || ^7.0 || ^8.0", + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "mockery/mockery": "^1.3", + "phpunit/phpunit": "^7.0 || ^8.5.21 || ^9.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "NotificationChannels\\Telegram\\TelegramServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "NotificationChannels\\Telegram\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Irfaq Syed", + "email": "syed@lukonet.com", + "homepage": "https://lukonet.com", + "role": "Developer" + } + ], + "description": "Telegram Notifications Channel for Laravel", + "homepage": "https://github.com/laravel-notification-channels/telegram", + "keywords": [ + "laravel", + "notification", + "telegram", + "telegram notification", + "telegram notifications channel" + ], + "support": { + "issues": "https://github.com/laravel-notification-channels/telegram/issues", + "source": "https://github.com/laravel-notification-channels/telegram/tree/0.9.0" + }, + "time": "2021-11-24T12:19:23+00:00" + }, { "name": "laravel-notification-channels/twitter", "version": "v5.1.0", diff --git a/config/services.php b/config/services.php index a59a428cb..586e80d8b 100644 --- a/config/services.php +++ b/config/services.php @@ -51,4 +51,9 @@ 'access_secret' => env('TWITTER_ACCESS_SECRET'), ], + 'telegram-bot-api' => [ + 'token' => env('TELEGRAM_BOT_TOKEN'), + 'channel' => env('TELEGRAM_CHANNEL'), + ], + ]; diff --git a/phpunit.xml b/phpunit.xml index 476245d85..daabbf7bd 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -25,5 +25,7 @@ + + diff --git a/tests/Feature/ArticleTest.php b/tests/Feature/ArticleTest.php index e61dee483..c85b06bdd 100644 --- a/tests/Feature/ArticleTest.php +++ b/tests/Feature/ArticleTest.php @@ -1,9 +1,11 @@ see('Awaiting approval'); }); +test('articles submitted for approval send telegram notification', function () { + Event::fake(); + $this->login(); + + $this->post('/articles', [ + 'title' => 'Using database migrations', + 'body' => 'This article will go into depth on working with database migrations.', + 'submitted' => '1', + ]); + + Event::assertDispatched(ArticleWasSubmittedForApproval::class); +}); + test('users can create a draft article', function () { $this->login(); @@ -71,6 +86,19 @@ ->see('Draft'); }); +test('draft articles do not send telegram notification', function () { + Event::fake(); + $this->login(); + + $this->post('/articles', [ + 'title' => 'Using database migrations', + 'body' => 'This article will go into depth on working with database migrations.', + 'submitted' => '0', + ]); + + Event::assertNotDispatched(ArticleWasSubmittedForApproval::class); +}); + test('users cannot create an article with a title that is too long', function () { $this->login(); @@ -132,6 +160,28 @@ ->assertSessionHas('success', 'Article successfully updated!'); }); +test('editing a draft article does not send telegram notification', function () { + Event::fake(); + $user = $this->createUser(); + $tag = Tag::factory()->create(['name' => 'Test Tag']); + + Article::factory()->create([ + 'author_id' => $user->id(), + 'slug' => 'my-first-article', + ]); + + $this->loginAs($user); + + $this->put('/articles/my-first-article', [ + 'title' => 'Using database migrations', + 'body' => 'This article will go into depth on working with database migrations.', + 'tags' => [$tag->id()], + 'submitted' => '0', + ]); + + Event::assertNotDispatched(ArticleWasSubmittedForApproval::class); +}); + test('user gets submitted message when submitting existing article for approval', function () { $user = $this->createUser(); @@ -173,6 +223,27 @@ ->dontSee('Draft'); }); +test('notification is sent to telegram when existing article is submitted for approval', function () { + Event::fake(); + $user = $this->createUser(); + + Article::factory()->create([ + 'author_id' => $user->id(), + 'slug' => 'my-first-article', + ]); + + $this->loginAs($user); + + $this->put('/articles/my-first-article', [ + 'title' => 'Using database migrations', + 'body' => 'This article will go into depth on working with database migrations.', + 'tags' => [], + 'submitted' => '1', + ]); + + Event::assertDispatched(ArticleWasSubmittedForApproval::class); +}); + test('users cannot edit an article with a title that is too long', function () { $user = $this->createUser(); Article::factory()->create([