• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Freegle / Iznik / 3988

15 Apr 2026 08:41AM UTC coverage: 70.846% (-0.09%) from 70.939%
3988

push

circleci

web-flow
Merge pull request #104 from Freegle/feature/batch-jobs-dry-run-and-fixes

feat: batch jobs dry-run, feature flags, List-Unsubscribe, and user cleanup

13102 of 20045 branches covered (65.36%)

Branch coverage included in aggregate %.

520 of 900 new or added lines in 34 files covered. (57.78%)

34 existing lines in 15 files now uncovered.

92779 of 129408 relevant lines covered (71.69%)

17.29 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/iznik-batch/app/Console/Commands/Chat/NotifyMod2ModCommand.php
1
<?php
2

3
namespace App\Console\Commands\Chat;
4

5
use App\Console\Concerns\PreventsOverlapping;
6
use App\Models\ChatRoom;
7
use App\Services\ChatNotificationService;
8
use App\Services\EmailSpoolerService;
9
use App\Traits\GracefulShutdown;
10
use Illuminate\Console\Command;
11
use Illuminate\Support\Facades\Log;
12

13
class NotifyMod2ModCommand extends Command
14
{
15
    use GracefulShutdown;
16
    use PreventsOverlapping;
17

18
    /**
19
     * The name and signature of the console command.
20
     *
21
     * For manual testing, use:
22
     *   php artisan mail:chat:mod2mod --chat=123 --delay=0 --force --once
23
     *
24
     * This will send notifications for chat 123 immediately, even if already sent.
25
     */
26
    protected $signature = 'mail:chat:mod2mod
27
                            {--chat= : Process only a specific chat ID}
28
                            {--delay=30 : Delay in seconds before sending notification (use 0 for immediate)}
29
                            {--since=24 : How many hours back to look for messages}
30
                            {--force : Force sending even for already mailed messages}
31
                            {--once : Run once and exit (for manual testing)}
32
                            {--max-iterations=120 : Maximum iterations before exiting}
33
                            {--spool : Spool emails instead of sending directly}
34
                            {--dry-run : Show what would be sent without actually sending}';
35

36
    /**
37
     * The console command description.
38
     */
39
    protected $description = 'Send email notifications for unread Mod2Mod chat messages';
40

41
    /**
42
     * Execute the console command.
43
     */
44
    public function handle(ChatNotificationService $notificationService, EmailSpoolerService $spooler): int
×
45
    {
46
        if (!$this->acquireLock()) {
×
47
            $this->info('Already running, exiting.');
×
48
            return Command::SUCCESS;
×
49
        }
50

51
        try {
52
            return $this->doHandle($notificationService, $spooler);
×
53
        } finally {
54
            $this->releaseLock();
×
55
        }
56
    }
57

58
    /**
59
     * The actual command logic.
60
     */
61
    protected function doHandle(ChatNotificationService $notificationService, EmailSpoolerService $spooler): int
×
62
    {
63
        $chatId = $this->option('chat') ? (int) $this->option('chat') : null;
×
64
        $delay = (int) $this->option('delay');
×
65
        $sinceHours = (int) $this->option('since');
×
66
        $forceAll = (bool) $this->option('force');
×
67
        $runOnce = (bool) $this->option('once');
×
68
        $maxIterations = $runOnce ? 1 : (int) $this->option('max-iterations');
×
69
        $spool = (bool) $this->option('spool');
×
NEW
70
        $dryRun = (bool) $this->option('dry-run');
×
71

NEW
72
        if ($dryRun) {
×
NEW
73
            $this->info('DRY RUN — no emails will be sent.');
×
74
        }
75

76
        // Inject spooler if spooling is enabled.
77
        if ($spool) {
×
78
            $notificationService->setSpooler($spooler);
×
79
        }
80

81
        $this->registerShutdownHandlers();
×
82

83
        Log::info('Starting Mod2Mod chat notification', [
×
84
            'chat_id' => $chatId,
×
85
            'delay' => $delay,
×
86
            'since_hours' => $sinceHours,
×
87
            'force' => $forceAll,
×
88
            'once' => $runOnce,
×
89
            'spool' => $spool,
×
90
        ]);
×
91

92
        $this->info('Processing Mod2Mod chat notifications...');
×
93
        if ($runOnce) {
×
94
            $this->info('Running once (manual test mode).');
×
95
        }
96
        $totalNotified = 0;
×
97
        $iteration = 0;
×
98

99
        do {
100
            if ($this->shouldAbort()) {
×
101
                $this->warn('Aborting due to shutdown signal.');
×
102
                break;
×
103
            }
104

105
            $startTime = now();
×
106
            $this->line('Starting iteration ' . ($iteration + 1) . ' at ' . $startTime->format('Y-m-d H:i:s'));
×
107

108
            $count = $notificationService->notifyByEmail(
×
109
                ChatRoom::TYPE_MOD2MOD,
×
110
                $chatId,
×
111
                $delay,
×
112
                $sinceHours,
×
NEW
113
                $forceAll,
×
NEW
114
                $dryRun
×
UNCOV
115
            );
×
116

117
            $totalNotified += $count;
×
118
            $iteration++;
×
119

120
            if ($count > 0) {
×
121
                $this->info("Sent {$count} notifications.");
×
122
            } else {
123
                if ($runOnce) {
×
124
                    $this->info("No messages to notify.");
×
125
                } else {
126
                    // No messages to process, sleep before next iteration.
127
                    sleep(1);
×
128
                }
129
            }
130
        } while ($iteration < $maxIterations);
×
131

132
        $this->newLine();
×
133
        $this->info("Mod2Mod notification complete. Total notifications sent: {$totalNotified}");
×
134

135
        Log::info('Mod2Mod chat notification complete', [
×
136
            'total_notified' => $totalNotified,
×
137
            'iterations' => $iteration,
×
138
        ]);
×
139

140
        return Command::SUCCESS;
×
141
    }
142
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc