تكوين واجهة برمجة تطبيقات Gmail لاستبدال امتداد PHP IMAP والعمل مع بروتوكول OAuth2

مرة واحدة من المحظوظين ، لم تكن مستعدة لحقيقة أنه اعتبارًا من 15 فبراير 2021 ، سيتم تشغيل تفويض Gmail والمنتجات الأخرى فقط من خلال OAuth ، لقد قرأت المقالة " ملحق دفن Google PHP IMAP " وبدأت حزين في اتخاذ إجراء بشأن استبدال ملحق PHP IMAP مشروعك على Google API. كانت هناك أسئلة أكثر من الإجابات ، لذلك قمت بكتابة دليل في نفس الوقت.



تم استخدام PHP IMAP للمهام التالية:



  1. إزالة الرسائل القديمة من علب البريد . لسوء الحظ ، في لوحة التحكم الخاصة بحساب G Suite للشركة ، يمكنك فقط تهيئة الفترة لحذف الرسائل من جميع صناديق بريد المؤسسة بعد N أيام من الاستلام. ومع ذلك ، فأنا بحاجة إلى حذف الرسائل فقط في علب بريد محددة وبعد عدد مختلف من الأيام بعد الاستلام.
  2. تصفية وتحليل وتمييز الحروف . يتم إرسال الكثير من الرسائل من موقعنا في الوضع التلقائي ، وبعضها لا يصل إلى المرسل إليهم ، وبالتالي تأتي التقارير عنها. من الضروري التقاط هذه التقارير وتفكيكها والعثور على عميل عن طريق البريد الإلكتروني وتشكيل خطاب يمكن قراءته من قبل الإنسان للمدير ، حتى يتمكن من الاتصال بالعميل وتوضيح أهمية عنوان البريد الإلكتروني.


سنقوم بحل هاتين المهمتين باستخدام واجهة برمجة تطبيقات Gmail في هذه المقالة (وفي نفس الوقت نقوم بتعطيل الوصول للتطبيقات غير الآمنة في إعدادات صندوق البريد ، والتي تم تمكينها لـ PHP IMAP للعمل ، وفي الواقع ، ستتوقف عن العمل في يوم رهيب في فبراير 2021). سنستخدم ما يسمى بحساب الخدمة لتطبيق Gmail ، والذي ، مع التكوين المناسب ، يجعل من الممكن الاتصال بجميع صناديق البريد الخاصة بالمؤسسة وتنفيذ أي إجراءات فيها.



1. نقوم بإنشاء مشروع في Google API Developer Console



بمساعدة هذا المشروع ، سننفذ تفاعل API مع Gmail ، وسننشئ فيه نفس حساب الخدمة.



لإنشاء مشروع:



  1. انتقل إلى وحدة تحكم مطوري واجهة برمجة تطبيقات Google وقم بتسجيل الدخول بصفتك مشرف G Suite (حسنًا ، أو من هو المستخدم الخاص بك هناك بجميع الحقوق)
  2. نحن نبحث عن زر "إنشاء مشروع".



    لقد وجدت هنا:
    image



    ثم هنا:
    image



    املأ اسم المشروع واحفظ:



    إنشاء المشروع
    image



  3. انتقل إلى المشروع وانقر على زر "تمكين API والخدمات":



    تمكين API والخدمات
    image



    اختيار Gmail API



2. إنشاء حساب الخدمة وتكوينه



للقيام بذلك ، يمكنك استخدام الدليل الرسمي أو متابعة القراءة:



  1. انتقل إلى Gmail API المضافة ، وانقر فوق الزر "إنشاء بيانات اعتماد" وحدد "حساب الخدمة":



    إنشاء حساب الخدمة
    image



    املأ شيئًا وانقر على "إنشاء":



    تفاصيل حساب الخدمة
    image



    يمكن ترك كل شيء آخر فارغًا:



    حقوق الوصول لحساب الخدمة
    image



    image



  2. , . G Suite, « — API».



    API
    image

    image



  3. « »:



    image



    «», « » , « OAuth» — :



    - https://mail.google.com/ -

    - https://www.googleapis.com/auth/gmail.modify -

    - https://www.googleapis.com/auth/gmail.readonly -

    - https://www.googleapis.com/auth/gmail.metadata -




    image

    image



  4. « G Suite»:



    image



    وقم أيضًا بملء اسم منتجك في الحقل أدناه.

  5. أنت الآن بحاجة إلى إنشاء مفتاح حساب خدمة: هذا ملف يجب أن يكون متاحًا في تطبيقك. هو ، في الواقع ، سيتم استخدامه للحصول على إذن.



    للقيام بذلك ، من صفحة "بيانات الاعتماد" في مشروعك ، اتبع الرابط "إدارة حسابات الخدمة":



    شهاداته
    image



    وحدد "إجراءات - إنشاء مفتاح" ، اكتب: JSON:



    إدارة حساب الخدمة
    image



    بعد ذلك ، سيتم إنشاء ملف مفتاح وتنزيله على جهاز الكمبيوتر الخاص بك ، والذي يجب وضعه في مشروعك ومنحه حق الوصول إليه عند استدعاء Gmail API.



