ارسال ایمیل با تاخیر

زمان مناسب نیست؟ ایمیل خیلی زود ارسال شد؟ به تأخیر انداختن ایمیل‌ها می‌تواند در بسیاری از موقعیت‌ها مفید باشد: چه به خودتان زمان بررسی بدهید و چه برای مدیریت موثر حجم کاری خود - زمان‌بندی می‌تواند بسیار مهم باشد. در اینجا سه راه وجود دارد که می توانید ارسال ایمیل را به تاخیر بیندازید و ارتباطات خود را بهینه کنید.


چشم انداز

Microsoft Outlook یک ویژگی داخلی ارائه می دهد که به شما امکان می دهد ایمیل ها را به تاخیر بیندازید. برای این کار از تابع استفاده می کنید "گزینه ها > تاخیر در تحویل":

عیب بزرگ این راه حل این است که (حتی در محیط های Exchange) Outlook باید در هنگام ارسال در سمت کلاینت اجرا شود.

گوگل

جیمیل در اینجا راه حل کمی بهتر ارائه می دهد. برای انجام این کار، قبل از ارسال ایمیل به قسمت «Schedule Send» بروید.:

ایمیل‌هایی که قبلاً ارسال شده‌اند نیز می‌توانند ظرف 30 ثانیه «لغو» شوند. برای انجام این کار، ابتدا تنظیمات مربوطه را در قسمت «تمام تنظیمات فراخوانی کنید» تنظیم کنید.:

اگر اکنون ایمیلی ارسال می‌کنید، می‌توانید آن را در این بازه زمانی در پایین سمت چپ لغو کنید:

بومرنگ

ارائه دهندگان خدمات پولی مانند Boomerang for Outlook وجود دارند که علاوه بر یادآوری یا پیگیری خودکار، ارسال ایمیل با تاخیر را نیز ارائه می دهند.:

نقطه ضعف در اینجا این است که ایمیل ها به سرورهای شخص ثالث ختم می شوند و استفاده از آنها هزینه ای دارد.

PHP

دلیل کافی برای ساختن چیزی خودتان: شما همچنین می توانید با کمک یک اسکریپت کوچک PHP به آنچه می خواهید برسید. ابتدا یک ساختار پوشه در صندوق پستی خود ایجاد می کنیم:

این نشان دهنده زمان مورد نظر حمل و نقل است. گردش کار اکنون شامل ذخیره ایمیل نوشته شده به عنوان پیش نویس به جای ارسال آن و انتقال آن به پوشه مربوطه است. سپس تمام بسته های PHP مورد نیاز را نصب می کنیم:

composer require php-imap/php-imap phpmailer/phpmailer vlucas/phpdotenv

در نهایت یکی را ایجاد می کنیم .env-فایل با داده های دسترسی و تنظیمات:

HOST_SMTP="xxx"
PORT_SMTP=465
HOST_IMAP="xxx"
PORT_IMAP=993
USERNAME="foo@bar.com"
PASSWORD="xxx"
ENCRYPTION="ssl"
FROM_ADDRESS="foo@bar.com"
FROM_NAME="Foo Bar"
FOLDER_INBOX="INBOX/DELAY"
FOLDER_OUTBOX="Gesendete Elemente"
PHP_EXECUTABLE="/usr/bin/php"

سپس اسکریپت PHP زیر بقیه را انجام می دهد:

