| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158 |
- <?php
-
- namespace App\Service;
-
- use PhpImap\IncomingMail;
- use PhpImap\IncomingMailAttachment;
- use Psr\Log\LoggerInterface;
- use SecIT\ImapBundle\Connection\ConnectionInterface;
- use Symfony\Component\DependencyInjection\Attribute\Target;
- use Symfony\Component\Filesystem\Filesystem;
- use Symfony\Component\Filesystem\Path;
- use App\Presentation\PortfolioPresenter as PresentationPortfolio;
- use App\Service\TelegramNotifier;
-
- readonly class MailFetcher
- {
- public function __construct(
- #[Target('finfollowConnection')]
- private ConnectionInterface $connection,
- private XmlParser $xmlParser,
- private PortfolioManager $portfolioManager,
- private LoggerInterface $logger,
- private TelegramNotifier $telegramNotifier,
- )
- {
- }
-
- public function fetchNewEmails(): void
- {
- $mailbox = $this->connection->getMailbox();
- try {
- // Get all emails (messages)
- // PHP.net imap_search criteria: http://php.net/manual/en/function.imap-search.php
- $mailsIds = $mailbox->searchMailbox('ALL');
- } catch (PhpImap\Exceptions\ConnectionException $ex) {
- $this->logger->error("IMAP connection failed: " . implode(",", $ex->getErrors('all')));
- die();
- }
-
- if (!$mailsIds) {
- $this->logger->info('Mailbox is empty');
-
- return;
- }
-
- // If '__DIR__' was defined in the first line, it will automatically
- // save all attachments to the specified directory
-
- foreach ($mailsIds as $mailId) {
- $mail = $mailbox->getMail($mailId);
-
- $this->logger->debug(print_r([
- 'date' => $mail->date,
- 'message_id' => $mail->messageId,
- 'from' => $mail->fromAddress,
- 'subject' => $mail->subject,
- 'hasAttachments' => $mail->hasAttachments(),
- ], true));
-
- if ($this->canProcessMail($mail)) {
- if ($this->processMail($mail)) {
- // for dev purposes
- //break;
- }
-
- $mailbox->deleteMail($mailId);
- $this->logger->debug("Deleted email with id=" . $mailId);
- } else {
- $this->logger->debug("Skipped mail with id=" . $mailId);
- }
- }
-
- $mailbox->disconnect();
- }
-
- private function canProcessMail(IncomingMail $mail): bool
- {
- if (!$mail->hasAttachments()) {
- return false;
- }
-
- if (mb_stripos($mail->subject, 'Broker report') === false) {
- return false;
- }
-
- if (mb_stripos($mail->fromAddress, '@bcs.ru') === false) {
- return false;
- }
-
- return true;
- }
-
- private function processMail(IncomingMail $mail): bool
- {
- $mailProcessed = false;
-
- foreach ($mail->getAttachments() as $attachment) {
- if ($attachment->mimeType != 'application/zip') {
- continue;
- }
-
- $mailProcessed |= $this->processZipArchiveAttachment($attachment);
- }
-
- return $mailProcessed;
- }
-
- private function processZipArchiveAttachment(IncomingMailAttachment $attachment): bool
- {
- $zip = new \ZipArchive();
- if ($zip->open($attachment->filePath) !== true) {
- $this->logger->error('ZIP open error: ' . $zip);
- return false;
- }
-
- $xmlFound = false;
- for ($i = 0; $i < $zip->numFiles; $i++) {
- $stat = $zip->statIndex($i);
-
- if (pathinfo($stat['name'])['extension'] !== 'xml') {
- continue;
- }
-
- $tmpdir = $this->getTmpDir();
-
- $zip->extractTo($tmpdir, [$stat['name']]);
- $this->logger->debug("Extracted '$stat[name]' into '$tmpdir'");
-
- $xmlString = file_get_contents($tmpdir . '/' . $stat['name']);
- $xml = simplexml_load_string($xmlString);
-
- $parsedPortfolio = $this->xmlParser->processXml($xml);
- $message = (new PresentationPortfolio)->toPrint($parsedPortfolio);
-
- if ($this->portfolioManager->updatePortfolio($parsedPortfolio, $xmlString)) {
- $this->telegramNotifier->notify('<pre>' . $message . '</pre>');
- }
-
- $fs = new Filesystem();
- $fs->remove($tmpdir);
-
- $xmlFound = true;
- }
-
- return $xmlFound;
- }
-
- private function getTmpDir(): string
- {
- $tmpdir = Path::normalize(sys_get_temp_dir() . '/FINFOLLOW-' . random_int(0, 100));
-
- $filesystem = new Filesystem();
- $filesystem->mkdir($tmpdir);
-
- return $tmpdir;
- }
-
- }
|