185 lines
6.0 KiB
PHP
185 lines
6.0 KiB
PHP
|
|
<?php
|
|||
|
|
|
|||
|
|
namespace app\command;
|
|||
|
|
|
|||
|
|
use Symfony\Component\Console\Command\Command;
|
|||
|
|
use Symfony\Component\Console\Input\InputOption;
|
|||
|
|
use Symfony\Component\Console\Helper\Table;
|
|||
|
|
use Symfony\Component\Console\Input\InputInterface;
|
|||
|
|
use Symfony\Component\Console\Output\OutputInterface;
|
|||
|
|
use support\Db;
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 拉取邮箱的UID入库
|
|||
|
|
*/
|
|||
|
|
class EmailUidCommand extends Command
|
|||
|
|
{
|
|||
|
|
protected static $defaultName = 'email-uid';
|
|||
|
|
protected static $defaultDescription = '拉取 UID 入库';
|
|||
|
|
|
|||
|
|
// protected function configure()
|
|||
|
|
// {
|
|||
|
|
// $this->addOption(
|
|||
|
|
// 'per-page', 'p',
|
|||
|
|
// InputOption::VALUE_OPTIONAL,
|
|||
|
|
// '每批处理多少条详情',
|
|||
|
|
// config('imap.per_page', 50)
|
|||
|
|
// );
|
|||
|
|
// }
|
|||
|
|
|
|||
|
|
protected function execute(InputInterface $input, OutputInterface $output): int
|
|||
|
|
{
|
|||
|
|
try {
|
|||
|
|
$cfg = config('imap');
|
|||
|
|
$mailbox = $cfg['mailbox'];
|
|||
|
|
$username = $cfg['username'];
|
|||
|
|
$password = $cfg['password'];
|
|||
|
|
// $batch = (int)$input->getOption('per-page');
|
|||
|
|
|
|||
|
|
$retryTimes = 3;
|
|||
|
|
$retryDelay = 10; // 秒
|
|||
|
|
|
|||
|
|
$inbox = false;
|
|||
|
|
$attempt = 0;
|
|||
|
|
|
|||
|
|
$maxUid = Db::table('email_uids')->max('uid');
|
|||
|
|
|
|||
|
|
while ($attempt < $retryTimes) {
|
|||
|
|
$attempt++;
|
|||
|
|
$output->writeln("尝试第 {$attempt} 次连接 IMAP...");
|
|||
|
|
$inbox = @imap_open($mailbox, $username, $password);
|
|||
|
|
|
|||
|
|
if ($inbox) {
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$error = imap_last_error();
|
|||
|
|
$output->writeln("<comment>第 {$attempt} 次失败:{$error}</comment>");
|
|||
|
|
|
|||
|
|
// 若出现 too many login failures 立即暂停一段时间
|
|||
|
|
if (stripos($error, 'Too many login failures') !== false) {
|
|||
|
|
$output->writeln("<error>检测到登录失败次数过多,暂停 60 秒避免封禁</error>");
|
|||
|
|
sleep(60);
|
|||
|
|
} else {
|
|||
|
|
sleep($retryDelay);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (!$inbox) {
|
|||
|
|
$output->writeln("<error>最终仍然连接失败:" . imap_last_error() . "</error>");
|
|||
|
|
return Command::FAILURE;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 拉取 UID 并入库
|
|||
|
|
$output->writeln("开始拉取 UID 并入库 …");
|
|||
|
|
$uids = imap_search($inbox, 'ALL', SE_UID) ?: [];
|
|||
|
|
|
|||
|
|
$uids = array_filter($uids, function($uid) use ($maxUid) {
|
|||
|
|
return $uid > $maxUid;
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
sort($uids);
|
|||
|
|
$now = time();
|
|||
|
|
$count = 0;
|
|||
|
|
|
|||
|
|
foreach ($uids as $uid) {
|
|||
|
|
try {
|
|||
|
|
Db::table('email_uids')->insert([
|
|||
|
|
'uid' => $uid,
|
|||
|
|
'fetched_at' => $now,
|
|||
|
|
'email' => $cfg['username']
|
|||
|
|
]);
|
|||
|
|
$count++;
|
|||
|
|
|
|||
|
|
$output->writeln("UID:".$uid." 已写入数据库");
|
|||
|
|
} catch (\Throwable $e) {
|
|||
|
|
$output->writeln("<comment>{$e->getMessage()}</comment>");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
imap_close($inbox);
|
|||
|
|
$output->writeln(date('Y-m-d H:i:s').":共拉到 {$count} 个 UID 并写入 email_uids 表。");
|
|||
|
|
return Command::SUCCESS;
|
|||
|
|
} catch (\Throwable $th) {
|
|||
|
|
dpr($th);
|
|||
|
|
return Command::FAILURE;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 获取指定格式的日期字符串
|
|||
|
|
*
|
|||
|
|
* @param string|null $input_date 可选,指定的日期(可以是任何PHP可解析的日期格式)
|
|||
|
|
* @return string 返回格式为 'd-M-Y' 的日期字符串(如 '28-Jul-2025')
|
|||
|
|
*/
|
|||
|
|
function getImapFormattedDate($input_date = null) {
|
|||
|
|
// 如果有传入日期参数,尝试解析
|
|||
|
|
if ($input_date !== null) {
|
|||
|
|
$timestamp = strtotime($input_date);
|
|||
|
|
if ($timestamp === false) {
|
|||
|
|
// 如果日期解析失败,使用当前日期并记录警告
|
|||
|
|
trigger_error("无法解析提供的日期: '$input_date',将使用当前日期", E_USER_WARNING);
|
|||
|
|
$timestamp = time();
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
// 如果没有提供日期,使用当前日期
|
|||
|
|
$timestamp = time();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 格式化为 IMAP 需要的日期格式 (如 '28-Jul-2025')
|
|||
|
|
return date('d-M-Y', $timestamp);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// // 使用示例
|
|||
|
|
// echo getImapFormattedDate(); // 输出当天的日期,如 '31-Jul-2025'
|
|||
|
|
// echo getImapFormattedDate('2025-07-28'); // 输出 '28-Jul-2025'
|
|||
|
|
// echo getImapFormattedDate('28/07/2025'); // 输出 '28-Jul-2025'
|
|||
|
|
// echo getImapFormattedDate('next Monday'); // 输出下个周一的日期
|
|||
|
|
|
|||
|
|
/*
|
|||
|
|
|
|||
|
|
// imap_search() 条件
|
|||
|
|
|
|||
|
|
// 1. 基本日期搜索(按接收日期)
|
|||
|
|
// 搜索今天收到的邮件
|
|||
|
|
$today = date("d-M-Y");
|
|||
|
|
$uids = imap_search($inbox, 'SINCE "' . $today . '"', SE_UID);
|
|||
|
|
|
|||
|
|
// 搜索特定日期之后的邮件
|
|||
|
|
$since_date = '15-Jul-2023';
|
|||
|
|
$uids = imap_search($inbox, 'SINCE "' . $since_date . '"', SE_UID);
|
|||
|
|
|
|||
|
|
// 搜索特定日期之前的邮件
|
|||
|
|
$before_date = '20-Jul-2023';
|
|||
|
|
$uids = imap_search($inbox, 'BEFORE "' . $before_date . '"', SE_UID);
|
|||
|
|
|
|||
|
|
// 搜索特定日期的邮件
|
|||
|
|
$on_date = '18-Jul-2023';
|
|||
|
|
$uids = imap_search($inbox, 'ON "' . $on_date . '"', SE_UID);
|
|||
|
|
|
|||
|
|
// 2. 日期范围搜索
|
|||
|
|
// 搜索2023年7月15日到2023年7月20日之间的邮件
|
|||
|
|
$start_date = '15-Jul-2023';
|
|||
|
|
$end_date = '20-Jul-2023';
|
|||
|
|
$uids = imap_search($inbox, 'SINCE "' . $start_date . '" BEFORE "' . $end_date . '"', SE_UID);
|
|||
|
|
|
|||
|
|
// 3. 结合日期和其他条件
|
|||
|
|
// 搜索2023年7月以后未读的邮件
|
|||
|
|
$date = '1-Jul-2023';
|
|||
|
|
$uids = imap_search($inbox, 'SINCE "' . $date . '" UNSEEN', SE_UID);
|
|||
|
|
|
|||
|
|
// 搜索2023年7月来自某人的邮件
|
|||
|
|
$date = '1-Jul-2023';
|
|||
|
|
$from = 'example@domain.com';
|
|||
|
|
$uids = imap_search($inbox, 'SINCE "' . $date . '" FROM "' . $from . '"', SE_UID);
|
|||
|
|
|
|||
|
|
// 4. 日期格式注意事项
|
|||
|
|
// - IMAP日期格式通常为 d-M-Y,例如 15-Jul-2023
|
|||
|
|
// - 月份使用英文缩写(Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec)
|
|||
|
|
// - 日期格式必须用双引号括起来
|
|||
|
|
|
|||
|
|
所有ID
|
|||
|
|
imap_search($inbox, 'ALL', SE_UID)
|
|||
|
|
|
|||
|
|
*/
|