File size: 4,516 Bytes
d2897cd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
<?php

namespace Mautic\EmailBundle\MonitoredEmail\Processor;

use Doctrine\ORM\EntityNotFoundException;
use Mautic\CoreBundle\Helper\EmailAddressHelper;
use Mautic\EmailBundle\EmailEvents;
use Mautic\EmailBundle\Entity\EmailReply;
use Mautic\EmailBundle\Entity\Stat;
use Mautic\EmailBundle\Event\EmailReplyEvent;
use Mautic\EmailBundle\Model\EmailStatModel;
use Mautic\EmailBundle\MonitoredEmail\Exception\ReplyNotFound;
use Mautic\EmailBundle\MonitoredEmail\Message;
use Mautic\EmailBundle\MonitoredEmail\Processor\Reply\Parser;
use Mautic\EmailBundle\MonitoredEmail\Search\ContactFinder;
use Mautic\LeadBundle\Model\LeadModel;
use Mautic\LeadBundle\Tracker\ContactTracker;
use Psr\Log\LoggerInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;

class Reply implements ProcessorInterface
{
    public function __construct(
        private EmailStatModel $emailStatModel,
        private ContactFinder $contactFinder,
        private LeadModel $leadModel,
        private EventDispatcherInterface $dispatcher,
        private LoggerInterface $logger,
        private ContactTracker $contactTracker,
        private EmailAddressHelper $addressHelper
    ) {
    }

    public function process(Message $message): void
    {
        $this->logger->debug('MONITORED EMAIL: Processing message ID '.$message->id.' for a reply');

        try {
            $parser       = new Parser($message);
            $repliedEmail = $parser->parse();
        } catch (ReplyNotFound) {
            // No hash found so bail as we won't consider this a reply
            $this->logger->debug('MONITORED EMAIL: No hash ID found in the email body');

            return;
        }

        $hashId = $repliedEmail->getStatHash();
        $result = $this->contactFinder->findByHash($hashId);
        if (!$stat = $result->getStat()) {
            // No stat found so bail as we won't consider this a reply
            $this->logger->debug('MONITORED EMAIL: Stat not found');

            return;
        }

        // A stat has been found so let's compare to the From address for the contact to prevent false positives
        $possibleFromEmails = $this->addressHelper->getVariations($stat->getLead()->getEmail());
        $fromEmail          = $this->addressHelper->cleanEmail($repliedEmail->getFromAddress());

        if (!in_array($fromEmail, $possibleFromEmails)) {
            // We can't reliably assume this email was from the originating contact
            $this->logger->debug('MONITORED EMAIL: '.implode(', ', $possibleFromEmails).' != '.$fromEmail.' so cannot confirm match');

            return;
        }

        $this->createReply($stat, $message->id);
        $this->dispatchEvent($stat);

        if (null !== $stat->getLead()) {
            $this->leadModel->getRepository()->detachEntity($stat->getLead());
        }
        $this->emailStatModel->getRepository()->detachEntity($stat);
    }

    /**
     * @param string $trackingHash
     * @param string $messageId
     */
    public function createReplyByHash($trackingHash, $messageId): void
    {
        /** @var Stat|null $stat */
        $stat = $this->emailStatModel->getRepository()->findOneBy(['trackingHash' => $trackingHash]);

        if (null === $stat) {
            throw new EntityNotFoundException("Email Stat with tracking hash {$trackingHash} was not found");
        }

        $stat->setIsRead(true);

        if (null === $stat->getDateRead()) {
            $stat->setDateRead(new \DateTime());
        }

        $this->createReply($stat, $messageId);

        $contact = $stat->getLead();

        if ($contact) {
            $this->dispatchEvent($stat);
        }
    }

    /**
     * @param string $messageId
     */
    protected function createReply(Stat $stat, $messageId)
    {
        $replies = $stat->getReplies()->filter(
            fn (EmailReply $reply): bool => $reply->getMessageId() === $messageId
        );

        if (!$replies->count()) {
            $emailReply = new EmailReply($stat, $messageId);
            $stat->addReply($emailReply);
            $this->emailStatModel->saveEntity($stat);
        }
    }

    private function dispatchEvent(Stat $stat): void
    {
        if ($this->dispatcher->hasListeners(EmailEvents::EMAIL_ON_REPLY)) {
            $this->contactTracker->setTrackedContact($stat->getLead());

            $event = new EmailReplyEvent($stat);
            $this->dispatcher->dispatch($event, EmailEvents::EMAIL_ON_REPLY);
            unset($event);
        }
    }
}