| 
...
 | 
...
 | 
@@ -2,11 +2,15 @@ | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
 namespace Service;
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
 use Event\Event;
 | 
| 
 | 
 | 
 use Lib\Imap\ImapConfig;
 | 
| 
 | 
 | 
 use Lib\Imap\ImapPool;
 | 
| 
 | 
 | 
 use Lib\Imap\Parse\Folder\Folder;
 | 
| 
 | 
 | 
 use Lib\Imap\Parse\MessageItem;
 | 
| 
 | 
 | 
 use Model\bodySql;
 | 
| 
 | 
 | 
 use Model\emailSql;
 | 
| 
 | 
 | 
 use Model\folderSql;
 | 
| 
 | 
 | 
 use Model\listsSql;
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
 /**
 | 
| 
 | 
 | 
  * 同步邮件
 | 
| 
...
 | 
...
 | 
@@ -32,6 +36,9 @@ class SyncMail { | 
| 
 | 
 | 
      */
 | 
| 
 | 
 | 
     protected $email;
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
     protected $isStop = false;
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
     /**
 | 
| 
 | 
 | 
      * SyncMail constructor.
 | 
| 
 | 
 | 
      * @param int|string|array $email
 | 
| 
...
 | 
...
 | 
@@ -54,12 +61,18 @@ class SyncMail { | 
| 
 | 
 | 
                 ->setHost($email['imap'])
 | 
| 
 | 
 | 
                 ->setEmail($email['email'])
 | 
| 
 | 
 | 
                 ->setPassword(base64_decode($email['password']))
 | 
| 
 | 
 | 
                 ->debug()
 | 
| 
 | 
 | 
         );
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
         $this->login();
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
     }
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
     public function stop(){
 | 
| 
 | 
 | 
         $this->isStop = true;
 | 
| 
 | 
 | 
     }
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
     protected function emailId(){
 | 
| 
 | 
 | 
         return $this->email['id'];
 | 
| 
 | 
 | 
     }
 | 
| 
...
 | 
...
 | 
@@ -78,16 +91,16 @@ class SyncMail { | 
| 
 | 
 | 
     }
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
     /**
 | 
| 
 | 
 | 
      * 同步文件夹
 | 
| 
 | 
 | 
      * @param $folder
 | 
| 
 | 
 | 
      * @param int $pid
 | 
| 
 | 
 | 
      * @return array
 | 
| 
 | 
 | 
      * @throws \Exception
 | 
| 
 | 
 | 
      * @author:dc
 | 
| 
 | 
 | 
      * @time 2024/9/26 10:22
 | 
| 
 | 
 | 
      * @time 2024/9/26 10:46
 | 
| 
 | 
 | 
      */
 | 
