55namespace PhpList \Core \Domain \Messaging \Command ;
66
77use Exception ;
8- use PhpList \Core \Domain \Messaging \Model \Bounce ;
9- use PhpList \Core \Domain \Messaging \Model \UserMessage ;
10- use PhpList \Core \Domain \Messaging \Model \UserMessageBounce ;
8+ use PhpList \Core \Domain \Messaging \Service \ConsecutiveBounceHandler ;
119use PhpList \Core \Domain \Messaging \Service \Processor \BounceProtocolProcessor ;
1210use PhpList \Core \Domain \Messaging \Service \Processor \AdvancedBounceRulesProcessor ;
1311use PhpList \Core \Domain \Messaging \Service \Processor \UnidentifiedBounceReprocessor ;
1412use PhpList \Core \Domain \Messaging \Service \LockService ;
15- use PhpList \Core \Domain \Messaging \Service \Manager \BounceManager ;
16- use PhpList \Core \Domain \Subscription \Repository \SubscriberRepository ;
17- use PhpList \Core \Domain \Subscription \Service \Manager \SubscriberHistoryManager ;
18- use PhpList \Core \Domain \Subscription \Service \Manager \SubscriberManager ;
1913use Psr \Log \LoggerInterface ;
2014use Symfony \Component \Console \Attribute \AsCommand ;
2115use Symfony \Component \Console \Command \Command ;
@@ -47,16 +41,13 @@ protected function configure(): void
4741 }
4842
4943 public function __construct (
50- private readonly BounceManager $ bounceManager ,
5144 private readonly LockService $ lockService ,
5245 private readonly LoggerInterface $ logger ,
53- private readonly SubscriberManager $ subscriberManager ,
54- private readonly SubscriberHistoryManager $ subscriberHistoryManager ,
55- private readonly SubscriberRepository $ subscriberRepository ,
5646 /** @var iterable<BounceProtocolProcessor> */
5747 private readonly iterable $ protocolProcessors ,
5848 private readonly AdvancedBounceRulesProcessor $ advancedRulesProcessor ,
5949 private readonly UnidentifiedBounceReprocessor $ unidentifiedBounceReprocessor ,
50+ private readonly ConsecutiveBounceHandler $ consecutiveBounceHandler ,
6051 ) {
6152 parent ::__construct ();
6253 }
@@ -83,6 +74,8 @@ protected function execute(InputInterface $input, OutputInterface $output): int
8374 try {
8475 $ io ->title ('Processing bounces ' );
8576 $ protocol = (string )$ input ->getOption ('protocol ' );
77+ $ unsubscribeThreshold = (int )$ input ->getOption ('unsubscribe-threshold ' );
78+ $ blacklistThreshold = (int )$ input ->getOption ('blacklist-threshold ' );
8679
8780 $ downloadReport = '' ;
8881
@@ -103,15 +96,9 @@ protected function execute(InputInterface $input, OutputInterface $output): int
10396 $ downloadReport .= $ processor ->process ($ input , $ io );
10497 $ this ->unidentifiedBounceReprocessor ->process ($ io );
10598 $ this ->advancedRulesProcessor ->process ($ io , (int )$ input ->getOption ('rules-batch-size ' ));
106-
107- $ this ->handleConsecutiveBounces (
108- $ io ,
109- (int )$ input ->getOption ('unsubscribe-threshold ' ),
110- (int )$ input ->getOption ('blacklist-threshold ' )
111- );
99+ $ this ->consecutiveBounceHandler ->handle ($ io , $ unsubscribeThreshold , $ blacklistThreshold );
112100
113101 $ this ->logger ->info ('Bounce processing completed ' , ['downloadReport ' => $ downloadReport ]);
114-
115102 $ io ->success ('Bounce processing completed. ' );
116103
117104 return Command::SUCCESS ;
@@ -124,59 +111,4 @@ protected function execute(InputInterface $input, OutputInterface $output): int
124111 $ this ->lockService ->release ($ lock );
125112 }
126113 }
127-
128- private function handleConsecutiveBounces (SymfonyStyle $ io , int $ unsubscribeThreshold , int $ blacklistThreshold ): void
129- {
130- $ io ->section ('Identifying consecutive bounces ' );
131- $ users = $ this ->subscriberRepository ->distinctUsersWithBouncesConfirmedNotBlacklisted ();
132- $ total = count ($ users );
133- if ($ total === 0 ) {
134- $ io ->writeln ('Nothing to do ' );
135- return ;
136- }
137- $ usercnt = 0 ;
138- foreach ($ users as $ user ) {
139- $ usercnt ++;
140- $ history = $ this ->bounceManager ->getUserMessageHistoryWithBounces ($ user );
141- $ cnt = 0 ; $ removed = false ; $ msgokay = false ; $ unsubscribed = false ;
142- foreach ($ history as $ bounce ) {
143- /** @var $bounce array{um: UserMessage, umb: UserMessageBounce|null, b: Bounce|null} */
144- if (
145- stripos ($ bounce ['b ' ]->getStatus () ?? '' , 'duplicate ' ) === false
146- && stripos ($ bounce ['b ' ]->getComment () ?? '' , 'duplicate ' ) === false
147- ) {
148- if ($ bounce ['b ' ]->getId ()) {
149- $ cnt ++;
150- if ($ cnt >= $ unsubscribeThreshold ) {
151- if (!$ unsubscribed ) {
152- $ this ->subscriberManager ->markUnconfirmed ($ user ->getId ());
153- $ this ->subscriberHistoryManager ->addHistory (
154- subscriber: $ user ,
155- message: 'Auto Unconfirmed ' ,
156- details: sprintf ('Subscriber auto unconfirmed for %d consecutive bounces ' , $ cnt )
157- );
158- $ unsubscribed = true ;
159- }
160- if ($ blacklistThreshold > 0 && $ cnt >= $ blacklistThreshold ) {
161- $ this ->subscriberManager ->blacklist (
162- subscriber: $ user ,
163- reason: sprintf ('%d consecutive bounces, threshold reached ' , $ cnt )
164- );
165- $ removed = true ;
166- }
167- }
168- } else {
169- break ;
170- }
171- }
172- if ($ removed ) {
173- break ;
174- }
175- }
176- if ($ usercnt % 5 === 0 ) {
177- $ io ->writeln (sprintf ('processed %d out of %d subscribers ' , $ usercnt , $ total ));
178- }
179- }
180- $ io ->writeln (sprintf ('total of %d subscribers processed ' , $ total ));
181- }
182114}
0 commit comments