1. 程式人生 > >郵件群發及自動統計退信、動態調整發送策略

郵件群發及自動統計退信、動態調整發送策略

<?php defined( 'IN_HEAVEN' ) or die ( 'Hacking Attempt!' );
/**
 * 自動郵件傳送
 * 
 * PHP versions 5
 * 
 * @category    app
 * @package     common
 * @author      zhongyiwen
 * @version     SVN: $Id: AI_SendMail.class.php 2820 2013-04-17 09:06:46Z blin $
 */



class AI_SendMail extends AppBase
{
	protected $_dsnSender = '
[email protected]
'; protected $_dsnStoreDir = './'; protected $_dsnParsePatterns = array( 'envelop' => array('/^Original-Envelope-Id:(.*)$/im', '/^X-Envelope-Id:(.*)$/im'), 'recipient' => array('/^Original-Recipient:\s*rfc822;(.*)$/im', '/^Final-Recipient:\s*rfc822;(.*)$/im'), 'arrive' => '/^Arrival\-Date:(.*)$/im', 'action' => '/^Action:\s*([a-zA-Z]+)/im', 'diagnostic' => array('/^Diagnostic\-Code:(.*(\n\x20+.+)*)$/im', '/^Status:\s*[0-9]{1}\.[0-9x]{1}\.[0-9]{1}\s*\(([^)]*)\)\s*$/im'), 'code' => array( '/^Status:\s*([0-9]{1}\.[0-9x]{1}\.[0-9]{1})[^0-9]*$/im', '/^Diagnostic\-Code:\s*smtp;\s*[0-9]{3}[^0-9]+([0-9]{1}\.[0-9x]{1}\.[0-9]{1})[^0-9]+/im', '/^Diagnostic\-Code:\s*smtp;\s*([0-9]{1}\.[0-9x]{1}\.[0-9]{1})[^0-9]+/im', ), 'smtp' => array( '/^Diagnostic\-Code:\s*smtp;\s*([0-9]{3})[^0-9]+/im', ), ); /** * 佇列中,處於正在處理中狀態的記錄殭屍時間 * 單位秒 * @var int */ protected $_zombieTimeout = 3600; /** * 最大失敗嘗試次數 * 超過次數將標記佇列中的記錄狀態為fail,並停止傳送 * @var int */ protected $_maxFailCount = 20; public function __construct($db=null, $runder=null) { parent::__construct(get_class($this), $db, $runder); $this->_dsnStoreDir = ROOT_DIR . '/data/dsn'; $this->_logDir = ROOT_DIR . '/data/logs/dsn'; if(!file_exists($this->_dsnStoreDir)){ mkdir($this->_dsnStoreDir, 0777); } if(!file_exists($this->_logDir)){ mkdir($this->_logDir, 0777); } $this->_dsnSender = SConfig::getConfig('edm.dsn_sender'); $this->_zombieTimeout = SConfig::getConfig('edm.zombie_timeout'); $this->_maxFailCount = SConfig::getConfig('edm.max_fail_count'); if(is_array($this->_dsnSender)){ if(isset($this->_dsnSender[SRunEnv::getSite()])){ $this->_dsnSender = $this->_dsnSender[SRunEnv::getSite()]; }else{ $this->_dsnSender = current($this->_dsnSender); } } //echo "<br>dsn Sender: " . $this->_dsnSender; if(SRunEnv::getMode()=='page'){ $this->setWeb(); $this->debug(true); }else{ $this->setCron(); if(($server_name=SConfig::getConfig('edm.server_name'))){ $_SERVER['SERVER_NAME'] = $server_name; } if(($http_host=SConfig::getConfig('edm.http_host'))){ $_SERVER['HTTP_HOST'] = $http_host; } } } public function sendMail($maxSents = NULL){ $cat = 'sendmail'; //$sendToken = date('Y-m-d H:i:s'); $sendToken = time(); $this->_logInfo(">>>>>>>>>>>>>[START SEND MAIL:{$sendToken}]>>>>>>>>>>>>>", $cat); if(!$maxSents){ $maxSents = SConfig::getConfig('edm.every_send_count'); } $this->_logInfo("Max send count: {$maxSents}", $cat); $oMailer = new EmailMailerModel($this->Runder, array('db'=>$this->DB)); $oDomain = new EmailDomainModel($this->Runder, array('db'=>$this->DB)); $oSend = new EmailSendModel($this->Runder, array('db'=>$this->DB)); $oSiteMailer = new Site_Mailer(); $oEmailMkt = new EmailMkt($this->Runder, null, new Site_MsgResource("cht")); $sentCounter = 0; $sentTrys = 0; $sentSuccs = 0; $maxFailCount = $this->_maxFailCount; $dsnSender = $this->_dsnSender; $this->_debugInfo("DSN Sender: $dsnSender", $cat); $aAvailMailers = $oMailer->getAllAvailMailers(); $aChecks = array('1m','5m','10m', '30m', '60m', '1d'); while($maxSents && ($sentCounter++)<$maxSents && ($queue = $oSend->popQueueByPriority())){ $sentTrys++; $msg = ">>>>> Start send mail: envelopId={$queue['envelopId']}, type={$queue['type']}, typeId={$queue['typeId']}"; $this->_logInfo($msg, $cat); // ---------------------------// // Content Check // ---------------------------// $oContent = $this->_getContentObj($queue); if($queue['type']=='edm' && $oContent && method_exists($oContent, 'getStatus')){ $status = $oContent->getStatus ( $queue ); if ($status != 'sending') { $msg = "Skip send, email content status is: {$status}"; $this->_logInfo ( $msg, $cat ); $oSend->repushQueue ( $queue, $msg ); continue; } } //---------------------------// // Domain Check //---------------------------// $domain = $queue['domain']; $setting = $oDomain->getSetting($domain); // use global setting if(!$setting){ $setting = $oDomain->getSetting('*'); } if($setting){ $stat = $oSend->getStat($domain, 0); $counter = $oSend->getSendCounter($domain, 0); // check interval time if($setting['interval'] && $stat['lastSent'] && (time()-$stat['lastSent'])<$setting['interval']){ $msg = "Skip send, Send interval less then domain interval={$setting['interval']} seconds, last sent = " . date('Y-m-d H:i:s', $stat['lastSent']); $this->_logInfo($msg, $cat); $oSend->repushQueue($queue, $msg); continue; } // check max $checkMax = true; foreach($aChecks as $ck){ $ckMax = $ck . 'Max'; if(isset($setting[$ckMax]) && $setting[$ckMax] && $counter[$ck] && $counter[$ck]>=$setting[$ckMax]){ $msg = "Skip send, Send Counter ({$ck} sent={$counter[$ck]}) >= Domain Setting ({$setting['domain']} {$ckMax} sent={$setting[$ckMax]})"; $this->_logInfo($msg, $cat); $oSend->repushQueue($queue, $msg); $checkMax = false; break; } } if(!$checkMax){ continue; } }else{ $setting = array(); } // ---------------------------------------// // Mailer Check // ---------------------------------------// $mailer = array(); if (! $setting ['mailers']) { $setting ['mailers'] = $aAvailMailers; } // filter unvailable mailer else { foreach ( $setting ['mailers'] as $k => $m ) { if (! isset ( $aAvailMailers [$m ['mailerId']] )) { unset ( $setting ['mailers'] [$k] ); } else { $setting ['mailers'] [$k] = $aAvailMailers [$m ['mailerId']]; $setting ['mailers'] [$k]['priority'] = $m['priority']; } } } // No mailer available if (! $setting ['mailers']) { $msg = "Failed send, No mailer is available for domain {$domain}"; $this->_logErr ( $msg, $cat ); $oSend->repushQueue ( $queue ); continue; } // sort mailer //$this->_sortByPriority ( $setting ['mailers'] ); $aSortedMailers = $this->_sortRandomMailers($setting['mailers']); //print_r($aSortedMailers); // check mailer foreach ( $aSortedMailers as $priority => $ms ) { foreach ( $ms as $m ) { // check mailer interval $stat = $oSend->getStat ( '', $m ['mailerId'] ); if ($m ['interval'] && $stat ['lastSent'] && (time () - $stat ['lastSent']) < $m ['interval']) { $msg = "Skip use mailer {$m['mailerName']} to send mail: send interval less then mailer interval={$m['interval']} seconds, last sent = " . date ( 'Y-m-d H:i:s', $stat ['lastSent'] ); $this->_logInfo ( $msg, $cat ); continue; } // check mailer host $skip = false; $counter = $oSend->getSendCounter ( $domain, $m ['mailerId'] ); foreach ( $aChecks as $ck ) { $ckHost = $ck . 'Host'; if (isset ( $setting [$ckHost] ) && $setting [$ckHost] && $counter [$ck] && $counter [$ck] >= $setting [$ckHost]) { $msg = "Skip use mailer {$m['mailerName']} to send mail: Send Counter ({$ck} sent={$counter[$ck]}) >= Domain Setting ({$ckHost} sent={$setting[$ckHost]})"; $this->_logInfo ( $msg, $cat ); $skip = true; break; } } if(!$skip){ // use this mailer $mailer = $m; break; } } if($mailer){ break; } } //----------------------------// // Send Mail //----------------------------// if(!$mailer){ $msg = "Failed send, No Mailer is available"; $this->_logInfo ( $msg, $cat ); $oSend->repushQueue ( $queue, $msg ); continue; } $mailerId = $mailer['mailerId']; // set mail server $oSiteMailer->setServer($mailer['mailerType'] , $mailer['mailerType']=='smtp'?$mailer['smtpHost']:$mailer['sendmail'] , $mailer['smtpUser'], $mailer['smtpPass'], $mailer['smtpPort'] ); $this->_logInfo("Use mailer [{$mailer['mailerName']}] to send mail"); $content = $this->_getContent($queue); if(!$content){ $msg = "Failed send, Email content not exists!"; $this->_logErr ( $msg, $cat ); $oSend->repushQueue ( $queue, $msg ); continue; } //print_r($content); // set mail header $oSiteMailer->setCharSet($content['charset']); $oSiteMailer->setFromName($content['fromName']); $oSiteMailer->setFrom($content['from']); $sender = $dsnSender; if($content['sender']){ $sender = $content['sender']; } if($sender){ $oSiteMailer->setSender($sender); } $this->_debugInfo("from: {$content['from']}", $cat); $this->_debugInfo("to: {$queue['address']}", $cat); $this->_debugInfo("sender: {$sender}", $cat); $this->_debugInfo("mailer: " . ($mailer['mailerType']=='smtp'?$mailer['smtpHost']:$mailer['sendmail']), $cat); // set Delivery Send Notification $oSiteMailer->setDSN($queue['envelopId']); $sentSucc = $oSiteMailer->sendHTML($content['subject'], $content['body'], $queue['address']); if(!$sentSucc){ $msg = "Failed send, " . $oSiteMailer->getErrorMsg() . " ( mailer: {$mailer['mailerName']} )"; $this->_logErr ( $msg, $cat ); $oSend->repushQueue($queue, $msg, true, $maxFailCount); continue; } // move queue to result $oSend->queue2Result($queue, $mailer['mailerId'], true, $mailer['mailerName']); $sentSuccs++; $msg = "<<<< Succ sent!"; $this->_logInfo ( $msg, $cat ); // counter $oSend->addSendCounter($domain, $mailerId); $oSend->addSendCounter('', $mailerId); $oSend->addSendCounter($domain, 0); $oSend->addSendCounter('', 0); // stat $oSend->addStat($domain, $mailerId, 'totalSent', 1); $oSend->addStat($domain, 0, 'totalSent', 1); $oSend->addStat('', $mailerId, 'totalSent', 1); $oSend->addStat('', 0, 'totalSent', 1); $oSend->addTodayStat($domain, $mailerId, 0, 'sent', 1); $oSend->addTodayStat($domain, 0, 0, 'sent', 1); $oSend->addTodayStat('', $mailerId, 0, 'sent', 1); $oSend->addTodayStat('', 0, $queue['contentId'], 'sent', 1); $oSend->addTodayStat('', 0, 0, 'sent', 1); $this->_getContentObj()->addStat($queue['contentId'], 'totalSent', 1); } if(!$sentTrys){ $this->_logInfo("No mail to send!", $cat); }else{ $this->_logInfo("Finished send:{$sendToken}, try: {$sentTrys}, succ: {$sentSuccs}", $cat); } $this->_logInfo("<<<<<<<<<<<<<<[END SEND MAIL:{$sendToken}]<<<<<<<<<<<<<<"); } protected function _sortRandomMailers($mailers){ $aSortedMailers = array(); $this->_sortByPriority($mailers); foreach($mailers as $m){ $aSortedMailers[$m['priority']][] = $m; } foreach($aSortedMailers as $p=>$ms){ shuffle($aSortedMailers[$p]); } return $aSortedMailers; } protected function _sortByPriority(&$array){ usort($array, array($this, '_cb_sort_priority')); // reset array reset($array); return $array; } protected function _cb_sort_priority($arr1, $arr2){ $a = $arr1['priority']; $b = $arr2['priority']; if ($a == $b) { return 0; } return ($a < $b) ? -1 : 1; } public function saveDSN(){ $cat = "savedsn"; $this->_logInfo(">>>>>>>>>>>>>[START SAVE DSN]>>>>>>>>>>>>>", $cat); $source = file_get_contents("php://stdin"); /* * //$handle = STDIN; $handle = fopen("php://stdin", "r"); if ($handle) * { while (!feof($handle)) { $buffer = fgets($handle, 4096); //echo * $buffer; file_put_contents($bounce_file, $buffer, FILE_APPEND); } * fclose($handle); } */ if (! $source) { $this->_logInfo ( "Source content is empty!", $cat ); } else { try { $oSend = new EmailSendModel ( $this->Runder, array('db'=>$this->DB) ); $dsnId = $oSend->addDSN ( $source, time() ); $this->_logInfo ( "Succ saved dsn mail into database, dsnId={$dsnId}" , $cat); } catch ( Exception $e ) { // store in file $nowTime = time (); $file = date ( 'YmdHis' ) . "_" . $nowTime . ".dsn"; if(false===file_put_contents($this->_dsnStoreDir . '/' . $file, $source)){ $this->_logErr("Failed save dsn mail into file {$file}", $cat); }else{ $this->_logInfo("Succ saved dsn mail as file: {$file}", $cat); } } } $this->_logInfo("<<<<<<<<<<<<<<[END SAVE DSN]<<<<<<<<<<<<<<", $cat); } public function restoreDSN() { $cat = 'restoredsn'; $oSend = new EmailSendModel ( $this->Runder, array ( 'db' => $this->DB ) ); // load stored dsn into database $aFiles = File_Func::getDirFiles ( $this->_dsnStoreDir, '.dsn' ); if ($aFiles) { $this->_logInfo ( ">>>>> Try to move stored dsn files in disk into database", $cat ); $loadSuccs = 0; foreach ( $aFiles as $file ) { $file = trim ( $file ); if (! $file || ! file_exists ( $file )) { continue; } $source = file_get_contents ( $file ); $receiveTime = filemtime ( $file ); if ($source) { $dsnId = $oSend->addDSN ( $source, $receiveTime ); $this->_logInfo ( "Succ saved dsn mail into database, dsnId={$dsnId}", $cat ); $loadSuccs ++; // delete dsn file unlink ( $file ); } } $this->_logInfo ( "Total stored: {$loadSuccs}", $cat ); $this->_logInfo ( "<<<<< Succ stored all dsn file into database!", $cat ); } } public function parseDSN($maxParses = NULL){ $cat = 'parsedsn'; $this->_logInfo(">>>>>>>>>>>>>[START PARSE DSN]>>>>>>>>>>>>>", $cat); $this->restoreDSN(); if(!$maxParses){ $maxParses = SConfig::getConfig('edm.every_parse_count'); } $this->_logInfo("Max parse count: {$maxParses}", $cat); $aActionStatusMaps = array( 'relayed' => 'succ', 'delivered' => 'succ', 'failed' => 'bounce', ); $oSend = new EmailSendModel ( $this->Runder, array ( 'db' => $this->DB ) ); $oContent = new EmailContentModel($this->Runder, array('db'=>$this->DB)); $parseCounter = 0; $parseTrys = 0; $parseSucc = 0; while($maxParses && ($parseCounter++)<$maxParses && ($dsn = $oSend->popDSNByTime())){ $parseTrys++; $this->_logInfo("Try to parse DSN ID:{$dsn['dsnId']}", $cat); if(!($aParse=$this->_parseDSN($dsn['source'])) || empty($aParse['envelop']) || empty($aParse['recipient'])){ $msg = "Parse Error: Cann't found envelop-Id or recipient!"; $this->_logInfo($msg, $cat); $oSend->dsn2Archive($dsn, $msg, false); continue; } $envelopId = $aParse['envelop']; $recipient = $aParse['recipient']; $this->_logInfo("Found envelopId={$envelopId}", $cat); $this->_debugInfo("parse result = " . print_r($aParse, true), $cat); $result = $oSend->findResult($envelopId); if(!$result){ $msg = "Fail: Envelop result not exists!"; $this->_logInfo($msg, $cat); $oSend->dsn2Archive($dsn, $msg, false); continue; }elseif(strcasecmp('sent', $result['status'])){ $msg = "Fail: result status is not 'sent', but '{$result['status']}'!"; $this->_logInfo($msg, $cat); $oSend->dsn2Archive($dsn, $msg, false); continue; }elseif(strcasecmp($recipient, $result['address'])){ $msg = "Fail: DSN recipient doesn't match result address: {$result['address']}!"; $this->_logInfo($msg, $cat); $oSend->dsn2Archive($dsn, $msg, false); continue; } $aParse['action'] = strtolower($aParse['action']); $sentStatus = isset($aActionStatusMaps[$aParse['action']])?$aActionStatusMaps[$aParse['action']]:'unknown'; $bounceClass = ''; if($sentStatus=='bounce'){ $bounceClass = $this->_analyseBounceClass($aParse['code'], $aParse['smtp']); } $aDSN = array_merge($dsn, $aParse); $aDSN['arriveTime'] = $aParse['arrive']?intval(strtotime($aParse['arrive'])):0; $aDSN['bounceClass'] = $bounceClass; // Check arriveTime & statusCode && diagnosticMsg $codeLength = 16; if(strlen($aDSN['code'])>$codeLength){ $this->_logErr("statusCode length over {$codeLength}: {$aDSN['code']}", $cat); $this->_logErr("Parse Result = " . print_r($aParse, true), $cat); $aDSN['code'] = substr($aDSN['code'], 0, $codeLength); } $diagnosticLength = 500; if(strlen($aDSN['diagnostic'])>$diagnosticLength){ $this->_logErr("diagnosticMsg length over {$diagnosticLength}: {$aDSN['diagnostic']}", $cat); $this->_logErr("Parse Result = " . print_r($aParse, true), $cat); $aDSN['diagnostic'] = substr($aDSN['diagnostic'], 0, $diagnosticLength); } if($aDSN['arriveTime'] && (!is_int($aDSN['arriveTime']) || $aDSN['arriveTime']>2147483647 || $aDSN['arriveTime']<0)){ $this->_logErr("arriveTime is error: {$aDSN['arriveTime']} , dsnId={$aDSN['dsnId']}", $cat); $this->_logErr("Parse Result = " . print_r($aParse, true), $cat); $aDSN['arriveTime'] = 0; } $oSend->dsn2Result($envelopId, $sentStatus, $aDSN); $oSend->dsn2Archive($dsn, $aParse); $this->_logInfo("Save dsn result succ!", $cat); // stat $domain = $result['domain']; $mailerId = $result['mailerId']; $contentId = $result['contentId']; $statField = ''; $todayStatField = ''; $contentStatField = ''; $bounceClassField = ''; switch($sentStatus){ case 'succ': $statField = 'totalSucc'; $todayStatField = 'succ'; $contentStatField = 'totalSucc'; break; case 'bounce': $statField = 'totalBounce'; $todayStatField = 'bounce'; $contentStatField = 'totalBounce'; if($bounceClass){ $bounceClassField = 'bounce' . ucfirst($bounceClass); } break; default: } // stat if ($statField) { $oSend->addStat ( $domain, $mailerId, $statField, 1 ); $oSend->addStat ( $domain, 0, $statField, 1 ); $oSend->addStat ( '', $mailerId, $statField, 1 ); $oSend->addStat ( '', 0, $statField, 1 ); } if($todayStatField){ $oSend->addTodayStat($domain, $mailerId, 0, $todayStatField, 1); $oSend->addTodayStat($domain, 0, 0, $todayStatField, 1); $oSend->addTodayStat('', $mailerId, 0, $todayStatField, 1); $oSend->addTodayStat('', 0, $contentId, $todayStatField, 1); $oSend->addTodayStat('', 0, 0, $todayStatField, 1); } if($contentStatField){ $oContent->addStat($contentId, $contentStatField, 1); } if($bounceClassField){ $oSend->addStat ( $domain, $mailerId, $bounceClassField, 1 ); $oSend->addStat ( $domain, 0, $bounceClassField, 1 ); $oSend->addStat ( '', $mailerId, $bounceClassField, 1 ); $oSend->addStat ( '', 0, $bounceClassField, 1 ); $oSend->addTodayStat($domain, $mailerId, 0, $bounceClassField, 1); $oSend->addTodayStat($domain, 0, 0, $bounceClassField, 1); $oSend->addTodayStat('', $mailerId, 0, $bounceClassField, 1); $oSend->addTodayStat('', 0, $contentId, $bounceClassField, 1); $oSend->addTodayStat('', 0, 0, $bounceClassField, 1); $oContent->addStat($contentId, $bounceClassField, 1); } // counter if ($sentStatus == 'bounce') { $oSend->addBounceCounter ( $domain, $mailerId ); $oSend->addBounceCounter ( '', $mailerId ); $oSend->addBounceCounter ( $domain, 0 ); $oSend->addBounceCounter ( '', 0 ); } $parseSucc++; } if(!$parseTrys){ $this->_logInfo("No dsn to parse!", $cat); }else{ $this->_logInfo("Finished parse, try: {$parseTrys}, succ: {$parseSucc}", $cat); } $this->_logInfo("<<<<<<<<<<<<<<[END PARSE DSN]<<<<<<<<<<<<<<", $cat); } protected function _parseDSN($source){ $aParse = array(); foreach($this->_dsnParsePatterns as $t=>$pt){ $pats = $pt; if(!is_array($pt)){ $pats = array($pt); } foreach ( $pats as $p ) { if (preg_match ( $p, $source, $match )) { //if (preg_match_all ( $p, $source, $match )) { $aParse [$t] = trim ( $match [1] ); break; } } } return $aParse?$aParse:false; } protected function _analyseBounceClass($code, $smtp=NULL){ $aBounceCodeMaps = array(); $aBounceCodeMaps['address'] = array( '5.1.0', '5.1.1', '5.1.2', '5.1.4', '5.1.6', '5.1.7', '5.1.8', '5.1.9', '501', ); $aBounceCodeMaps['mailbox'] = array( '5.2.0', '5.2.1', '5.2.2', '5.2.3', '5.2.4', '552', '553', '450', '550', ); $aBounceCodeMaps['system'] = array( '5.3.0', '5.3.1', '5.3.2', '5.3.3', '5.3.4', '5.3.5', '452', '451', '554', ); $aBounceCodeMaps['network'] = array( '5.4.0', '5.4.1', '5.4.2', '5.4.3', '5.4.4', '5.4.5', '5.4.6', '5.4.7', '421', ); $aBounceCodeMaps['protocol'] = array( '5.5.0', '5.5.1', '5.5.2', '5.5.3', '5.5.4', '5.5.5', '5.5.6', '500', '502', '503', '504', '554', '555', '530', ); $aBounceCodeMaps['content'] = array( '5.6.0', '5.6.1', '5.6.2', '5.6.3', '5.6.5', '5.6.6', '5.6.7', '5.6.8', '5.6.9', ); $aBounceCodeMaps['security'] = array( '5.7.0', '5.7.1', '5.7.2', '5.7.3', '5.7.4', '5.7.5', '5.7.6', '5.7.7', '5.7.8', '5.7.9', '5.7.10', '5.7.11', '5.7.12', '5.7.13', '5.7.14', '5.7.15', '535', '523', '534', '538', '524', '525', ); if ($code) { foreach ( $aBounceCodeMaps as $type => $codes ) { if (in_array ( $code, $codes )) { return $type; } } } if ($smtp) { foreach ( $aBounceCodeMaps as $type => $codes ) { if (in_array ( $smtp, $codes )) { return $type; } } } return 'other'; } public function resetCounter(){ $cat = 'resetcounter'; $this->_logInfo(">>>>>>>>>>>>>[START RESET COUNTER]>>>>>>>>>>>>>", $cat); $oSend = new EmailSendModel ( $this->Runder, array ( 'db' => $this->DB ) ); $aResetCounters = array( '1m' => 60, '5m' => 60*5, '10m' => 60*10, '30m' => 60*30, '60m' => 60*60, '1d' => 60*60*24, '7d' => 60*60*24*7, '30d' => 60*60*24*30, '365d' => 60*60*24*365, ); $nowTime = time(); $AllSendCounters = $oSend->getAllSendCounters(); foreach($AllSendCounters as $counter){ foreach($aResetCounters as $c=>$t){ if(isset($counter[$c])){ $r = $c.'Reset'; if(!$counter[$r] || ($nowTime - $counter[$r])>=$t){ $oSend->resetSendCounter($counter['domain'], $counter['mailerId'], $c); $this->_logInfo("Reset Send Counter: $c, domain={$counter['domain']}, mailerId={$counter['mailerId']}, last reset time: " . date('Y/m/d H:i:s', $counter[$r])); } } } } $AllBounceCounters = $oSend->getAllBounceCounters(); foreach($AllBounceCounters as $counter){ foreach($aResetCounters as $c=>$t){ if(isset($counter[$c])){ $r = $c.'Reset'; if(!$counter[$r] || ($nowTime - $counter[$r])>=$t){ $oSend->resetBounceCounter($counter['domain'], $counter['mailerId'], $c); $this->_logInfo("Reset Bounce Counter: $c, domain={$counter['domain']}, mailerId={$counter['mailerId']}, last reset time: " . date('Y/m/d H:i:s', $counter[$r])); } } } } $this->_logInfo("<<<<<<<<<<<<<<[END RESET COUNTER]<<<<<<<<<<<<<<", $cat); } public function cleanQueue(){ $cat = 'cleanqueue'; $this->_logInfo(">>>>>>>>>>>>>[START CLEAN QUEUE]>>>>>>>>>>>>>", $cat); $oSend = new EmailSendModel ( $this->Runder, array ( 'db' => $this->DB ) ); $oContent = new EmailContentModel($this->Runder, array('db'=>$this->DB)); $timeout = $this->_zombieTimeout; // recover zombie queues $zombieQueueCount = $oSend->recoverZombieQueue($timeout); $this->_logInfo("Recover {$zombieQueueCount} zombie send queue", $cat); $zombieDSNCount = $oSend->recoverZombieDSN($timeout); $this->_logInfo("Recover {$zombieDSNCount} zombie dsn queue", $cat); // move fail queue to result $aFailQueues = $oSend->cleanFailQueue(); $failQueueCount = is_array($aFailQueues)?count($aFailQueues):0; $this->_logInfo("Clean {$failQueueCount} fail send queue", $cat); if ($aFailQueues) { foreach ( $aFailQueues as $queue ) { if (! $queue) { continue; } $oContent->addStat ( $queue ['contentId'], 'totalFail' ); } } $this->_logInfo("<<<<<<<<<<<<<<[END CLEAN QUEUE]<<<<<<<<<<<<<<", $cat); } public function finishStatus(){ $cat = 'finishstatus'; $this->_logInfo(">>>>>>>>>>>>>[START FINISH STATUS]>>>>>>>>>>>>>", $cat); $oSend = new EmailSendModel ( $this->Runder, array ( 'db' => $this->DB ) ); $oContent = new EmailContentModel($this->Runder, array('db'=>$this->DB)); $oEDMContent = new EDMContentModel($this->Runder, array('db'=>$this->DB)); // draft : 草稿 sending: 傳送中 sent : 傳送完成 stop: 暫停傳送 $aSendingContents = $oContent->getSendingContents(); $totalFinish = 0; foreach($aSendingContents as $content){ if(!($queue=$oSend->contentQueueExist($content['contentId']))){ $oContent->sendFinish($content['contentId']); //need to update the email_content status if all email content have been sent / update email forward history finished time $oEDMContent->updateEmailContentFinishTime($content); $this->_logInfo("Finish send contentId={$content['contentId']}", $cat); $totalFinish++; } } if(!$totalFinish){ $this->_logInfo("No content finish send!", $cat); }else{ $this->_logInfo("Total finish send = {$totalFinish}.", $cat); } $this->_logInfo("<<<<<<<<<<<<<<[END FINISH STATUS]<<<<<<<<<<<<<<", $cat); } public function pushQueue(){ $cat = 'pushqueue'; $this->_logInfo(">>>>>>>>>>>>>[START PUSH QUEUE]>>>>>>>>>>>>>", $cat); $oSend = new EmailSendModel ( $this->Runder, array ( 'db' => $this->DB ) ); $oContent = new EmailContentModel($this->Runder, array('db'=>$this->DB)); $totalPush = 0; $aReadyContents = $oContent->getReadyContents(); foreach($aReadyContents as $content){ $recipientCount = $oSend->pushRecipients($content['contentId'], $content); $this->_logInfo("Push contentId={$content['contentId']}, recipients={$recipientCount}", $cat); $totalPush++; } if(!$totalPush){ $this->_logInfo("No ready content!", $cat); }else{ $this->_logInfo("Total push ready content = {$totalPush}.", $cat); } $this->_logInfo("<<<<<<<<<<<<<<[END PUSH QUEUE]<<<<<<<<<<<<<<", $cat); } protected function _getContentObj($queue=NULL){ static $_contentObjs = array(); $type = 'default'; if($queue){ if(is_array($queue)){ $type = $queue['type']; }else{ $type = $queue; } } if(!isset($_contentObjs[$type])){ switch($type){ case 'edm': case 'forward': $_contentObjs[$type] = new EDMContentModel($this->Runder, array('db'=>$this->DB)); break; default: $_contentObjs[$type] = new EmailContentModel($this->Runder, array('db'=>$this->DB)); break; } } return $_contentObjs[$type]; } protected function _getContent($queue){ static $oEmailMkt = NULL; $oContent = $this->_getContentObj($queue); if(!$oContent){ return false; } $content = $oContent->getContent($queue); if(!$content){ return false; } if($queue['type']=='edm' || $queue['type']=='forward'){ $content['charset'] = 'utf-8'; $content['contentType'] = 'html'; $content['fromName'] = $content['sender_name']; $content['from'] = $content['sender_email']; if(!$oEmailMkt){ $oEmailMkt = new EmailMkt($this->Runder, null, new Site_MsgResource("cht")); } $content['subject'] = $oEmailMkt->genContentSubject($content,$queue); $content['body'] = $oEmailMkt->genContentBody($content,$queue); } return $content; } }


