1111use PhpList \Core \Domain \Messaging \Model \UserMessageBounce ;
1212use PhpList \Core \Domain \Messaging \Service \BounceProcessingService ;
1313use PhpList \Core \Domain \Messaging \Service \Processor \BounceProtocolProcessor ;
14+ use PhpList \Core \Domain \Messaging \Service \Processor \AdvancedBounceRulesProcessor ;
1415use PhpList \Core \Domain \Messaging \Service \LockService ;
1516use PhpList \Core \Domain \Messaging \Service \Manager \BounceManager ;
16- use PhpList \Core \Domain \Messaging \Service \Manager \BounceRuleManager ;
1717use PhpList \Core \Domain \Subscription \Repository \SubscriberRepository ;
1818use PhpList \Core \Domain \Subscription \Service \Manager \SubscriberHistoryManager ;
1919use PhpList \Core \Domain \Subscription \Service \Manager \SubscriberManager ;
@@ -49,7 +49,6 @@ protected function configure(): void
4949
5050 public function __construct (
5151 private readonly BounceManager $ bounceManager ,
52- private readonly BounceRuleManager $ ruleManager ,
5352 private readonly LockService $ lockService ,
5453 private readonly LoggerInterface $ logger ,
5554 private readonly SubscriberManager $ subscriberManager ,
@@ -58,6 +57,7 @@ public function __construct(
5857 private readonly BounceProcessingService $ processingService ,
5958 /** @var iterable<BounceProtocolProcessor> */
6059 private readonly iterable $ protocolProcessors ,
60+ private readonly AdvancedBounceRulesProcessor $ advancedRulesProcessor ,
6161 ) {
6262 parent ::__construct ();
6363 }
@@ -97,28 +97,23 @@ protected function execute(InputInterface $input, OutputInterface $output): int
9797
9898 if ($ processor === null ) {
9999 $ io ->error ('Unsupported protocol: ' .$ protocol );
100+
100101 return Command::FAILURE ;
101102 }
102103
103104 $ downloadReport .= $ processor ->process ($ input , $ io );
104105
105- // Reprocess unidentified bounces (status = "unidentified bounce")
106106 $ this ->reprocessUnidentified ($ io );
107107
108- // Advanced bounce rules
109- $ this ->processAdvancedRules ($ io , (int )$ input ->getOption ('rules-batch-size ' ));
108+ $ this ->advancedRulesProcessor ->process ($ io , (int )$ input ->getOption ('rules-batch-size ' ));
110109
111- // Identify and unconfirm users with consecutive bounces
112110 $ this ->handleConsecutiveBounces (
113111 $ io ,
114112 (int )$ input ->getOption ('unsubscribe-threshold ' ),
115113 (int )$ input ->getOption ('blacklist-threshold ' )
116114 );
117115
118- // Summarize and report (email or log)
119- $ this ->logger ->info ('Bounce processing completed ' , [
120- 'downloadReport ' => $ downloadReport ,
121- ]);
116+ $ this ->logger ->info ('Bounce processing completed ' , ['downloadReport ' => $ downloadReport ]);
122117
123118 $ io ->success ('Bounce processing completed. ' );
124119
@@ -159,117 +154,6 @@ private function reprocessUnidentified(SymfonyStyle $io): void
159154 $ io ->writeln (sprintf ('%d bounces were re-processed and %d bounces were re-identified ' , $ reparsed , $ reidentified ));
160155 }
161156
162- private function processAdvancedRules (SymfonyStyle $ io , int $ batchSize ): void
163- {
164- $ io ->section ('Processing bounces based on active bounce rules ' );
165- $ rules = $ this ->ruleManager ->loadActiveRules ();
166- if (!$ rules ) {
167- $ io ->writeln ('No active rules ' );
168- return ;
169- }
170-
171- $ total = $ this ->bounceManager ->getUserMessageBounceCount ();
172- $ fromId = 0 ;
173- $ matched = 0 ;
174- $ notmatched = 0 ;
175- $ counter = 0 ;
176-
177- while ($ counter < $ total ) {
178- $ batch = $ this ->bounceManager ->fetchUserMessageBounceBatch ($ fromId , $ batchSize );
179- $ counter += count ($ batch );
180- $ io ->writeln (sprintf ('processed %d out of %d bounces for advanced bounce rules ' , min ($ counter , $ total ), $ total ));
181- foreach ($ batch as $ row ) {
182- $ fromId = $ row ['umb ' ]->getId ();
183- // $row has: bounce(header,data,id), umb(user,message,bounce)
184- $ text = $ row ['bounce ' ]->getHeader ()."\n\n" .$ row ['bounce ' ]->getData ();
185- $ rule = $ this ->ruleManager ->matchBounceRules ($ text , $ rules );
186- $ userId = (int )$ row ['umb ' ]->getUserId ();
187- $ bounce = $ row ['bounce ' ];
188- $ userdata = $ userId ? $ this ->subscriberManager ->getSubscriberById ($ userId ) : null ;
189- $ confirmed = $ userdata ?->isConfirmed() ?? false ;
190- $ blacklisted = $ userdata ?->isBlacklisted() ?? false ;
191-
192- if ($ rule ) {
193- $ this ->ruleManager ->incrementCount ($ rule );
194- $ rule ->setCount ($ rule ->getCount () + 1 );
195- $ this ->ruleManager ->linkRuleToBounce ($ rule , $ bounce );
196-
197- switch ($ rule ->getAction ()) {
198- case 'deleteuser ' :
199- if ($ userdata ) {
200- $ this ->logger ->info ('User deleted by bounce rule ' , ['user ' => $ userdata ->getEmail (), 'rule ' => $ rule ->getId ()]);
201- $ this ->subscriberManager ->deleteSubscriber ($ userdata );
202- }
203- break ;
204- case 'unconfirmuser ' :
205- if ($ userdata && $ confirmed ) {
206- $ this ->subscriberManager ->markUnconfirmed ($ userId );
207- $ this ->subscriberHistoryManager ->addHistory ($ userdata , 'Auto Unconfirmed ' , 'Subscriber auto unconfirmed for bounce rule ' .$ rule ->getId ());
208- }
209- break ;
210- case 'deleteuserandbounce ' :
211- if ($ userdata ) {
212- $ this ->subscriberManager ->deleteSubscriber ($ userdata );
213- }
214- $ this ->bounceManager ->delete ($ bounce );
215- break ;
216- case 'unconfirmuseranddeletebounce ' :
217- if ($ userdata && $ confirmed ) {
218- $ this ->subscriberManager ->markUnconfirmed ($ userId );
219- $ this ->subscriberHistoryManager ->addHistory ($ userdata , 'Auto unconfirmed ' , 'Subscriber auto unconfirmed for bounce rule ' .$ rule ->getId ());
220- }
221- $ this ->bounceManager ->delete ($ bounce );
222- break ;
223- case 'decreasecountconfirmuseranddeletebounce ' :
224- if ($ userdata ) {
225- $ this ->subscriberManager ->decrementBounceCount ($ userdata );
226- if (!$ confirmed ) {
227- $ this ->subscriberManager ->markConfirmed ($ userId );
228- $ this ->subscriberHistoryManager ->addHistory ($ userdata , 'Auto confirmed ' , 'Subscriber auto confirmed for bounce rule ' .$ rule ->getId ());
229- }
230- }
231- $ this ->bounceManager ->delete ($ bounce );
232- break ;
233- case 'blacklistuser ' :
234- if ($ userdata && !$ blacklisted ) {
235- $ this ->subscriberManager ->blacklist ($ userdata , 'Subscriber auto blacklisted by bounce rule ' .$ rule ->getId ());
236- $ this ->subscriberHistoryManager ->addHistory ($ userdata , 'Auto Unsubscribed ' , 'User auto unsubscribed for bounce rule ' .$ rule ->getId ());
237- }
238- break ;
239- case 'blacklistuseranddeletebounce ' :
240- if ($ userdata && !$ blacklisted ) {
241- $ this ->subscriberManager ->blacklist ($ userdata , 'Subscriber auto blacklisted by bounce rule ' .$ rule ->getId ());
242- $ this ->subscriberHistoryManager ->addHistory ($ userdata , 'Auto Unsubscribed ' , 'User auto unsubscribed for bounce rule ' .$ rule ->getId ());
243- }
244- $ this ->bounceManager ->delete ($ bounce );
245- break ;
246- case 'blacklistemail ' :
247- if ($ userdata ) {
248- $ this ->subscriberManager ->blacklist ($ userdata , 'Email address auto blacklisted by bounce rule ' .$ rule ->getId ());
249- $ this ->subscriberHistoryManager ->addHistory ($ userdata , 'Auto Unsubscribed ' , 'email auto unsubscribed for bounce rule ' .$ rule ->getId ());
250- }
251- break ;
252- case 'blacklistemailanddeletebounce ' :
253- if ($ userdata ) {
254- $ this ->subscriberManager ->blacklist ($ userdata , 'Email address auto blacklisted by bounce rule ' .$ rule ->getId ());
255- $ this ->subscriberHistoryManager ->addHistory ($ userdata , 'Auto Unsubscribed ' , 'User auto unsubscribed for bounce rule ' .$ rule ->getId ());
256- }
257- $ this ->bounceManager ->delete ($ bounce );
258- break ;
259- case 'deletebounce ' :
260- $ this ->bounceManager ->delete ($ bounce );
261- break ;
262- }
263- $ matched ++;
264- } else {
265- $ notmatched ++;
266- }
267- }
268- }
269- $ io ->writeln (sprintf ('%d bounces processed by advanced processing ' , $ matched ));
270- $ io ->writeln (sprintf ('%d bounces were not matched by advanced processing rules ' , $ notmatched ));
271- }
272-
273157 private function handleConsecutiveBounces (SymfonyStyle $ io , int $ unsubscribeThreshold , int $ blacklistThreshold ): void
274158 {
275159 $ io ->section ('Identifying consecutive bounces ' );
@@ -324,5 +208,4 @@ private function handleConsecutiveBounces(SymfonyStyle $io, int $unsubscribeThre
324208 }
325209 $ io ->writeln (sprintf ('total of %d subscribers processed ' , $ total ));
326210 }
327-
328211}
0 commit comments