审查视图

lib/Mail/Mail.php 8.1 KB
1  
邓超 authored
1 2 3 4
<?php

namespace Lib\Mail;
1  
邓超 authored
5
use Lib\DbPool;
1  
邓超 authored
6 7
use Model\folderSql;
use Model\listsSql;
1  
邓超 authored
8
1  
邓超 authored
9 10 11 12 13 14 15 16 17 18 19
/**
 * 操作邮件
 * @author:dc
 * @time 2023/2/5 10:10
 * Class MailFun
 * @package Helper\Mail
 */
class Mail {

    /**
     * imap服务器连接实例
1  
邓超 authored
20
     * @var Imap
1  
邓超 authored
21
     */
1  
邓超 authored
22
    public Imap $client;
1  
邓超 authored
23 24 25 26 27 28 29 30 31 32


    /**
     * 登录imap服务器
     * @param string $email
     * @param string $password
     * @param string $imap
     * @author:dc
     * @time 2023/2/5 10:46
     */
1  
邓超 authored
33 34 35 36
    public function login(string $email,string $password,string $imap) {

        $this->client = new Imap();
1  
邓超 authored
37
        // 是否初始成功
1  
邓超 authored
38 39
        $this->client->login("ssl://{$imap}:993",$email,$password);
1  
邓超 authored
40 41 42 43 44 45
        return true;
    }


    /**
     * 同步文件夹
1  
邓超 authored
46
     * @param int $email_id
1  
邓超 authored
47
     * @param DbPool|null $db
1  
邓超 authored
48 49 50 51
     * @return mixed
     * @author:dc
     * @time 2023/2/5 10:58
     */
1  
邓超 authored
52
    public function syncFolder($email_id,$db=null){
1  
邓超 authored
53
        $db = $db ? $db : db();
1  
邓超 authored
54
        // 读取所有文件夹,未解密
1  
邓超 authored
55
        $folders    =   $this->client->getFolder();
1  
邓超 authored
56
1  
邓超 authored
57
//        $db->transaction();
1  
邓超 authored
58 59
        foreach ($folders as $folder){
            $pid = 0;
1  
邓超 authored
60 61 62 63 64 65 66 67 68 69 70 71 72 73
            $uuid = md5($email_id.$folder['folder']);

            // 处理子父文件夹
            if(str_contains($folder['folder'], '/')){
                // 子目录
                $folder['name'] = explode('/',$folder['parseFolder']);
                // 查找pid
                $pid = $db->value(folderSql::has(['uuid'=>md5($email_id.explode('/',$folder['folder'])[0])]));
                // 去掉父目录名称
                $folder['parseFolder'] = explode('/',$folder['parseFolder'])[1];
            }

            try {
                $db->insert(folderSql::$table,[
1  
邓超 authored
74
                    'email_id' => $email_id,
1  
邓超 authored
75 76 77 78 79 80 81
                    'folder' => $folder['parseFolder'],
                    'origin_folder' => $folder['folder'],
                    'uuid'  =>  $uuid,
                    'pid'   =>  $pid
                ],false);
            }catch (\Throwable $e){
                // 这里就不处理失败了
1  
邓超 authored
82
            }
1  
邓超 authored
83
1  
邓超 authored
84
        }
1  
邓超 authored
85
//        $db->commit();
1  
邓超 authored
86 87 88 89 90 91 92 93 94

    }