هذا يكمل إعداد Gmail API ، ثم سيكون هناك القليل من كود cocoa الخاص بي ، في الواقع ، تنفيذ الوظائف التي تم حلها حتى الآن بواسطة امتداد IMAP PHP.



3. كتابة الكود



وهناك وثائق رسمية جيدة جدا ( فوق و انقر ) ل جوجل API ، التي استعملتها. لكن منذ أن بدأت في كتابة دليل مفصل ، سأرفق كود الكاكاو الخاص بي.



لذلك ، أولاً وقبل كل شيء ، قمنا بتثبيت مكتبة عميل Google (apiclient) باستخدام الملحن:



composer require google/apiclient



(في البداية ، كخبير أدبي حقيقي ، قمت بتثبيت الإصدار 2.0 من عميل api ، كما هو موضح في PHP Quickstart ، ولكن في البداية ، سقطت جميع أنواع vornings والإنذارات على PHP 7.4 ، لذلك لا أنصحك بفعل الشيء نفسه)



بعد ذلك ، بناءً على أمثلة من الوثائق الرسمية ، نكتب فصلنا الخاص للعمل مع Gmail ، ولا ننسى تحديد ملف مفتاح حساب الخدمة:



فئة للعمل مع Gmail
<?php
//     Gmail
class GmailAPI
{
    private $credentials_file = __DIR__ . '/../Gmail/credentials.json'; //   

    // ---------------------------------------------------------------------------------------------
    /**
     *   Google_Service_Gmail Authorized Gmail API instance
     *
     * @param  string $strEmail  
     * @return Google_Service_Gmail Authorized Gmail API instance
     * @throws Exception
     */
    function getService(string $strEmail){
        //    
        try{
            $client = new Google_Client();
            $client->setAuthConfig($this->credentials_file);
            $client->setApplicationName('My Super Project');
            $client->setScopes(Google_Service_Gmail::MAIL_GOOGLE_COM);
            $client->setSubject($strEmail);
            $service = new Google_Service_Gmail($client);
        }catch (Exception $e) {
            throw new \Exception('   getService: '.$e->getMessage());
        }
        return $service;
    }
    // ---------------------------------------------------------------------------------------------

    /**
     *    ID    
     *
     * @param  Google_Service_Gmail $service Authorized Gmail API instance.
     * @param  string $strEmail  
     * @param  array $arrOptionalParams      
     *         Gmail  after: 2020/08/20 in:inbox label:
     *      q  $opt_param
     * @return array  ID     array('arrErrors' => $arrErrors),   
     * @throws Exception
     */
    function listMessageIDs(Google_Service_Gmail $service, string $strEmail, array $arrOptionalParams = array()) {
        $arrIDs = array(); //  ID 

        $pageToken = NULL; //     
        $messages = array(); //    

        //  
        $opt_param = array();
        //    ,       Gmail      q
        if (count($arrOptionalParams)) $opt_param['q'] = str_replace('=', ':', http_build_query($arrOptionalParams, null, ' '));

        //   ,   ,     
        do {
            try {
                if ($pageToken) {
                    $opt_param['pageToken'] = $pageToken;
                }
                $messagesResponse = $service->users_messages->listUsersMessages($strEmail, $opt_param);
                if ($messagesResponse->getMessages()) {
                    $messages = array_merge($messages, $messagesResponse->getMessages());
                    $pageToken = $messagesResponse->getNextPageToken();
                }
            } catch (Exception $e) {
                throw new \Exception('   listMessageIDs: '.$e->getMessage());
            }
        } while ($pageToken);

        //   ID  
        if (count($messages)) {
            foreach ($messages as $message) {
                $arrIDs[] = $message->getId();
            }
        }
        return $arrIDs;
    }
    // ---------------------------------------------------------------------------------------------