<?php
require_once __DIR__ . '/vendor/autoload.php';
class MailDelay
{
    private \PhpImap\Mailbox $mailbox;
    private array $folders;
    public function init(): void
    {
        $this->loadEnvironmentVariables();
        $this->initMailbox();
        $this->initFolders();
        $this->processFolders();
    }
    private function loadEnvironmentVariables(): void
    {
        $dotenv = \Dotenv\Dotenv::createImmutable(__DIR__);
        $dotenv->load();
    }
    private function initMailbox(): void
    {
        $this->mailbox = new \PhpImap\Mailbox(
            '{' . $_SERVER['HOST_IMAP'] . ':' . $_SERVER['PORT_IMAP'] . '/imap/ssl}' . $_SERVER['FOLDER_INBOX'],
            $_SERVER['USERNAME'],
            $_SERVER['PASSWORD'],
            sys_get_temp_dir(),
            'UTF-8'
        );
    }
    private function initFolders(): void
    {
        $this->folders = [];
        $folders = $this->mailbox->getMailboxes('*');
        foreach ($folders as $folder) {
            if ($folder['shortpath'] === $_SERVER['FOLDER_INBOX']) {
                continue;
            }
            $this->folders[] = (object) $folder;
        }
    }
    private function processFolders(): void
    {
        foreach ($this->folders as $folder) {
            $this->mailbox->switchMailbox($folder->fullpath);
            $mailIds = $this->mailbox->searchMailbox('ALL');
            foreach ($mailIds as $mailId) {
                $preparedMail = $this->prepareMailData($mailId, $folder->shortpath);
                if (
                    $preparedMail->subject !== 'Dies ist Plain Text' &&
                    strtotime($preparedMail->time_to_send) > strtotime('now')
                ) {
                    continue;
                }
                try {
                    $this->sendMail($preparedMail);
                    echo 'Successfully sent mail #' . $preparedMail->id . '.' . PHP_EOL;
                } catch (\Exception $e) {
                    echo 'Error in sending mail #' . $preparedMail->id . ': ' . $e->getMessage() . PHP_EOL;
                }
            }
        }
        echo 'All mails have been processed.' . PHP_EOL;
    }
    private function prepareMailData(int $id, string $folder): object
    {
        $mail = $this->mailbox->getMail($id, false); // don't mark as unread
        return (object) [
            'id' => (string) $mail->id,
            'to' => $this->formatEmailAddresses($mail->to),
            'cc' => $this->formatEmailAddresses($mail->cc),
            'bcc' => $this->formatEmailAddresses($mail->bcc),
            'subject' => (string) $mail->subject,
            'content_html' => $this->convertEncoding($mail->textHtml),
            'content_plain' => $this->convertEncoding($mail->textPlain),
            'attachments' => $this->determineAttachments($mail->getAttachments()),
            'time_to_send' => $this->determineTimeToSend(explode('/', $folder)[2], $mail->date)
        ];
    }
    private function formatEmailAddresses(?array $addresses): ?array
    {
        if (empty($addresses)) {
            return null;
        }
        return array_map(
            function ($key, $value) {
                return [
                    'email' => $key,
                    'name' => $key === $value ? null : str_replace(' (' . $key . ')', '', $value)
                ];
            },
            array_keys($addresses),
            $addresses
        );
    }
    private function convertEncoding(string $text): string
    {
        return mb_detect_encoding($text, 'UTF-8, ISO-8859-1') !== 'UTF-8'
            ? \UConverter::transcode($text, 'UTF8', 'ISO-8859-1')
            : $text;
    }
    private function determineAttachments(array $attachmentsImap): array
    {
        $attachments = [];
        if (!empty($attachmentsImap)) {
            foreach ($attachmentsImap as $attachment) {
                $attachments[] = [
                    'name' => $attachment->name,
                    'file' => $attachment->filePath,
                    'disposition' => $attachment->disposition,
                    'inline_id' => $attachment->contentId
                ];
            }
        }
        return $attachments;
    }
    private function determineTimeToSend(string $delayTime, string $date): ?string
    {
        $timeToSend = null;
        if ($delayTime === 'THIS EVENING') {
            $timeToSend = date('Y-m-d', strtotime($date)) . ' 18:00:00';
        } elseif ($delayTime === 'THIS NIGHT') {
            $timeToSend =
                date('Y-m-d', strtotime($date . (date('H', strtotime($date)) >= 4 ? ' + 1 day' : ''))) . ' 03:42:00';
        } elseif ($delayTime === 'NEXT MORNING') {
            $timeToSend =
                date('Y-m-d', strtotime($date . (date('H', strtotime($date)) >= 9 ? ' + 1 day' : ''))) . ' 09:00:00';
        } elseif ($delayTime === 'NEXT WEEK') {
            $date = new \DateTime(date('Y-m-d', strtotime($date)));
            $date->modify('next monday');
            $timeToSend = $date->format('Y-m-d') . ' 09:00:00';
        }
        return $timeToSend;
    }
    private function sendMail(object $preparedMail): void
    {
        $mail = new \PHPMailer\PHPMailer\PHPMailer(true);
        $mail->isSMTP();
        $mail->Host = $_SERVER['HOST_SMTP'];
        $mail->Port = $_SERVER['PORT_SMTP'];
        $mail->Username = $_SERVER['USERNAME'];
        $mail->Password = $_SERVER['PASSWORD'];
        $mail->SMTPSecure = $_SERVER['ENCRYPTION'];
        $mail->setFrom($_SERVER['FROM_ADDRESS'], $_SERVER['FROM_NAME']);
        $mail->SMTPAuth = true;
        $mail->SMTPOptions = [
            'tls' => ['verify_peer' => false, 'verify_peer_name' => false, 'allow_self_signed' => true],
            'ssl' => ['verify_peer' => false, 'verify_peer_name' => false, 'allow_self_signed' => true]
        ];
        $mail->CharSet = 'utf-8';
        $this->addRecipients($mail, $preparedMail->to, 'addAddress');
        $this->addRecipients($mail, $preparedMail->cc, 'addCC');
        $this->addRecipients($mail, $preparedMail->bcc, 'addBCC');
        $mail->isHTML(!empty($preparedMail->content_html));
        $mail->Subject = $preparedMail->subject;
        if (!empty($preparedMail->content_html)) {
            $mail->Body = $preparedMail->content_html;
            $mail->AltBody = !empty($preparedMail->content_plain)
                ? $preparedMail->content_plain
                : strip_tags(str_replace(['<br>', '<br/>', '<br />'], "\r\n", $preparedMail->content_html));
        } else {
            $mail->Body = $preparedMail->content_plain;
        }
        $this->addAttachments($mail, $preparedMail->attachments);
        $mail->send();
        $this->mailbox->moveMail($preparedMail->id, $_SERVER['FOLDER_OUTBOX']);
    }
    private function addRecipients(\PHPMailer\PHPMailer\PHPMailer $mail, ?array $recipients, string $method): void
    {
        if (!empty($recipients)) {
            foreach ($recipients as $recipient) {
                $mail->$method($recipient['email'], $recipient['name']);
            }
        }
    }
    private function addAttachments(\PHPMailer\PHPMailer\PHPMailer $mail, array $attachments): void
    {
        if (!empty($attachments)) {
            foreach ($attachments as $attachment) {
                if (!empty($attachment['file']) && !empty($attachment['name']) && file_exists($attachment['file'])) {
                    if ($attachment['disposition'] === 'attachment') {
                        $mail->addAttachment($attachment['file'], $attachment['name']);
                    } elseif ($attachment['disposition'] === 'inline') {
                        $mail->AddEmbeddedImage(
                            $attachment['file'],
                            $attachment['inline_id'],
                            $attachment['name'],
                            'base64',
                            'image/png'
                        );
                    }
                }
            }
        }
    }
}
$md = new MailDelay();
$md->init();

برای اینکه این اسکریپت به طور مکرر اجرا شود، یک فایل bash ایجاد می کنیم:

#!/usr/bin/env bash
source $(dirname "$0")/.env
"$PHP_EXECUTABLE" $(dirname "$0")/maildelay.php

سپس این اسکریپت را هر 10 دقیقه از طریق cron job اجرا می کنیم:

*/10 * * * * /path/to/maildelay/maildelay.sh 2>&1

این راه حل به طور انعطاف پذیر قابل تنظیم، حفظ حریم خصوصی و رایگان است. با هر حساب (سازگار با IMAP) کار می کند. اما صرف نظر از اینکه آیا از عملکرد یکپارچه در Outlook یا Gmail استفاده می کنید، از ابزارهای پیشرفته مانند بومرنگ یا پیاده سازی یک راه حل فردی استفاده کنید - ارسال ایمیل ها بر اساس تغییر زمان ابزار ارزشمندی برای افزایش بهره وری شماست.

بازگشت