    /**
     * 同步邮件
     * @param $email_id
     * @param $folder_id
     * @param string $folder
1  
邓超 authored
95
     * @param null|DbPool $db
1  
邓超 authored
96
     * @return bool
1  
邓超 authored
97
     * @throws \Exception
1  
邓超 authored
98
     * @author:dc
1  
邓超 authored
99
     * @time 2023/2/18 9:54
1  
邓超 authored
100
     */
1  
邓超 authored
101
    public function syncMail($email_id,$folder_id,$folder='INBOX',$db = null):bool {
1  
邓超 authored
102
//        _echo('正在同步文件夹:'.$folder);
1  
邓超 authored
103
        $db = $db ? $db : db();
1  
邓超 authored
104
        // 选择文件夹
1  
邓超 authored
105 106
        $status =   $this->client->selectFolder($folder);
1  
邓超 authored
107 108 109 110 111 112
        // 是否有邮件
        if (!isset($status['EXISTS']) || !$status['EXISTS']){
            return true;
        }

        // 更新数量
1  
邓超 authored
113 114 115
        $db->update(
            folderSql::$table,
            ['exsts'=>$status['EXISTS'],'unseen'=>$status['UNSEEN']??0],
1  
邓超 authored
116 117
            dbWhere(['id'=>$folder_id]),
            false
1  
邓超 authored
118 119
        );
1  
邓超 authored
120
1  
邓超 authored
121 122
        // 最后拉取的msgno
        $lastMsgno   =   $db->value(listsSql::lastMsgno($email_id,$folder_id));
1  
邓超 authored
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141

        $nu = 20;

        if(!$lastMsgno){
            $msgno = range(1,$nu);
        }else{
            $msgno = range($lastMsgno,$lastMsgno+$nu);

            if($lastMsgno > $status['EXISTS']){
                $msgno = range($status['EXISTS'] > $nu ? $status['EXISTS'] - $nu : 1,$status['EXISTS']);
            }
            // 一样就不拉新的
            if($lastMsgno == $status['EXISTS']){
                return true;
            }
        }


        // 循环
1  
邓超 authored
142
        $results = $this->client->fetchHeader($msgno);
1  
邓超 authored
143 144 145
        if($results){
            // 批量插入
            foreach ($results as $key=>$result){
1  
邓超 authored
146
                $header = $result['HEADER.FIELDS'];
1  
邓超 authored
147 148 149 150 151 152

                foreach ($result['FLAGS'] as $k=>$FLAG){
                    $result['FLAGS'][$k] = strtolower(str_replace('\\','',$FLAG));
                }
                try {
1  
邓超 authored
153
                    $file_header = $result['BODYSTRUCTURE'];
1  
邓超 authored
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176

                    // 没有收件人
                    if(!empty($header['To'])){
                        $header['To'] = MailFun::toOrFrom($header['To']);
                    }else{
                        $header['To'] = [];
                    }

                    $header['From'] = MailFun::toOrFrom($header['From']);

                    $data   =   [
                        'msgno'   =>  $key,
                        'uid'   =>  $result['UID'],
                        'subject'   =>  $header['Subject'],
                        'cc'    =>  $header['Cc']??'',
                        'bcc'    =>  $header['Bcc']??'',
                        'from'   =>  $header['From'][0]['email']??'',
                        'from_name'   =>  $header['From'][0]['name']??'',
                        'to'   =>  $header['To']?implode(',',array_column($header['To'],'email')):'',
                        'to_name'   =>  json_encode($header['To']),
                        'date'   =>  isset($header['Date'])&&$header['Date'] ? strtotime(is_array($header['Date']) ? $header['Date'][0] : $header['Date']) : strtotime($result['INTERNALDATE']),
                        'message_id'   =>  $header['Message-ID']??'',
                        'udate'   =>  strtotime($result['INTERNALDATE']),
1  
邓超 authored
177
                        'size'   =>  $result['RFC822.SIZE']??0,
1  
邓超 authored
178 179 180 181 182 183 184
                        'recent'   =>  in_array('recent',$result['FLAGS']),
                        'seen'   =>  in_array('seen',$result['FLAGS']),
                        'draft'   =>  in_array('draft',$result['FLAGS']),
                        'flagged'   =>  in_array('flagged',$result['FLAGS']),
                        'answered'   =>  in_array('answered',$result['FLAGS']),
                        'folder_id'   =>  $folder_id,
                        'email_id'    =>  $email_id,
1  
邓超 authored
185
                        'uuid'  =>  md5($email_id.$folder_id.$result['UID']),
1  
邓超 authored
186 187 188
                        'is_file'  =>  MailFun::isFile($file_header[$key]['BODYSTRUCTURE']??[]) //是否附件
                    ];
                }catch (\Throwable $e){
1  
邓超 authored
189 190 191 192
                    logs(
                        '邮件解析失败:'.PHP_EOL.$e->getMessage().PHP_EOL.print_r($result,true),
                        LOG_PATH.'/imap/mail/'.$email_id.'/'.$result['UID'].'.log'
                    );
1  
邓超 authored
193 194 195 196 197 198
                    unset($results[$key]);
                    continue;
                }

                $results[$key]  =   $data;
            }
1  
邓超 authored
199 200 201 202 203

            // 保存数据,这里其实不用再次写循环的。我想写一个
            $uuids = $db->all(listsSql::hasUuid(array_column($results,'uuid')));
            $uuids = $uuids ? array_column($uuids,null,'uuid') : [];
1  
邓超 authored
204
//            $db->transaction();
1  
邓超 authored
205 206 207 208 209 210 211 212 213 214 215 216 217
            foreach ($results as $insert){
                if(empty($uuids[$insert['uuid']])){
                    // 新增
                    $db->insert(listsSql::$table,$insert);
                }else{
                    // 修改
                    $db->update(
                        listsSql::$table,
                        $insert,
                        dbWhere(['id'=>$uuids[$insert['uuid']]['id']])
                    );
                }
            }
1  
邓超 authored
218
//            $db->commit();
1  
邓超 authored
219
1  
邓超 authored
220 221 222 223 224 225 226
            // 更新数量
            $db->update(
                folderSql::$table,
                ['last_sync_time' => time()],
                dbWhere(['id'=>$folder_id]),
                false
            );
1  
邓超 authored
227
            // 结束操作了
1  
邓超 authored
228
1  
邓超 authored
229 230 231
            // 再次调用
            $this->syncMail($email_id,$folder_id,$folder,$db);
        }
1  
邓超 authored
232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267

        return true;

    }


    /**
     * 同步 邮件 内容 body
     * @param $id
     * @param $msgno
     * @param $email_id
     * @param $folder_name
     * @param $email
     * @return bool
     * @throws \Exception
     * @author:dc
     * @time 2023/2/9 10:29
     */
    public static function syncBody($id,$msgno, $email_id,$folder_name,$email):bool {

        // 选择文件夹
        static::$client[$email]->selectFolder($folder_name);

        $body = static::$client[$email]->fetchBody([$msgno],storage_path('email/'.$email_id));

        if(!empty($body[$msgno]['RFC822.TEXT'])){
            \App\Models\Body::_insert($id,$body[$msgno]['RFC822.TEXT']);
        }

        return true;

    }



}