    /**
     *      ID  batchDelete
     *
     * @param  Google_Service_Gmail $service Authorized Gmail API instance.
     * @param  string $strEmail  
     * @param  array $arrIDs  ID      listMessageIDs
     * @throws Exception
     */
    function deleteMessages(Google_Service_Gmail $service, string $strEmail, array $arrIDs){
        //      1000 ,      batchDelete
        $arrParts = array_chunk($arrIDs, 999);
        if (count($arrParts)){
            foreach ($arrParts as $arrPartIDs){
                try{
                    //     
                    $objBatchDeleteMessages = new Google_Service_Gmail_BatchDeleteMessagesRequest();
                    //   
                    $objBatchDeleteMessages->setIds($arrPartIDs);
                    //  
                    $service->users_messages->batchDelete($strEmail,$objBatchDeleteMessages);
                }catch (Exception $e) {
                    throw new \Exception('   deleteMessages: '.$e->getMessage());
                }
            }
        }
    }
    // ---------------------------------------------------------------------------------------------

    /**
     *     get
     *
     * @param  Google_Service_Gmail $service Authorized Gmail API instance.
     * @param  string $strEmail  
     * @param  string $strMessageID ID 
     * @param  string $strFormat The format to return the message in.
     * Acceptable values are:
     * "full": Returns the full email message data with body content parsed in the payload field; the raw field is not used. (default)
     * "metadata": Returns only email message ID, labels, and email headers.
     * "minimal": Returns only email message ID and labels; does not return the email headers, body, or payload.
     * "raw": Returns the full email message data with body content in the raw field as a base64url encoded string; the payload field is not used.
     * @param  array $arrMetadataHeaders When given and format is METADATA, only include headers specified.
     * @return  object Message
     * @throws Exception
     */
    function getMessage(Google_Service_Gmail $service, string $strEmail, string $strMessageID, string $strFormat = 'full', array $arrMetadataHeaders = array()){
        $arrOptionalParams = array(
            'format' => $strFormat // ,    
        );
        //   - metadata,     
        if (($strFormat == 'metadata') and count($arrMetadataHeaders))
            $arrOptionalParams['metadataHeaders'] = implode(',',$arrMetadataHeaders);

        try{
            $objMessage = $service->users_messages->get($strEmail, $strMessageID,$arrOptionalParams);
            return $objMessage;
        }catch (Exception $e) {
            throw new \Exception('   getMessage: '.$e->getMessage());
        }
    }
    // ---------------------------------------------------------------------------------------------

    /**
     *   ,    
     *
     * @param  Google_Service_Gmail $service Authorized Gmail API instance.
     * @param  string $strEmail  
     * @return  object $objLabels -  -  
     * @throws Exception
     */
    function listLabels(Google_Service_Gmail $service, string $strEmail){
        try{
            $objLabels = $service->users_labels->listUsersLabels($strEmail);
            return $objLabels;
        }catch (Exception $e) {
            throw new \Exception('   listLabels: '.$e->getMessage());
        }
    }
    // ---------------------------------------------------------------------------------------------

    /**
     *     ()  
     *
     * @param  Google_Service_Gmail $service Authorized Gmail API instance.
     * @param  string $strEmail  
     * @param  string $strMessageID ID 
     * @param  array $arrAddLabelIds  ID ,     
     * @param  array $arrRemoveLabelIds  ID ,     
     * @return  object Message -  
     * @throws Exception
     */
    function modifyLabels(Google_Service_Gmail $service, string $strEmail, string $strMessageID, array $arrAddLabelIds = array(), array $arrRemoveLabelIds = array()){
        try{
            $objPostBody = new Google_Service_Gmail_ModifyMessageRequest();
            $objPostBody->setAddLabelIds($arrAddLabelIds);
            $objPostBody->setRemoveLabelIds($arrRemoveLabelIds);
            $objMessage = $service->users_messages->modify($strEmail,$strMessageID,$objPostBody);
            return $objMessage;
        }catch (Exception $e) {
            throw new \Exception('   modifyLabels: '.$e->getMessage());
        }
    }
    // ---------------------------------------------------------------------------------------------

}