相關推薦

郵件群發自動統計退動態調整策略

<?php defined( 'IN_HEAVEN' ) or die ( 'Hacking Attempt!' ); /** * 自動郵件傳送 * * PHP versions 5 * * @category app * @package common * @autho

Kotlin基本語法筆記之類型檢測自動類型轉換循環

ems lis tst 表達 turn gets nbsp null items 類型檢測及自動類型轉換 is運算符用於檢測一個表達式是否為某類型的一個實例檢測出為某類型後,檢測後的分支中可以直接當作該類型使用,無需顯示轉換 fun getStringLength(

使用selenium自動登錄126/163郵箱並郵件

點擊 word tool mail 使用 table password python2 switch 我使用的是python2.7.13+selenium ps:幾天之前,我曾多次嘗試寫這段代碼,但是在點擊寫信的步驟時失敗了,我想我的問題應該大致是這幾點: 1

c++動態庫封裝調用(1動態庫介紹)

oca 新版本 指令 二進制 運行時 失去 理論 load 程序 1、一個程序從源文件編譯生成可執行文件的步驟: 預編譯 --> 編譯 --> 匯編 --> 鏈接 (1)預編譯,即預處理,主要處理在源代碼文件中以“#”開始的預編譯指令,如宏展開、處理條

Android仿微錄製音訊併發功能

1.首先是主頁面的佈局     佈局採用線性佈局,上面使用的一個ListView,下面使用的是一個自定義的Button(會在下面進行介紹) <LinearLayout xmlns:android="http://schemas.android.com/apk/re

python利用企業微api來進行自定義報警的類實現

python 微信報警python利用企業微信api來進行發送自定義報警的類實現企業微信註冊打開http://work.weixin.qq.com/企業微信主頁;點擊企業註冊;填寫相關信息,營業執照和註冊號可以不用填,直接下一步,按照提示操作即可;註冊完成後,登陸,就顯示如下界面:點擊我的企業標簽:看到如上界

模板消息

href mode ssa shu from ole on() pin 申請 整個開發流程,我在“簡書” 上看到了一個完整的開發流程。https://www.jianshu.com/p/eb0e9c4dcdfe 微信官方接口為:https://mp.weixin.qq.co

爬取一個天氣預報結合微公總號

verify apple ade urn 1.5 pytho elf 二維碼 decode 最近看見我的一個朋友些的一個爬取天氣預報的爬蟲不錯,後來發現每次執行發送的時候非常的不方便,每次都要掃描二維碼,就想起了以前zabbi公總號的方法傳送天氣預報信息:/test cat

c# 微開發 《主動內容》

ccs efi open ima end err log summary 用戶 需要用戶和公眾號,在48小時內有過互動,不然發不出去。 public const string customUrl = "https://api.weixin.qq.com/cgi-b

在線管理即時通訊通知

即時通訊 在線管理 同步刷新多個客戶端①需要的jar包java_websocket.jar②在線聊天服務池類(在線用戶管理)package com.kentra.plugin.websocketOnline; import java.util.ArrayList; import java.util.Coll

Spring整合JMSIBM MQ和接收消息

turn this oat implement actor AI encoding title post Spring整合JMS、IBM MQ發送和接收消息 2017年03月23日 17:45:30 轉載。 https://blog.csdn.net/Yolanda

地理處理模型案例教程培訓低價

壓縮包 應用 time tom 名稱 width cgi 說明 宋體 地理處理模型、案例、教程、培訓低價發送 模型價格 模型名稱價格(元)描述最大值提取50針對柵格數據,例如氣溫,降水,水文分析河網提取50針對DEM數據空文件刪除100刪除某一文件夾或數據庫裏的記錄個數

小程序如何驗證碼,無需搭建服務器

.data 數字 mes 你好 cli message 多次 cti 自己 自從微信小程序提供雲開發支持,開發者無需搭建後臺服務器,使用微信提供的核心API就可以實現應用功能,此時就需要小程序能夠自己發送短信,比如短信驗證碼,榛子雲短信(http://smsow.zhenz

Kubernetes資源擴容項目策略

src 操作 所有 .com https 等待 生產 process mas Master擴容 100臺node,2臺master足夠了 這個在集群中講過,可以參考之前的 Node擴容 這個在集群中講過,可以參考之前的 Pod 擴容 以下是手動擴容為5個kubectl sc

Centos6.10下Open-falcon微郵件告警安裝配置使用

1 郵件告警 1.1 郵件告警元件安裝 使用官方提供的一個郵件閘道器(https://github.com/open-falcon/mail-provider)配置郵件報警 下載mail-provider並安裝 #下載 cd ~/open-falcon #我的工作目錄 wget h

新增客戶端自動獲取微地址功能優化商品規格編輯體驗更新!

酷客多 微信小程序最近,微信小程序官方又更新了,又開放十幾項接口,接口的增加意味著擁有小程序的企業主有了更多福利。酷客多作為國內首家微信小程序技術服務商自然要緊跟微信小程序官方的更新步伐,為客戶提供最新最前沿的技術服務,為此我們做了此次更新。此次版本更新主要是小程序端地址添加優化和後端商品規格添加體驗優化。

動態創建table,按回車鍵自動切換光標位置金額統計Js代碼實現

tex move break mov sel 自動 false click ipp 1.JS頁面輸出拼接的Table代碼,參數來自於Ajax請求響應回來的Json數據 $("#rightTipPayBackTb").append("<tr><td>

統計各個數字空白符及其他字符出現的次數1.13,1.14

col clu || 長度 編寫一個程序 isp 水平 div style 統計各個數字、空白符及其他字符出現的次數 1 #include<stdio.h> 2 3 int main() 4 { 5 int i, j, c, nc, nl;

iOS-打電話郵件【指定QQ用戶QQ消息】

ios 郵件內容 取消 bar 內容 tex 是否 ext else 1.發短信 頭文件 #import <MessageUI/MessageUI.h> 頭部代理 @interface ViewController ()<MFMessageCompo

文件系統基礎ext文件系統創建管理詳解文件系統的使用卸載fstab文件格式自動掛載系統的

文件系統創建文件系統--分區格式化 格式化: 低級格式化: 劃分磁道 高級格式化: 創建文件系統,按照某種特定的標準,將整個分區劃分為大小相同的若幹小的邏輯編址單元,每個這樣的單元稱為塊(block)【windows中稱為簇】; 劃分塊的標準: 在Linux的文件系統中,主要的塊的劃分