Développez des fonctionnalités de messagerie avancées avec IMAP et PHP

王林
Libérer: 2023-08-31 08:34:01
original
1358 Les gens l'ont consulté

Ce que vous allez créer

Dans ce didacticiel, je vais vous montrer quelques exemples concrets d'utilisation de PHP et IMAP pour créer de nouvelles fonctionnalités de gestion du courrier électronique que les grands fournisseurs de courrier électronique n'ont pas encore créées pour nous.

Mon intérêt pour cela a commencé en 2010 lorsque j'ai écrit Douze idées Gmail pour révolutionner (encore) le courrier électronique, mais la plupart des idées que je souhaitais étaient encore hors de portée. Malgré son importance, l’e-mail en tant qu’application a été assez lent à innover.

Nous sommes inondés d'e-mails et la gestion de notre boîte de réception peut encore être une lourde charge. Les services de courrier et les clients n’ont pas fait grand-chose pour nous aider à cet égard. La plupart des e-mails que nous recevons sont envoyés par des machines plutôt que par des personnes, mais nous devons tous les traiter individuellement.

L'analyse de mes propres e-mails a montré que j'avais reçu des e-mails de plus de 230 expéditeurs automatisés, alors que le nombre d'expéditeurs réels était bien inférieur. J'en ai marre de créer des filtres et de remplir d'innombrables formulaires de désabonnement dans Gmail. Je souhaite avoir plus de contrôle sur la gestion de mes emails et me simplifier la vie.

Enfin, au cours de la dernière année, j'ai décidé de créer les fonctionnalités dont j'avais besoin. Le résultat est Simplify Email (SE), une petite application Web que vous pouvez héberger vous-même et qui offre une variété de nouvelles fonctionnalités de messagerie intéressantes, que vous pouvez toutes consulter sur le site Web du projet.

Ce qui est intéressant avec SE, c'est qu'il s'agit d'une plate-forme de lecture, d'analyse, d'acheminement et de gestion des e-mails - les possibilités sont nombreuses. Le courrier électronique simplifié est essentiellement un terrain de jeu programmable pour « pirater » votre propre courrier électronique.

Je vais vous expliquer le code de trois exemples de traitement d'e-mails dans SE à l'aide de PHP, IMAP et MySQL :

  1. Vérifiez votre boîte de réception et filtrez vos messages
  2. Mise en œuvre d'une requête de liste blanche pour les expéditeurs inconnus
  3. Signaler un e-mail sans réponse

Ce tutoriel vous donnera certainement une longueur d'avance sur l'écriture de code IMAP en PHP. Mais vous pouvez également utiliser directement la base de code Simplify Email. Vous pouvez acheter le code pour seulement 10 $, et il existe une ancienne version open source (à laquelle manque certaines des fonctionnalités décrites ci-dessous). Des instructions d'installation pour les configurations Linux typiques sont fournies. Je propose également des images préinstallées chez Digital Ocean pour 25 $ et propose une installation avec voiturier portable. SE est écrit en PHP dans le framework Yii.

Veuillez noter qu'à moins de compiler une bibliothèque IMAP sécurisée pour PHP, vous ne pourrez pas accéder à la plupart des serveurs de messagerie depuis votre machine de développement locale. C'est l'une des raisons pour lesquelles j'encourage les gens à exécuter Simplify Email sous forme de droplet dans Digital Ocean. Il existe également quelques conseils pour sécuriser votre compte Google lorsque vous vous connectez via IMAP.

Utilisez IMAP

Comment simplifier le fonctionnement du courrier électronique

Avec SE, vous pouvez continuer à utiliser le client de messagerie de votre choix sur le Web et les appareils mobiles. Vous n'avez pas besoin de modifier vos applications ou vos habitudes personnelles. SE accède à votre compte de messagerie en coulisse via IMAP ; en tant qu'assistant personnel intelligent, SE prétraite votre courrier électronique, déplaçant les messages vers l'emplacement approprié en fonction de tout ce que vous lui dites.

Lorsqu'un message arrive d'un expéditeur familier, SE le déplace vers le dossier que vous spécifiez. Lorsqu'un expéditeur inconnu reçoit un message pour la première fois, celui-ci est déplacé vers un dossier de révision.