عندما نتفاعل مع Gmail ، فإن أول شيء نفعله هو استدعاء وظيفة getService ($ strEmail) لفئة GmailAPI ، والتي تُرجع كائنًا "مرخصًا" للعمل مع صندوق بريد $ strEmail. علاوة على ذلك ، تم تمرير هذا الكائن بالفعل إلى أي وظيفة أخرى لأداء الإجراءات التي نحتاجها مباشرة. تؤدي جميع الوظائف الأخرى في فئة GmailAPI مهام محددة بالفعل:



  • listMessageIDs - البحث عن الرسائل وفقًا للمعايير المحددة وإرجاع المعرف الخاص بها (يجب أن تكون سلسلة البحث التي تم تمريرها إلى listUsersMessages Gmail API مشابهة لسلسلة البحث في واجهة الويب لصندوق البريد) ،
  • deleteMessages - يحذف الرسائل ذات المعرفات التي تم تمريرها إليها (تقوم وظيفة batchDelete API Gmail بحذف ما لا يزيد عن 1000 رسالة في مسار واحد ، لذلك اضطررت إلى تقسيم مجموعة المعرفات التي تم تمريرها إلى الوظيفة إلى عدة مصفوفات من 999 حرفًا لكل منها وإجراء الحذف عدة مرات)
  • getMessage - يحصل على جميع المعلومات حول الرسالة التي تم تمرير المعرف إليها ،
  • listLabels - تُرجع قائمة العلامات في صندوق البريد (لقد استخدمتها للحصول على معرف العلامة التي تم إنشاؤها في الأصل في واجهة الويب لصندوق البريد وتم تعيينها للرسائل المطلوبة)
  • تعديل التسميات - إضافة أو إزالة العلامات إلى الرسالة


بعد ذلك ، لدينا مهمة حذف الحروف القديمة في صناديق البريد المختلفة. في الوقت نفسه ، نعتبر أن الرسائل القديمة قد استلمت عدد الأيام منذ كل صندوق بريد. لإنجاز هذه المهمة ، نكتب البرنامج النصي التالي ، والذي يتم تشغيله يوميًا بواسطة cron:



إزالة رسائل البريد الإلكتروني القديمة
<?php
/**
 *      Gmail
 *      
 */
require __DIR__ .'/../general/config/config.php'; //   
require __DIR__ .'/../vendor/autoload.php'; //   

//       
$arrMailBoxesForClean = array(
    'a@domain.com' => 30,
    'b@domain.com' => 30,
    'c@domain.com' => 7,
    'd@domain.com' => 7,
    'e@domain.com' => 7,
    'f@domain.com' => 1
);

$arrErrors = array(); //  
$objGmailAPI = new GmailAPI(); //     GMail

//     ,      
foreach ($arrMailBoxesForClean as $strEmail => $intDays) {
    try{
        //    
        $service = $objGmailAPI->getService($strEmail);
        //       
        $arrParams = array('before' => date('Y/m/d', (time() - 60 * 60 * 24 * $intDays)));
        //   ,   
        $arrIDs = $objGmailAPI->listMessageIDs($service,$strEmail,$arrParams);
        //     ID   $arrIDs
        if (count($arrIDs)) $objGmailAPI->deleteMessages($service,$strEmail,$arrIDs);
        //    
        unset($service,$arrIDs);
    }catch (Exception $e) {
        $arrErrors[] = $e->getMessage();
    }
}

if (count($arrErrors)){
    $strTo = 'my_email@domain.com';
    $strSubj = '       ';
    $strMessage = '         :'.
        '<ul><li>'.implode('</li><li>',$arrErrors).'</li></ul>'.
        '<br/>URL: '.filter_input(INPUT_SERVER, 'REQUEST_URI', FILTER_SANITIZE_URL);
    $objMailSender = new mailSender();
    $objMailSender->sendMail($strTo,$strSubj,$strMessage);
}




يتصل البرنامج النصي بكل صندوق بريد محدد ، ويختار الحروف القديمة ويحذفها.



يتم حل مهمة إنشاء تقارير للمدير حول رسائل البريد الإلكتروني التي لم يتم تسليمها بناءً على التقارير التلقائية من خلال البرنامج النصي التالي:



تصفية رسائل البريد الإلكتروني وتمييزها
<?php
/*
 *    a@domain.com
 *      ,     : : mailer-daemon@googlemail.com
 *      .        ,   b@domain.com
 *   
 */
require __DIR__ .'/../general/config/config.php'; //   
require __DIR__ .'/../vendor/autoload.php'; //   

$strEmail = 'a@domain.com';
$strLabelID = 'Label_2399611988534712153'; //  reportProcessed -    

//  
$arrParams = array(
    'from' => 'mailer-daemon@googlemail.com', //       
    'in' => 'inbox', //  
    'after' => date('Y/m/d', (time() - 60 * 60 * 24)), //   
    'has' => 'nouserlabels' //  
);

$arrErrors = array(); //  
$objGmailAPI = new GmailAPI(); //     GMail
$arrClientEmails = array(); //    ,      

try{
    //    
    $service = $objGmailAPI->getService($strEmail);
    //         ,    
    $arrIDs = $objGmailAPI->listMessageIDs($service,$strEmail, $arrParams);
    //      'X-Failed-Recipients',    ,      
    if (count($arrIDs)){
        foreach ($arrIDs as $strMessageID){
            //   
            $objMessage = $objGmailAPI->getMessage($service,$strEmail,$strMessageID,'metadata',array('X-Failed-Recipients'));
            //  
            $arrHeaders = $objMessage->getPayload()->getHeaders();
            //  
            foreach ($arrHeaders as $objMessagePartHeader){
                if ($objMessagePartHeader->getName() == 'X-Failed-Recipients'){
                    $strClientEmail = mb_strtolower(trim($objMessagePartHeader->getValue()), 'UTF-8');
                    if (!empty($strClientEmail)) {
                        if (!in_array($strClientEmail, $arrClientEmails)) $arrClientEmails[] = $strClientEmail;
                    }
                    //    reportProcessed,       
                    $objGmailAPI->modifyLabels($service,$strEmail,$strMessageID,array($strLabelID));
                }
            }
        }
    }
    unset($service,$arrIDs,$strMessageID);
}catch (Exception $e) {
    $arrErrors[] = $e->getMessage();
}

//     ,      ,    
if (count($arrClientEmails)) {
    $objClients = new clients();
    //   email  
    $arrAllClientsEmails = $objClients->getAllEmails();

    foreach ($arrClientEmails as $strClientEmail){
        $arrUsages = array();
        foreach ($arrAllClientsEmails as $arrRow){
            if (strpos($arrRow['email'], $strClientEmail) !== false) {
                $arrUsages[] = '  email  "<a href="'.MANAGEURL.'?m=admin&sm=clients&edit='.$arrRow['s_users_id'].'">'.$arrRow['name'].'</a>"';
            }
            if (strpos($arrRow['email2'], $strClientEmail) !== false) {
                $arrUsages[] = '  email  "<a href="'.MANAGEURL.'?m=admin&sm=clients&edit='.$arrRow['s_users_id'].'">'.$arrRow['name'].'</a>"';
            }
            if (strpos($arrRow['site_user_settings_contact_email'], $strClientEmail) !== false) {
                $arrUsages[] = '  email  "<a href="'.MANAGEURL.'?m=admin&sm=clients&edit='.$arrRow['s_users_id'].'">'.$arrRow['name'].'</a>"';
            }
        }
        $intUsagesCnt = count($arrUsages);
        if ($intUsagesCnt > 0){
            $strMessage = '          <span style="color: #000099;">'.$strClientEmail.'</span><br/>
                  ';
            if ($intUsagesCnt == 1){
                $strMessage .= ' '.$arrUsages[0].'<br/>';
            }else{
                $strMessage .= ':<ul>';
                foreach ($arrUsages as $strUsage){
                    $strMessage .= '<li>'.$strUsage.'</li>';
                }
                $strMessage .= '</ul>';
            }
            $strMessage .= '<br/>,        .<br/><br/>
                    ,    ';
            if (empty($objMailSender)) $objMailSender = new mailSender();
            $objMailSender->sendMail('b@domain.com',' email ',$strMessage);
        }
    }
}

if (count($arrErrors)){
    $strTo = 'my_email@domain.com';
    $strSubj = '      ';
    $strMessage = '        :'.
        '<ul><li>'.implode('</li><li>',$arrErrors).'</li></ul>'.
        '<br/>URL: '.filter_input(INPUT_SERVER, 'REQUEST_URI', FILTER_SANITIZE_URL);
    if (empty($objMailSender)) $objMailSender = new mailSender();
    $objMailSender->sendMail($strTo,$strSubj,$strMessage);
}




هذا البرنامج النصي ، مثله مثل الأول ، يتصل بصندوق البريد المحدد ، ويختار الأحرف الضرورية منه (تقارير عن الرسائل التي لم يتم تسليمها) بدون علامة ، ويجد في الرسالة عنوان البريد الإلكتروني الذي تمت محاولة إرسال الرسالة إليه ويميز هذا الحرف بعلامة "تمت معالجته" ... ثم يتم إجراء عمليات التلاعب بعنوان البريد الإلكتروني الذي تم العثور عليه ، ونتيجة لذلك يتم تكوين خطاب يمكن قراءته من قبل الإنسان إلى الموظف المسؤول.



المصادر متاحة على جيثب .



هذا كل ما أردت أن أقوله في هذا المقال. شكرا للقراءة! إذا شعرت بشفري في عينيك ، فقط قم بلف المفسد أو اكتب تعليقاتك - سأكون سعيدًا بالنقد البناء.



All Articles