| 
 | 
 | 
     protected function folders(){
 | 
| 
 | 
 | 
     protected function folder($folder,$pid = 0){
 | 
| 
 | 
 | 
         $uuids = [];
 | 
| 
 | 
 | 
         // 获取文件夹
 | 
| 
 | 
 | 
         $folders = $this->imap->getFolders();
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
         $tmp = function ($folder) use ($folders,&$uuids){
 | 
| 
 | 
 | 
         foreach ($folder as $item){
 | 
| 
 | 
 | 
             /** @var Folder $item*/
 | 
| 
 | 
 | 
 
 | 
| 
...
 | 
...
 | 
@@ -95,21 +108,13 @@ class SyncMail { | 
| 
 | 
 | 
             $uuids[$uuid] = $uuid;
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
             $folder_name = '';
 | 
| 
 | 
 | 
                 // 已发送
 | 
| 
 | 
 | 
                 if(in_array('Send',$item->flags)){
 | 
| 
 | 
 | 
                     $folder_name = folderAlias('Send');
 | 
| 
 | 
 | 
                 }
 | 
| 
 | 
 | 
                 // 草稿
 | 
| 
 | 
 | 
                 elseif(in_array('Drafts',$item->flags)){
 | 
| 
 | 
 | 
                     $folder_name = folderAlias('Drafts');
 | 
| 
 | 
 | 
             if($item->flags){
 | 
| 
 | 
 | 
                 // 有些邮箱是把公共的文件夹标记在flag里面的,识别出来
 | 
| 
 | 
 | 
                 foreach ($item->flags as $flag){
 | 
| 
 | 
 | 
                     if(in_array($flag,['Send','Drafts','Junk','Trash'])){
 | 
| 
 | 
 | 
                         $folder_name = folderAlias($flag);
 | 
| 
 | 
 | 
                     }
 | 
| 
 | 
 | 
                 // 垃圾
 | 
| 
 | 
 | 
                 elseif(in_array('Junk',$item->flags)){
 | 
| 
 | 
 | 
                     $folder_name = folderAlias('Junk');
 | 
| 
 | 
 | 
                 }
 | 
| 
 | 
 | 
                 // 回收站
 | 
| 
 | 
 | 
                 elseif(in_array('Trash',$item->flags)){
 | 
| 
 | 
 | 
                     $folder_name = folderAlias('Trash');
 | 
| 
 | 
 | 
             }
 | 
| 
 | 
 | 
             if(!$folder_name){
 | 
| 
 | 
 | 
                 $fn = explode('/',$item->getParseFolder());
 | 
| 
...
 | 
...
 | 
@@ -120,29 +125,191 @@ class SyncMail { | 
| 
 | 
 | 
             $id = $this->db->value(folderSql::has(['email_id'=>$this->emailId(),'uuid'=>$uuid]));
 | 
| 
 | 
 | 
             $data = [
 | 
| 
 | 
 | 
                 'email_id' => $this->emailId(),
 | 
| 
 | 
 | 
                     'folder' => folderAlias($folder_name),
 | 
| 
 | 
 | 
                     'origin_folder' => $folder['folder'],
 | 
| 
 | 
 | 
                 'folder' => $folder_name,
 | 
| 
 | 
 | 
                 'origin_folder' => $item->folder,
 | 
| 
 | 
 | 
                 'uuid'  =>  $uuid,
 | 
| 
 | 
 | 
                 'pid'   =>  $pid
 | 
| 
 | 
 | 
             ];
 | 
| 
 | 
 | 
             if ($id){
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
                 $this->db->update(folderSql::$table,$data,dbWhere(['id'=>$id]),false);
 | 
| 
 | 
 | 
             }else{
 | 
| 
 | 
 | 
                 $id = $this->db->insert(folderSql::$table,$data,false);
 | 
| 
 | 
 | 
                 if(!$id) abort('文件夹写入异常 '.json_encode($data,JSON_UNESCAPED_UNICODE));
 | 
| 
 | 
 | 
             }
 | 
| 
 | 
 | 
             // 是否有子级目录
 | 
| 
 | 
 | 
             if($id && $item->getChild()){
 | 
| 
 | 
 | 
                 $uuids = array_merge($uuids,$this->folder($item->getChild(),$id));
 | 
| 
 | 
 | 
             }
 | 
| 
 | 
 | 
         }
 | 
| 
 | 
 | 
         };
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
         $tmp($folders->getTopFolder());
 | 
| 
 | 
 | 
         return $uuids;
 | 
| 
 | 
 | 
     }
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
     /**
 | 
| 
 | 
 | 
      * 同步
 | 
| 
 | 
 | 
      * @throws \Exception
 | 
| 
 | 
 | 
      * @author:dc
 | 
| 
 | 
 | 
      * @time 2024/9/26 10:46
 | 
| 
 | 
 | 
      */
 | 
| 
 | 
 | 
     public function sync(){
 | 
| 
 | 
 | 
         $this->isStop = false;
 | 
| 
 | 
 | 
         /*********************************** 同步文件夹 ***************************************/
 | 
| 
 | 
 | 
         // 获取文件夹
 | 
| 
 | 
 | 
         $folders = $this->imap->getFolders();
 | 
| 
 | 
 | 
         $uuids = $this->folder($folders->getTopFolder());
 | 
| 
 | 
 | 
         if($uuids){
 | 
| 
 | 
 | 
             // 删除以前的
 | 
| 
 | 
 | 
             $this->db->delete(folderSql::$table,['uuid.notin'=>$uuids,'email_id'=>$this->emailId()]);
 | 
| 
 | 
 | 
         }
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
         return $folders;
 | 
| 
 | 
 | 
         if($this->isStop) return;
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
         /********************* 同步邮件 **********************/
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
         // 循环文件夹
 | 
| 
 | 
 | 
         foreach ($folders->all() as $f){
 | 
| 
 | 
 | 
             if($this->isStop) return;
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
             if($f->isSelect){ // 是否可以选择 只有可以选中的文件夹才有邮件
 | 
| 
 | 
 | 
                 $folder = $this->imap->folder($f); // 选择文件夹后,有状态
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
                 // 这个暂时不要
 | 
| 
 | 
 | 
 //                $this->db->update(folderSql::$table,[
 | 
| 
 | 
 | 
 //                   'exsts' =>   $folder->getTotal(),
 | 
| 
 | 
 | 
 //                   'unseen' =>   $folder->getUnseen(),
 | 
| 
 | 
 | 
 //                   'last_sync_time' => time()
 | 
| 
 | 
 | 
 //                ],dbWhere(['email_id'=>$this->emailId(),'origin_folder'=>$folder->getName()]),false);
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
                 // 是否有邮件 有邮件才继续
 | 
| 
 | 
 | 
                 if ($folder->getTotal()){
 | 
| 
 | 
 | 
                     $this->mail($folder);
 | 
| 
 | 
 | 
                 }
 | 
| 
 | 
 | 
             }
 | 
| 
 | 
 | 
         }
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
     }
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
     public function sync(){
 | 
| 
 | 
 | 
         $folders = $this->folders();
 | 
| 
 | 
 | 
     /**同步邮件
 | 
| 
 | 
 | 
      *
 | 
| 
 | 
 | 
      * @param string|\Lib\Imap\Request\Folder $folder
 | 
| 
 | 
 | 
      * @param array $uids 固定的uid
 | 
| 
 | 
 | 
      * @param false $isBody 是否同时同步body
 | 
| 
 | 
 | 
      * @author:dc
 | 
| 
 | 
 | 
      * @time 2024/9/26 11:10
 | 
| 
 | 
 | 
      */
 | 
| 
 | 
 | 
     public function mail(string|\Lib\Imap\Request\Folder $folder, array $uids = [],$isBody = false){
 | 
| 
 | 
 | 
         if(is_string($folder)){
 | 
| 
 | 
 | 
             $folder = $this->imap->folder($folder);
 | 
| 
 | 
 | 
         }
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
         $folder_id = $this->db->value(folderSql::first([
 | 
| 
 | 
 | 
             'email_id'=>$this->emailId(),
 | 
| 
 | 
 | 
             'uuid'  =>  md5($this->emailId().$folder->getName())
 | 
| 
 | 
 | 
         ],'`id`'));
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
         // 选择成功
 | 
| 
 | 
 | 
         if($folder->isOk()){
 | 
| 
 | 
 | 
             $msg = $folder->msg();
 | 
| 
 | 
 | 
             if($uids){
 | 
| 
 | 
 | 
                 $this->saveMail($folder_id,$msg->uid($uids)->get()->all(),$isBody);
 | 
| 
 | 
 | 
             }else{
 | 
| 
 | 
 | 
                 $p=1;
 | 
| 
 | 
 | 
                 while (1){
 | 
| 
 | 
 | 
                     if($this->isStop) return;
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
                     $lists = $msg->forPage($p)->get()->all();
 | 
| 
 | 
 | 
                     // 没有数据就跳出
 | 
| 
 | 
 | 
                     if(!$lists){
 | 
| 
 | 
 | 
                         break;
 | 
| 
 | 
 | 
                     }else{
 | 
| 
 | 
 | 
                         $p++;
 | 
| 
 | 
 | 
                         $this->saveMail($folder_id,$lists,$isBody);
 | 
| 
 | 
 | 
                     }
 | 
| 
 | 
 | 
                 }
 | 
| 
 | 
 | 
             }
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
         }
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
     }
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
     /**
 | 
| 
 | 
 | 
      * 保存邮件列表
 | 
| 
 | 
 | 
      * @param int $folder_id
 | 
| 
 | 
 | 
      * @param MessageItem[] $lists
 | 
| 
 | 
 | 
      * @param bool $isBody
 | 
| 
 | 
 | 
      * @author:dc
 | 
| 
 | 
 | 
      * @time 2024/9/29 15:14
 | 
| 
 | 
 | 
      */
 | 
| 
 | 
 | 
     protected function saveMail(int $folder_id, array $lists, bool $isBody=false){
 | 
| 
 | 
 | 
         foreach ($lists as $item){
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
             $data   =   [
 | 
| 
 | 
 | 
                 'uid'   =>  $item->uid,
 | 
| 
 | 
 | 
                 'subject'   =>  mb_substr($item->header->getSubject(),0,1000),// 控制下,有的蛋疼,整tm多长
 | 
| 
 | 
 | 
                 'cc'    =>  $item->header->getCc(true),
 | 
| 
 | 
 | 
                 'bcc'    =>  $item->header->getBcc(true),
 | 
| 
 | 
 | 
                 'from'   =>  $item->header->getFrom()->email,
 | 
| 
 | 
 | 
                 'from_name'   =>  $item->header->getFrom()->name,
 | 
| 
 | 
 | 
                 'to'   =>  implode(',',array_column($item->header->getTo(true),'email')),
 | 
| 
 | 
 | 
                 'to_name'   =>  $item->header->getTo(true),
 | 
| 
 | 
 | 
                 // 这个是 邮件的时间 就是header里面带的 一般情况就是发件时间
 | 
| 
 | 
 | 
 //                'date'   =>  strtotime($item->header->getDate()),
 | 
| 
 | 
 | 
                 'udate'   =>  strtotime($item->date), // 有这个时间就够了,内部时间,就是收到邮件的时间
 | 
| 
 | 
 | 
                 'size'   =>  $item->size,
 | 
| 
 | 
 | 
                 'recent'   =>  $item->isRecent() ? 1 : 0,
 | 
| 
 | 
 | 
                 'seen'   =>  $item->isSeen() ? 1 : 0,
 | 
| 
 | 
 | 
                 'draft'   =>  $item->isDraft() ? 1 : 0,
 | 
| 
 | 
 | 
                 'flagged'   =>  $item->isFlagged() ? 1 : 0,
 | 
| 
 | 
 | 
                 'answered'   =>  $item->isAnswered() ? 1 : 0,
 | 
| 
 | 
 | 
                 'folder_id'   =>  $folder_id,
 | 
| 
 | 
 | 
                 'email_id'    =>  $this->emailId(),
 | 
| 
 | 
 | 
                 'is_file'  =>  $item->isAttachment() ? 1: 0 //是否附件
 | 
| 
 | 
 | 
             ];
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
             // 查询是否存在
 | 
| 
 | 
 | 
             $id = $this->db->value(listsSql::first(dbWhere([
 | 
| 
 | 
 | 
                 'email_id'=> $data['email_id'],
 | 
| 
 | 
 | 
                 'folder_id' =>  $data['folder_id'],
 | 
| 
 | 
 | 
                 'uid'   =>  $data['uid']
 | 
| 
 | 
 | 
             ]),'`id`'));
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
             if(!$id){
 | 
| 
 | 
 | 
                 $id = $this->db->insert(listsSql::$table,$data);
 | 
| 
 | 
 | 
                 // 新邮件标记
 | 
| 
 | 
 | 
                 if($item->getFolderName() == 'INBOX')
 | 
| 
 | 
 | 
                     redis()->incr('have_new_mail_'.$this->emailId(),120);
 | 
| 
 | 
 | 
                 // 执行事件
 | 
| 
 | 
 | 
                 $data['Aicc-Hot-Mail'] = $item->header->get('Aicc-Hot-Mail');
 | 
| 
 | 
 | 
                 Event::call('mail_sync_list',$id, $data);
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
             }else{
 | 
| 
 | 
 | 
                 $this->db->update(listsSql::$table,$data,dbWhere(['id'=> $id]));
 | 
| 
 | 
 | 
             }
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
             // 是否同步body内容
 | 
| 
 | 
 | 
             if($isBody){
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
                 $body = [
 | 
| 
 | 
 | 
                     'lists_id'  =>  $id,
 | 
| 
 | 
 | 
                     'text_html' =>  $item->getBody()->getItems()
 | 
| 
 | 
 | 
                 ];
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
                 if($this->db->count(bodySql::has($id))){
 | 
| 
 | 
 | 
                     $this->db->update(bodySql::$table,$body,'`lists_id` = '.$id,false);
 | 
| 
 | 
 | 
                 }else{
 | 
| 
 | 
 | 
                     $this->db->insert(bodySql::$table,$body,false);
 | 
| 
 | 
 | 
                 }
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
             }
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
         }
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
     }
 | 
| 
 | 
 | 
 
 | 
| 
 | 
 | 
 
 | 
...
 | 
...
 | 
 |