*/ class FollowUpClassifierListener implements IEventListener { public function __construct( private IJobList $jobList, private AiIntegrationsService $aiService, ) { } #[\Override] public function handle(Event $event): void { if (!($event instanceof NewMessagesSynchronized)) { return; } if (!$event->getMailbox()->isSpecialUse('sent') && $event->getAccount()->getMailAccount()->getSentMailboxId() !== $event->getMailbox()->getId() ) { return; } if (!$this->aiService->isLlmProcessingEnabled()) { return; } if (!$this->aiService->isLlmAvailable(FreePromptTaskType::class)) { return; } // Do not process emails older than 14D to save some processing power $notBefore = (new DateTimeImmutable('now')) ->sub(new DateInterval('P14D')); $userId = $event->getAccount()->getUserId(); foreach ($event->getMessages() as $message) { if ($message->getSentAt() < $notBefore->getTimestamp()) { continue; } $isTagged = false; foreach ($message->getTags() as $tag) { if ($tag->getImapLabel() === '$follow_up') { $isTagged = true; break; } } if ($isTagged) { continue; } $jobArguments = [ FollowUpClassifierJob::PARAM_MESSAGE_ID => $message->getMessageId(), FollowUpClassifierJob::PARAM_MAILBOX_ID => $message->getMailboxId(), FollowUpClassifierJob::PARAM_USER_ID => $userId, ]; // Delay job a bit because there might be some replies until then and we might be able // to skip the expensive LLM task $timestamp = (new DateTimeImmutable('@' . $message->getSentAt())) ->add(new DateInterval('P3DT12H')) ->getTimestamp(); $this->jobList->scheduleAfter( FollowUpClassifierJob::class, $timestamp, $jobArguments, ); } } }