Toutes les quelques heures (ou aussi souvent que vous le souhaitez), SE vous enverra un résumé de l'endroit où il a déplacé vos messages et des messages en cours d'examen. Notez que le dossier d'audit contient un lien vers l'expéditeur de la formation, ce qui facilite grandement la formation du SE au fil du temps.

Développez des fonctionnalités de messagerie avancées avec IMAP et PHP

Vous pouvez parcourir votre dossier de révision à tout moment - pas besoin d'attendre l'arrivée des résumés. Mais l’avantage de SE est que vous n’avez plus besoin de parcourir vos dossiers ; vous pouvez simplement lire le résumé pour visualiser les emails reçus et former les nouveaux expéditeurs.

1. Vérifiez votre boîte de réception et filtrez les messages

SE utilise plusieurs tâches cron à exécuter en arrière-plan du serveur. Chacun est appelé depuis DaemonController.php.

Le premier, processInbox, est appelé fréquemment et doit être effectué rapidement : son travail consiste à passer au crible les e-mails et à les sortir de la boîte de réception et dans des dossiers catégorisés, appelés dossiers de filtrage, le plus rapidement possible.

Le second, processFiltering, est plus gourmand en traitement et effectue des opérations plus approfondies sur l'e-mail, déplaçant finalement le message vers sa destination finale.

Méthode ProcessInbox

La tâche Cron est appelée régulièrement processInbox :

public function actionInbox() {
    
    // moves inbox messages to @filtering
    // runs frequently 
    $r = new Remote();
    $r->processInbox();	  
    
}
Copier après la connexion

Développez des fonctionnalités de messagerie avancées avec IMAP et PHP

Pour chaque compte, nous décryptons vos identifiants de messagerie, puis utilisons imap_open pour créer un flux IMAP dans votre dossier de boîte de réception :

public function open($account_id, $mailbox='',$options=NULL) {
  // opens folder in an IMAP account
  $account = Account::model()->findByPk($account_id);
  $this->hostname = $account->address;
  if (!stristr($this->hostname,'{'))
    $this->hostname = '{'.$this->hostname.'}';
  $cred = Account::model()->getCredentials($account->cred);
  if ($account->provider == Account::PROVIDER_ICLOUD) {
    // icloud accepts only name part of mailbox e.g. stevejobs vs. stevejobs@icloud.com
    $temp = explode('@',$cred[0]);
    $cred[0]=$temp[0];
  }
  $this->stream = imap_open($this->hostname.$mailbox,$cred[0],$cred[1],$options,1) or die('Cannot connect to mail server - account_id:'.$account_id .' '.print_r(imap_errors()));
}
Copier après la connexion

Dans processInbox nous utilisons les fonctions de la bibliothèque PHP imap_search et imap_fetch_overview pour récupérer le tableau des messages :

// lookup folder_id of this account's INBOX
$folder_id = Folder::model()->lookup($account_id,$this->path_inbox);
$this->open($account_id,$this->path_inbox);
$cnt=0;
$message_limit= 50; // break after n messages to prevent timeout
echo 'Sort since: '.date("j F Y",$tstamp);           
// imap_search date format 30 November 2013
 $recent_messages = @imap_search($this->stream, 'SINCE "'.date("j F Y",$tstamp).'"',SE_UID); 
 if ($recent_messages===false) continue; // to do - continue into next account
 $result = imap_fetch_overview($this->stream, implode(',',array_slice($recent_messages,0,$message_limit)),FT_UID);
Copier après la connexion

Ensuite, nous traitons l'ensemble des messages dans la boîte de réception :

