Created
January 30, 2024 16:54
-
-
Save noogen/bb57f10d62c1901559c751cafdbbd4b8 to your computer and use it in GitHub Desktop.
Laravel CPanel bounce email handler as a artisan command
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace App\Console\Commands; | |
use Illuminate\Console\Command; | |
use Illuminate\Support\Facades\Http; | |
class CpanelEmailCallback extends Command | |
{ | |
/** | |
* The console command description. | |
* | |
* @var string | |
*/ | |
protected $description = 'Cpanel Email Callback'; | |
/** | |
* The name and signature of the console command. | |
* | |
* @var string | |
*/ | |
protected $signature = 'cpanel_email_callback {path}'; | |
/** | |
* Create a new command instance. | |
* | |
* @return void | |
*/ | |
public function __construct() | |
{ | |
parent::__construct(); | |
} | |
/** | |
* Execute the console command. | |
* | |
* @return mixed | |
*/ | |
public function handle() | |
{ | |
$path = $this->argument('path'); | |
$sep = DIRECTORY_SEPARATOR; | |
$dir = $sep.trim(base_path($path), $sep); | |
// example: php artisan cpanel_email_callback tests/_data/bounces | |
// example: php artisan cpanel_email_callback ../mail/mailer-domain.email | |
$this->processDir("{$dir}{$sep}*"); | |
$this->processDir("{$dir}{$sep}cur{$sep}*"); | |
$this->processDir("{$dir}{$sep}new{$sep}*"); | |
$this->processDir("{$dir}{$sep}.spam{$sep}cur{$sep}*"); | |
$this->processDir("{$dir}{$sep}.spam{$sep}new{$sep}*"); | |
$this->processDir("{$dir}{$sep}.Junk{$sep}cur{$sep}*"); | |
$this->processDir("{$dir}{$sep}.Junk{$sep}new{$sep}*"); | |
} | |
public function processDir($dir) | |
{ | |
\Log::info("Processing " . $dir); | |
if (file_exists($dir)) { | |
$files = array_filter(glob($dir . DIRECTORY_SEPARATOR . '*'), 'is_file'); | |
foreach($files as $file) | |
{ | |
\Log::info("{$file} ..."); | |
$this->processFile($file); | |
} | |
} | |
} | |
public function processFile($file) | |
{ | |
$message = file_get_contents($file); | |
$matches = Array(); | |
preg_match('/Final-Recipient:\s?rfc822;\s?(.*)/', $message, $matches); | |
$address = $matches[1]; | |
$bounceType = $this->bounceType($message); | |
$env = \App::environment(); | |
if ($bounceType === 'soft' || $bounceType === 'hard') { | |
$callbackApi = 'https://callback.api/api/v1/bounces'; | |
$url = "{$callbackApi}/{$bounceType}?email=".urlencode($address); | |
\Log::info("Bounce {$url} ..."); | |
// TODO: upgrade to SES clone and do a POST here | |
$response = Http::get($url); | |
if ($env === 'prd') { | |
unlink($handle); | |
} | |
} | |
} | |
/** | |
* Check bounce type by message | |
* | |
* @param string $message the entire message string | |
* @return string soft|hard|unknown | |
*/ | |
public function bounceType($message) | |
{ | |
$matches = Array(); | |
preg_match('/Diagnostic-Code:\s?smtp;\s?(.*)/', $message, $matches); | |
$dcode = $matches[1]; | |
\Log::info("Diagnostic-Code: ${dcode} ..."); | |
$codeStart = $dcode[0]; | |
if ($codeStart == '4') { | |
return 'soft'; | |
} else if ($codeStart == '5') { | |
return 'hard'; | |
} | |
return 'unknown'; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment