Skip to content

Commit 7d707d6

Browse files
authored
Feature/lar 10 schedule notifications (#152)
* feat: LAR 10 Send a Telegram notification for articles that are submitted but neither approved nor declined. * refactor: (LAR-10) refactoring some file * fix: (LAR-10) fix php stan error
1 parent 4f3c3b9 commit 7d707d6

File tree

8 files changed

+139
-17
lines changed

8 files changed

+139
-17
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Console\Commands;
6+
7+
use App\Models\Article;
8+
use Illuminate\Console\Command;
9+
use Illuminate\Notifications\AnonymousNotifiable;
10+
use App\Notifications\PendingArticlesNotification;
11+
12+
final class NotifyPendingArticles extends Command
13+
{
14+
protected $signature = 'lcm:notify-pending-articles';
15+
16+
protected $description = 'Send a Telegram notification for articles that are submitted but neither approved nor declined';
17+
18+
public function handle(AnonymousNotifiable $notifiable): void
19+
{
20+
$pendingArticles = Article::awaitingApproval()->get();
21+
22+
if ($pendingArticles->isNotEmpty()) {
23+
$notifiable->notify(new PendingArticlesNotification($pendingArticles));
24+
}
25+
}
26+
}

app/Console/Kernel.php

+2-1
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,12 @@ protected function schedule(Schedule $schedule): void
2323
$schedule->command('lcm:post-article-to-telegram')->everyFourHours();
2424
$schedule->command('lcm:send-unverified-mails')->weeklyOn(1, '8:00');
2525
$schedule->command('sitemap:generate')->daily();
26+
$schedule->command('lcm:notify-pending-articles')->days(2);
2627
}
2728
}
2829

2930
protected function commands(): void
3031
{
3132
$this->load(__DIR__.'/Commands');
3233
}
33-
}
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Notifications;
6+
7+
use Illuminate\Bus\Queueable;
8+
use Illuminate\Notifications\Notification;
9+
use Illuminate\Contracts\Queue\ShouldQueue;
10+
use Illuminate\Database\Eloquent\Collection;
11+
use Illuminate\Notifications\Messages\MailMessage;
12+
use NotificationChannels\Telegram\TelegramChannel;
13+
use NotificationChannels\Telegram\TelegramMessage;
14+
15+
final class PendingArticlesNotification extends Notification
16+
{
17+
use Queueable;
18+
19+
public function __construct(public Collection $pendingArticles) {}
20+
21+
public function via(mixed $notifiable): array
22+
{
23+
return [TelegramChannel::class];
24+
}
25+
26+
public function toTelegram(): TelegramMessage
27+
{
28+
$message = $this->content();
29+
30+
return TelegramMessage::create()
31+
->to(config('services.telegram-bot-api.channel'))
32+
->content($message);
33+
}
34+
35+
private function content(): string
36+
{
37+
$message = __("Pending approval articles:\n\n");
38+
foreach ($this->pendingArticles as $article) {
39+
$message .= __(
40+
"[@:username](:profile_url) submitted the article [:title](:url) on: :date\n\n", [
41+
'username' => $article->user?->username,
42+
'profile_url' => route('profile', $article->user?->username),
43+
'title' => $article->title,
44+
'url' => route('articles.show', $article->slug),
45+
'date' => $article->submitted_at->translatedFormat('d/m/Y')
46+
]
47+
);
48+
}
49+
50+
return $message;
51+
}
52+
}

lang/en.json

+4-2
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,7 @@
33
"The given :attribute has appeared in a data leak. Please choose a different :attribute.": "The given :attribute has appeared in a data leak. Please choose a different :attribute.",
44
"Verify Email Address": "Verify Email Address",
55
"Please click the button below to verify your email address.": "Please click the button below to verify your email address.",
6-
"If you did not create an account, no further action is required.": "If you did not create an account, no further action is required."
7-
}
6+
"If you did not create an account, no further action is required.": "If you did not create an account, no further action is required.",
7+
"Pending approval articles:" : "Pending approval articles:",
8+
"[@:username](:profile_url) submitted the article [:title](:url) on: :date" : "[@:username](:profile_url) submitted the article [:title](:url) on: :date"
9+
}

lang/fr.json

+4-2
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,7 @@
33
"The given :attribute has appeared in a data leak. Please choose a different :attribute.": "Le champ :attribute donné est apparu dans une fuite de données. Veuillez choisir un autre :attribute.",
44
"Verify Email Address": "Vérifier l'adresse e-mail",
55
"Please click the button below to verify your email address.": "Veuillez cliquer sur le bouton ci-dessous pour vérifier votre adresse email.",
6-
"If you did not create an account, no further action is required.": "Si vous n'avez pas créé de compte, aucune autre action n'est requise."
7-
}
6+
"If you did not create an account, no further action is required.": "Si vous n'avez pas créé de compte, aucune autre action n'est requise.",
7+
"Pending approval articles:" : "Articles soumis en attente d'approbation:",
8+
"[@:username](:profile_url) submitted the article [:title](:url) on: :date" : "[@:username](:profile_url) a soumit l'article [:title](:url) le: :date"
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use App\Console\Commands\NotifyPendingArticles;
6+
use App\Models\Article;
7+
use Illuminate\Support\Facades\Notification;
8+
9+
beforeEach(fn() => Notification::fake());
10+
11+
it('will send a notification when there are pending articles', function (): void {
12+
Article::factory()->createMany([
13+
[
14+
'submitted_at' => now(),
15+
],
16+
[
17+
'submitted_at' => now()->subDay(),
18+
'approved_at' => now(),
19+
],
20+
[
21+
'submitted_at' => now()->subDay(),
22+
'declined_at' => now(),
23+
],
24+
]);
25+
26+
$this->assertDatabaseCount('articles', 3);
27+
$this->artisan(NotifyPendingArticles::class)->assertExitCode(0);
28+
29+
Notification::assertCount(1);
30+
});
31+
32+
it('will not send a notification when there are no pending articles', function (): void {
33+
Article::factory()->createMany([
34+
[
35+
'submitted_at' => now()->subDay(),
36+
'approved_at' => now(),
37+
],
38+
[
39+
'submitted_at' => now()->subDay(),
40+
'declined_at' => now(),
41+
],
42+
]);
43+
44+
$this->assertDatabaseCount('articles', 2);
45+
$this->artisan(NotifyPendingArticles::class)->assertExitCode(0);
46+
47+
Notification::assertNothingSent();
48+
Notification::assertCount(0);
49+
});

tests/Integration/DiscussionTest.php

+1-6
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,6 @@
55
use App\Models\Activity;
66
use App\Models\Discussion;
77
use App\Models\Tag;
8-
use Illuminate\Foundation\Testing\DatabaseMigrations;
9-
use Illuminate\Foundation\Testing\RefreshDatabase;
10-
11-
uses(RefreshDatabase::class);
12-
uses(DatabaseMigrations::class);
138

149
it('can find by slug', function (): void {
1510
Discussion::factory()->create(['slug' => 'foo']);
@@ -74,4 +69,4 @@
7469

7570
// When providing a slug with invalid url characters, a random 5 character string is returned.
7671
expect($discussion->slug())->toMatch('/\w{5}/');
77-
});
72+
});

tests/Integration/ThreadTest.php

+1-6
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,6 @@
88
use App\Models\Thread;
99
use App\Models\User;
1010
use Carbon\Carbon;
11-
use Illuminate\Foundation\Testing\DatabaseMigrations;
12-
use Illuminate\Foundation\Testing\RefreshDatabase;
13-
14-
uses(RefreshDatabase::class);
15-
uses(DatabaseMigrations::class);
1611

1712
it('can find by slug', function (): void {
1813
Thread::factory()->create(['slug' => 'foo']);
@@ -192,4 +187,4 @@ function createActiveThread(): Thread
192187
$reply->save();
193188

194189
return $thread;
195-
}
190+
}

0 commit comments

Comments
 (0)