foreach ($result as $item) {         
   if (!$this->checkExecutionTime($time_start)) break;
   // get msg header and stream uid
   $msg = $this->parseHeader($item); 
Copier après la connexion

Il s'agit d'une version adaptée du code d'analyse d'en-tête IMAP accessible au public qui collecte les informations supplémentaires nécessaires au SE pour accomplir diverses tâches. Fondamentalement, il utilise imap_rfc822_parse_adrlist pour déterminer les informations sur le destinataire, l'ID du message, l'objet et l'horodatage (ou les informations sur l'expéditeur lors de l'analyse du dossier Envoyé) :

  public function parseHeader($header) {
    // parses header object returned from imap_fetch_overview    
    if (!isset($header->from)) {
      return false;
    } else {
      $from_arr = imap_rfc822_parse_adrlist($header->from,'gmail.com');
      $fi = $from_arr[0];
      $msg = array(
        "uid" => (isset($header->uid))
              ? $header->uid : 0,
         "personal" => (isset($fi->personal))
            ? @imap_utf8($fi->personal) : "",
          "email" => (isset($fi->mailbox) && isset($fi->host))
              ? $fi->mailbox . "@" . $fi->host : "",
          "mailbox" => (isset($fi->mailbox))
            ? $fi->mailbox : "",
          "host" => (isset($fi->host))
            ? $fi->host : "",
          "subject" => (isset($header->subject))
              ? @imap_utf8($header->subject) : "",
          "message_id" => (isset($header->message_id))
                ? $header->message_id : "",
          "in_reply_to" => (isset($header->in_reply_to))
                    ? $header->in_reply_to : "",
          "udate" => (isset($header->udate))
              ? $header->udate : 0,
          "date_str" => (isset($header->date))
              ? $header->date : ""
      );    
      // handles fetch with uid and rfc header parsing
      if ($msg['udate']==0 && isset($header->date)) {
          $msg['udate']=strtotime($header->date);
      }
      $msg['rx_email']='';        
      $msg['rx_personal']='';
        $msg['rx_mailbox']='';
        $msg['rx_host']='';
      if (isset($header->to)) {        
        $to_arr = imap_rfc822_parse_adrlist($header->to,'gmail.com');
        $to_info = $to_arr[0];
        if (isset($to_info->mailbox) && isset($to_info->host)) {
          $msg['rx_email']=$to_info->mailbox.'@'.$to_info->host;
        }
        if (isset($to_info->personal))
          $msg['rx_personal']=$to_info->personal;
        if (isset($to_info->mailbox))
          $msg['rx_mailbox']=$to_info->mailbox;
        if (isset($to_info->host))
          $msg['rx_host']=$to_info->host;
      }
      return $msg;
    }
  }
Copier après la connexion

Nous créons des enregistrements dans la base de données pour l'expéditeur et l'enveloppe du courrier :

   // skip any system messages
   if ($msg['email']==$system_email) continue;
    // if udate is too old, skip msg
    if (time()-$msg['udate']>$this->scan_seconds) continue; // skip msg
    // default action
     $action = self::ACTION_MOVE_FILTERED;
     $isNew = $s->isNew($account_id,$msg["email"]);
    // look up sender, if new, create them
    $sender_id = $s->add($user_id,$account_id,$msg["personal"], $msg["mailbox"], $msg["host"],0);                       
    $sender = Sender::model()->findByPk($sender_id);
     // create a message in db if needed
     $message_id = $m->add($user_id,$account_id,0,$sender_id,$msg['message_id'],$msg['subject'],$msg['udate'],$msg['in_reply_to']);        
        $message = Message::model()->findByPk($message_id);
Copier après la connexion

Si l'expéditeur est nouveau (inconnu) pour nous, nous enverrons un e-mail de défi de liste blanche (nous discutons en détail des défis de liste blanche dans la section suivante ci-dessous) :

if ($isNew) {
        $this->challengeSender($user_id,$account_id,$sender,$message);
      }
Copier après la connexion
Copier après la connexion

Ensuite, nous déterminons si l'utilisateur a peut-être fait glisser un message d'un autre dossier vers la boîte de réception - dans l'intention de l'entraîner par glisser-déposer. Si tel est le cas, nous définirons la formation de cet expéditeur dans la boîte de réception. En d'autres termes, la prochaine fois, nous souhaitons acheminer uniquement le courrier de cet expéditeur vers la boîte de réception :

         if ($message['status'] == Message::STATUS_FILTERED ||
   	    $message['status'] == Message::STATUS_REVIEW ||
   	    ($message['status'] == Message::STATUS_TRAINED && $message['folder_id'] <> $folder_id) ||
   	    ($message['status'] == Message::STATUS_ROUTED && $message['folder_id'] <> $folder_id))
   	    {
     	  // then it's a training
   	    $action = self::ACTION_TRAIN_INBOX;     	    
   	  } else if (($message['status'] == Message::STATUS_TRAINED || $message['status'] == Message::STATUS_ROUTED) && $message['folder_id'] == $folder_id) {
   	    // if trained already or routed to inbox already, skip it
   	    $action = self::ACTION_SKIP;  
   	    echo 'Trained previously, skip ';lb();   	  
   	    continue;  
   	  }
Copier après la connexion

Sinon, nous préparerons le message à déplacer vers le dossier Filtre pour un traitement ultérieur. Tout d'abord, nous pouvons envoyer une notification sur le téléphone de l'utilisateur si l'expéditeur de la notification correspond ou si un mot-clé correspond (et ce n'est pas une période calme) :

   if ($action == self::ACTION_MOVE_FILTERED) {
     $cnt+=1;         
     if ($sender->exclude_quiet_hours == Sender::EQH_YES or !$this->isQuietHours($user_id)) {
       // send smartphone notifications based on sender
       if ($sender->alert==Sender::ALERT_YES) {
         $this->notify($sender,$message,Monitor::NOTIFY_SENDER);
       }
       // send notifications based on keywords
       if (AlertKeyword::model()->scan($msg)) {
         $this->notify($sender,$message,Monitor::NOTIFY_KEYWORD);
       }               
     }               
     // move imap msg to +Filtering
     echo 'Moving to +Filtering';lb();
     //$result = @imap_mail_move($this->stream,$msg['uid'],$this->path_filtering,CP_UID);
     $result = $this->messageMoveHandler($msg['uid'],$this->path_filtering,false);             
     if ($result) {
       echo 'moved<br />';
       $m->setStatus($message_id,Message::STATUS_FILTERED);
     }
   } 
Copier après la connexion

Si un message est glissé dans la boîte de réception, nous mettrons à jour nos paramètres d'entraînement :

else if ($action == self::ACTION_TRAIN_INBOX) {
     // set sender folder_id to inbox
     echo 'Train to Inbox';lb();
     $m->setStatus($message_id,Message::STATUS_TRAINED);
     // only train sender when message is newer than last setting
     if ($msg['udate']>=$sender['last_trained']) {
       $s->setFolder($sender_id,$folder_id);
     } 
   }
Copier après la connexion

Méthode ProcessFiltering

La méthode de traitement secondaire est appelée processFiltering,也在DaemonController.php中. Il s'agit du travail le plus fastidieux consistant à déplacer les messages vers les dossiers appropriés :

public function actionIndex()
{
  // processes messages in @Filtering to appropriate folders
  $r = new Remote();
  $r->processFiltering();
  // Record timestamp of cronjob for monitoring
  $file = file_put_contents('./protected/runtime/cronstamp.txt',time(),FILE_USE_INCLUDE_PATH);	  
}
Copier après la connexion

Cette méthode ouvre votre compte de messagerie pour rechercher des messages récents et collecter des données à leur sujet. Il utilise également imap_searchimap_fetch_overviewparseHeader:

$tstamp = time()-(7*24*60*60); // 7 days ago
$recent_messages = @imap_search($this->stream, 'SINCE "'.date("j F Y",$tstamp).'"',SE_UID);
if ($recent_messages===false) continue; // to do - continue into next account
$result = imap_fetch_overview($this->stream, implode(',',array_slice($recent_messages,0,$message_limit)),FT_UID);
foreach ($result as $item) {         
      $cnt+=1;
      if (!$this->checkExecutionTime($time_start)) break;
      // get msg header and stream uid
      $msg = $this->parseHeader($item);            
Copier après la connexion

La boucle de traitement principale pour chaque message du dossier filtre est très détaillée. Nous regardons d'abord l'adresse du destinataire, puisque SE permet de former des dossiers par adresse de destinataire, par exemple un message envoyé au domaine happyvegetarian.com ira dans le dossier veggie :

 // Set the default action to move to the review folder
 $action = self::ACTION_MOVE_REVIEW;
 $destination_folder =0;
 // look up & create recipient
 $recipient_id = $r->add($user_id,$account_id,$msg['rx_email'],0);
 $routeByRx = $this->routeByRecipient($recipient_id);
 if ($routeByRx!==false) {
  $action = $routeByRx->action;
  $destination_folder = $routeByRx->destination_folder;
 }            
Copier après la connexion

Nous recherchons ensuite l'expéditeur et créons un nouvel enregistrement dans la base de données (si nécessaire). Si une formation existe pour l'expéditeur, nous pouvons définir le dossier cible :

 // look up sender, if new, create them
 $sender_id = $s->add($user_id,$account_id,$msg["personal"], $msg["mailbox"], $msg["host"],0);                       
 $sender = Sender::model()->findByPk($sender_id);
 // if sender destination known, route to folder
 if ($destination_folder ==0 && $sender['folder_id'] > 0) {
   $action = self::ACTION_ROUTE_FOLDER;  
   $destination_folder = $sender['folder_id'];      
 }
 
Copier après la connexion

Si le (nouvel) expéditeur non formé s'est authentifié via un défi de liste blanche (dont nous discutons dans la section suivante ci-dessous), alors nous acheminerons ce message vers la boîte de réception :

// whitelist verified senders go to inbox
 if ($sender->is_verified==1 && $sender['folder_id'] ==0 && UserSetting::model()->useWhitelisting($user_id)) {
   // place message in inbox
   $action = self::ACTION_ROUTE_FOLDER;  
   $destination_folder = Folder::model()->lookup($account_id,$this->path_inbox);             
 }
Copier après la connexion

Nous créons ensuite une entrée de message dans la base de données qui contient les informations d'enveloppe concernant ce message :

  // create a message in db
      $message = Message::model()->findByAttributes(array('message_id'=>$msg['message_id']));
  if (!empty($message)) {
    // message exists already, 
	  $message_id = $message->id;    	  
  } else {
    $message_id = $m->add($user_id,$account_id,0,$sender_id,$msg['message_id'],$msg['subject'],$msg['udate'],$msg['in_reply_to']);         
  }
Copier après la connexion

Si le message provient d'un expéditeur inconnu et non vérifié, nous pouvons déplacer le message vers un dossier de révision. Le dossier de révision contient tous les messages provenant d'expéditeurs que nous ne reconnaissons pas.

Si le message provient d'un expéditeur connu et que nous avons identifié la destination, nous pouvons le déplacer tant que ce n'est pas une heure calme (et que la fonction Ne pas déranger est désactivée) :

  if ($recipient_id!==false) $m->setRecipient($message_id,$recipient_id);
  if ($action == self::ACTION_MOVE_REVIEW) {
    echo 'Moving to +Filtering/Review';lb();
    //$result = @imap_mail_move($this->stream,$msg['uid'],$this->path_review,CP_UID);
    $result = $this->messageMoveHandler($msg['uid'],$this->path_review,false);               
    if ($result) {
      echo 'moved<br />';
      $m->setStatus($message_id,Message::STATUS_REVIEW);
    }      
 } else if ($action == self::ACTION_ROUTE_FOLDER || $action == self::ACTION_ROUTE_FOLDER_BY_RX) {
  // lookup folder name by folder_id
  $folder = Folder::model()->findByPk($destination_folder);       
  // if inbox & quiet hours, don't route right now
  if (strtolower($folder['name'])=='inbox' and $sender->exclude_quiet_hours == Sender::EQH_NO and $this->isQuietHours($user_id)) continue;
  echo 'Moving to '.$folder['name'];lb();
  $mark_read = Folder::model()->isMarkRead($folder['mark_read']) || Sender::model()->isMarkRead($sender['mark_read']);
  //$result = @imap_mail_move($this->stream,$msg['uid'],$folder['name'],CP_UID);
  $result = $this->messageMoveHandler($msg['uid'],$folder['name'],$mark_read);
  if ($result) {
    echo 'moved<br />';
    $m->setStatus($message_id,Message::STATUS_ROUTED);         
    $m->setFolder($message_id,$destination_folder);
  }
}
Copier après la connexion

Pendant les périodes calmes, les messages sont principalement enregistrés dans des dossiers filtrés.

Toutes les quelques heures, un processus différent crée un résumé des messages à l'aide des enregistrements de la table des messages pour déterminer les e-mails récemment reçus et filtrés ainsi que la manière dont ils ont été acheminés.

2. Mettre en œuvre un défi de liste blanche pour les expéditeurs inconnus

L'objectif du défi de la liste blanche est de conserver tous les messages provenant d'expéditeurs inconnus, tels que des robots marketing ou des spammeurs, qui pourraient se trouver dans votre boîte de réception. SE place les messages provenant d'expéditeurs inconnus dans un dossier de révision. Cependant, si vous activez la liste blanche, nous enverrons un e-mail de défi pour donner à l'expéditeur la possibilité de vérifier qu'il est humain. S'ils répondent, nous déplacerons le message vers votre boîte de réception. Si l'e-mail s'avère indésirable, vous pouvez supprimer le message du résumé ou le faire glisser vers n'importe quel dossier dans lequel vous souhaitez l'entraîner.

Les utilisateurs peuvent activer et désactiver la liste blanche dans les paramètres :

Développez des fonctionnalités de messagerie avancées avec IMAP et PHP

Pour mettre en œuvre la liste blanche, nous enverrons un défi par e-mail chaque fois qu'un nouvel expéditeur recevra un message :

if ($isNew) {
        $this->challengeSender($user_id,$account_id,$sender,$message);
      }
Copier après la connexion
Copier après la connexion

ChallengeSender Envoyez aux utilisateurs un lien codé sur lequel ils pourront cliquer. Nous avons également mis en place certaines mesures de protection pour garantir que nous ne restons pas coincés dans une boucle de courrier électronique avec des messages d'absence du bureau :

 public function challengeSender($user_id,$account_id,$sender,$message) {
   // whitelist email challenge
   $yg = new Yiigun();
   $ac = Account::model()->findByPk($account_id);
   if (!empty($ac['challenge_name']))
     $from = $ac['challenge_name'].' <no-reply@'.$yg->mg_domain.'>';
    else
      $from = 'Filter <no-reply@'.$yg->mg_domain.'>';      
   $cred = Account::model()->getCredentials($ac->cred);
   $account_email = $cred[0];
   unset($cred); 
   // safety: checks no recent email
   if ($sender->last_emailed>(time()-(48*60*60))) return false;   
   if ($sender->isBot($sender['email'])) {
     // to do - can also set this person to bulk by default
     return false;
   }
$link=Yii::app()->getBaseUrl(true)."/sender/verify/s/".$sender->id."/m/".$message->id.'/u/'.$message->udate;
    $subject = 'Please verify the message you sent to '.$account_email;
    $body="<p>Hi,<br /><br /> I'm trying to reduce unsolicited email. Could you please verify your email address by clicking the link below:<br /><a href=\"".$link.'">'.$link.'</a><br /><br />Verifying your email address will help speed your message into my inbox. Thanks for your assistance!</p>';
    $yg->send_html_message($from, $sender['email'], $subject,$body);
    // update last_emailed
    $sender->touchLastEmailed($sender->id);
 }
Copier après la connexion

Ensuite, si le destinataire clique sur le lien encodé, nous le vérifions dans la base de données. Le contrôleur émetteur traite ces demandes et vérifie leur validité :

  public function actionVerify($s = 0, $m=0,$u=0) {
    // verify that secure msg url from digest is valid, log in user, show msg
    $sender_id = $s;
    $message_id = $m;
    $udate = $u;
    $msg = Message::model()->findByPk($message_id);
    if (!empty($msg) && $msg->sender_id == $sender_id && $msg->udate == $udate) {
      $result = 'Thank you for your assistance. I\'ll respond to your email as soon as possible.';
      $a = new Advanced();
      $a->verifySender($msg->account_id,$sender_id);
    } else {
      $result = 'Sorry, we could not verify your email address.';
    }
    	$this->render('verify',array(
			'result'=>$result,
		));
  }
Copier après la connexion

Cela indique à notre boucle de traitement de déplacer ce message et les futurs messages de cet expéditeur vers la boîte de réception.

3。报告未回复的电子邮件

有时,查看您已发送但未收到回复的消息摘要会有所帮助。为了识别这些邮件,Simplify Email 会监视已发送但尚未收到回复的邮件。

我们收到的每条消息都包含一个唯一的 ID,称为 message_id(IMAP 规范的一部分)。它通常看起来像这样:

Message-Id: <CALe0OAaF3fb3d=gCq2Fs=Ex61Qp6FdbiA4Mvs6kTQ@mail.gmail.com>
Copier après la connexion

此外,当发送消息以回复其他消息时,它们有一个 in_reply_to 字段,该字段链接回原始 message_id

因此,我们使用 SQL 查询来查找所有收到的消息,这些消息没有引用其 message_id 的相应回复消息。为此,我们在没有 in_reply_to id 的情况下使用 LEFT OUTER JOIN:

public function getUnanswered($account_id,$mode=0, $range_days = 7) {
    if ($mode==0)
      $subject_compare = 'not';
    else
      $subject_compare = '';
    $query = Yii::app()->db->createCommand("SELECT fi_sent_message.id, fi_sent_message.recipient_id as sender_id,fi_sent_message.subject,fi_sent_message.udate,fi_message.in_reply_to,fi_sent_message.message_id  FROM fi_sent_message LEFT OUTER JOIN fi_message ON fi_message.in_reply_to = fi_sent_message.message_id WHERE fi_sent_message.account_id = ".$account_id." AND fi_message.in_reply_to is null and fi_sent_message.udate > ".(time()-(3600*24*$range_days))." and fi_sent_message.subject ".$subject_compare." like 'Re: %' ORDER BY fi_sent_message.udate  DESC")->queryAll();
    return $query;
  }
Copier après la connexion

我们使用 $subject_compare 模式来区分我们发送的尚未回复的消息和我们发送给尚未回复的线程的回复。以下是您帐户中的未回复消息报告:

Développez des fonctionnalités de messagerie avancées avec IMAP et PHP

SE 还将此信息作为可选摘要提供,称为未回复电子邮件摘要。您可以每天、每隔几天或每周收到它。

我们还使用类似的 SQL 表格和 Google Charts 来提供有关某些人向您发送电子邮件的频率的报告:

Développez des fonctionnalités de messagerie avancées avec IMAP et PHP

  public function reportInbound($account_id,$range=30,$limit = 100) {
    $result= Yii::app()->db->createCommand('SELECT fi_sender.personal, fi_sender.email,count(sender_id) as cnt
      FROM fi_message LEFT JOIN fi_sender ON fi_sender.id =fi_message.sender_id WHERE fi_sender.account_id = :account_id AND fi_message.created_at > DATE_SUB( NOW() , INTERVAL :range DAY )  
      GROUP BY sender_id ORDER BY cnt desc LIMIT :limit ')->bindValue('range',$range)->bindValue('account_id',$account_id)->bindValue('limit',$limit)->queryAll();
    return $result;
  }
Copier après la connexion

我很快就会撰写更多有关 Tuts+ 的 Google Charts 的文章。

后续步骤

我希望您已经发现 Simplify Email 足够有趣,可以尝试 PHP IMAP 编程。您可以构建许多很酷的功能,而不需要大型电子邮件提供商做任何新的事情。

如果您有任何疑问或更正,请在评论中提出。如果您想继续关注我未来的 Tuts+ 教程和其他系列,请关注 @reifman 或访问我的作者页面。您也可以在这里联系我。

相关链接

以下是一些您可能会觉得有用的附加链接:

  • 简化电子邮件
  • 简化电子邮件简介(视频)
  • (再次)彻底改变电子邮件的 12 个 Gmail 创意

  • BoingBoing 中简化电子邮件的报道(此处和此处)
  • PHP IMAP 参考
  • Yii 框架简介 (Tuts+)

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
source:php.cn
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal