Browse Source

Fix #14 : ne pas perdre un mail en cas d'echec de l'envoi

- on se donne 5 essais, personalisable via la constante _FACTEUR_NOMBRE_ESSAIS_ENVOI_MAIL
- lors d'un echec, on dump les arguments de la fonction facteur_envoyer_mail() dans un fichier json dans facteur/retry/ et on programme un job queue pour re-essayer plus tard, avec un delai de 10mn, puis 1h, puis 3h puis 12h puis 24h en fonction du nombre d'essai
- lorsqu'on atteint le nombre maxi d'essais, on dump le mail en echec dans tmp/facteur/failed ce qui permet de garder une trace ou de retraiter l'envoi manuellement ulterieurement
pull/16/head
Cerdic 8 months ago committed by Gitea
parent
commit
79ebcf8b98
  1. 18
      inc/Facteur/FacteurMail.php
  2. 112
      inc/facteur.php

18
inc/Facteur/FacteurMail.php

@ -71,6 +71,11 @@ class FacteurMail extends PHPMailer {
*/
protected $important = false;
/**
* @var bool
*/
protected $isFinalTry = true;
protected $sendFailFunction = null;
protected static $logName = 'facteur';
@ -273,6 +278,15 @@ class FacteurMail extends PHPMailer {
$this->important = $important;
}
/**
* Indique si l'envoi est un dernier essai et que tout echec est definitif
* @param $isFinalTry
* @return void
*/
public function setIsFinalTry($isFinalTry) {
$this->isFinalTry = $isFinalTry;
}
/**
* Set the fail function to call if an important mail was not sent
* @param $function
@ -525,7 +539,9 @@ class FacteurMail extends PHPMailer {
*/
protected function sendAlertIfNeeded($res) {
if ($res === false) {
if ($this->important and !empty($this->sendFailFunction)) {
if ($this->important
and $this->isFinalTry
and !empty($this->sendFailFunction)) {
$facteur_envoyer_alerte_fail = charger_fonction('facteur_envoyer_alerte_fail', 'inc');
$facteur_envoyer_alerte_fail($this->sendFailFunction['function'], $this->sendFailFunction['args'], $this->sendFailFunction['include']);
}

112
inc/facteur.php

@ -8,6 +8,7 @@
* @package SPIP\Facteur\Inc\Facteur_factory
*/
defined('_FACTEUR_NOMBRE_ESSAIS_ENVOI_MAIL') || define('_FACTEUR_NOMBRE_ESSAIS_ENVOI_MAIL', 5);
/**
* @param array|string $destinataires
@ -38,7 +39,8 @@
* @return bool
* @throws Exception
*/
function facteur_envoyer_mail($destinataires, string $sujet, array $message) {
function facteur_envoyer_mail($destinataires, string $sujet, array $message, int $try = 0) {
$args_retry = [$destinataires, $sujet, $message, $try];
$important = false;
// si $message est un tableau -> fonctionnalites etendues
@ -240,16 +242,122 @@ function facteur_envoyer_mail($destinataires, string $sujet, array $message) {
$facteur->setSendFailFunction('envoyer_mail', $args, 'inc/');
}
// indiquer au facteur si c'est un essai final ou non
$facteur->setIsFinalTry($try >= _FACTEUR_NOMBRE_ESSAIS_ENVOI_MAIL);
$retour = $facteur->Send();
if (!$retour) {
spip_log('Erreur Envoi mail via Facteur : ' . print_r($facteur->ErrorInfo, true), 'mail' . _LOG_ERREUR);
// si le mail est important, c'est le facteur qui aura gere l'envoi de l'alerte fail
// si le mail est important, le facteur aura gere l'envoi de l'alerte fail
// mais ici on gere une nouvelle tentative plus tard ou un dump du mail en echec
facteur_reprogrammer_ou_dumper_mail_echec($args_retry, $try + 1);
}
return $retour;
}
/**
* Ressayer d'envoyer un mail dumpé dans un fichier suite à un essai
*
* @param string $mailid
* @return void
*/
function facteur_retry_envoyer_mail(string $mailid) {
$dir_tmp_facteur = sous_repertoire(_DIR_TMP, 'facteur');
$file = $dir_tmp_facteur . $mailid . ".json";
if (file_exists($file)) {
$arguments = file_get_contents($file);
if ($arguments = json_decode($arguments, true)) {
include_spip('inc/envoyer_mail');
$function = array_shift($arguments);
if (function_exists($function)) {
spip_log("facteur_retry_envoyer_mail: Nouvel essai pour l'envoi du mail $mailid via $function()", 'facteur' . _LOG_INFO_IMPORTANTE);
@unlink($file);
$function(...$arguments);
return;
}
}
spip_log("facteur_retry_envoyer_mail: Impossible de traiter le mail $mailid", 'facteur' . _LOG_ERREUR);
$dir_tmp_facteur_failed = sous_repertoire($dir_tmp_facteur, 'failed');
$file_archived = $dir_tmp_facteur_failed . basename($file);
@rename($file, $file_archived);
spip_log("Mail en echec archivé dans : $file_archived", 'facteur' . _LOG_INFO_IMPORTANTE);
}
}
/**
* Gérer l'echec de l'envoi de mail :
* * si on a atteint le nombre maxi d'essais on le dump dans tmp/facteur/failed/
* * sinon on le dump dans tmp/facteur/retry/ et on lance un job_queue pour le re-essayer plus tard
*
* @param array $arguments
* @param int $try
* @return void
*/
function facteur_reprogrammer_ou_dumper_mail_echec(array $arguments, int $try) {
$dir_tmp_facteur = sous_repertoire(_DIR_TMP, 'facteur');
// ajouter en tete le nom de la fonction
array_unshift($arguments, 'facteur_envoyer_mail');
// md5 invariant avec le nombre d'essai
array_pop($arguments);
$md5 = md5(json_encode($arguments));
// mettre a jour le nombre d'essai dans les arguments
$arguments[] = $try;
// le dump des arguments
$arguments = json_encode($arguments);
if ($try >= _FACTEUR_NOMBRE_ESSAIS_ENVOI_MAIL) {
// un mail definitivement en echec est stocke pour retraitement manuel eventuel
$dir_tmp_facteur = sous_repertoire($dir_tmp_facteur, 'failed');
$file = $dir_tmp_facteur . $md5 . ".json";
file_put_contents($file, $arguments);
spip_log("Mail en echec archivé dans : $file", 'facteur' . _LOG_INFO_IMPORTANTE);
}
else {
// on ressaye plus tard
$dir_tmp_facteur = sous_repertoire($dir_tmp_facteur, 'retry');
$fileid = "$try-$md5";
$file = $dir_tmp_facteur . $fileid . ".json";
file_put_contents($file, $arguments);
switch ($try) {
case 1: //
// 10mn
$delay = 10 * 60;
break;
case 2:
// 1h
$delay = 60 * 60;
break;
case 3:
// 3h
$delay = 3 * 60 * 60;
break;
case 4:
// 12h
$delay = 12 * 60 * 60;
break;
case 5:
default:
// 24h
$delay = 24 * 60 * 60;
break;
}
spip_log("Mail archivé pour un nouvel essai dans {$delay}s : $file", 'facteur' . _LOG_INFO_IMPORTANTE);
job_queue_add('facteur_retry_envoyer_mail', "Re-essayer d'envoyer le mail en echec $fileid", ["retry/$fileid"], 'inc/facteur', false, time() + $delay);
}
}
/**
* Initialiser les destinataires en s'assurant qu'il y en a au moins un valide

Loading…
Cancel
Save