source: spip-zone/_plugins_/mailshot/trunk/inc/mailshot.php @ 108049

Last change on this file since 108049 was 108049, checked in by marcimat@…, 3 years ago

Suite de r102612 : une erreur s’était subtilement glissée dans la requête SQL car il manquait la fin du order by.

File size: 11.0 KB
Line 
1<?php
2/**
3 * Plugin MailShot
4 * (c) 2012 Cedric Morin
5 * Licence GNU/GPL
6 */
7
8if (!defined('_ECRIRE_INC_VERSION')) return;
9
10/**
11 * Mettre a jour les stats d'un envoi
12 * @param $id_mailshot
13 */
14function mailshot_compter_envois($id_mailshot){
15
16        // todo, sent, fail, [read, [clic]],[spam]
17        $total = sql_countsel("spip_mailshots_destinataires","id_mailshot=".intval($id_mailshot));
18        // si aucun destinataires en base on ne fait rien, pour ne pas risquer de tuer les stats d'un envoi purge
19        if (!$total) {
20                return;
21        }
22
23        $statuts = sql_allfetsel("statut,count(email) as nb","spip_mailshots_destinataires","id_mailshot=".intval($id_mailshot),"statut");
24        $statuts = array_combine(array_map('reset',$statuts),array_map('end',$statuts));
25        #var_dump($statuts);
26        $set = array(
27                'total' => $total,
28                'failed'  => 0,
29                "nb_read" => 0,
30                "nb_clic" => 0,
31                "nb_spam" => 0,
32        );
33        if (isset($statuts['fail']))
34                $set['failed'] = $statuts['fail'];
35        if (isset($statuts['read']))
36                $set['nb_read'] = $statuts['read'];
37        if (isset($statuts['clic'])){
38                $set['nb_read'] += $statuts['clic']; // les clics sont aussi des lus
39                $set['nb_clic'] = $statuts['clic'];
40        }
41        if (isset($statuts['spam']))
42                $set['nb_spam'] = $statuts['spam'];
43
44        // current c'est tous les envoyes (y compris fails)
45        unset($statuts['todo']);
46        $set['current'] = array_sum($statuts);
47        #var_dump($set);
48        sql_updateq("spip_mailshots",$set,"id_mailshot=".intval($id_mailshot));
49}
50
51
52/**
53 * Archiver un envoi (vieux en cron ou a la demande)
54 * @param $id_mailshot
55 */
56function mailshot_archiver($id_mailshot){
57        // mettre a jour les stats avant de purger
58        mailshot_compter_envois($id_mailshot);
59        sql_delete("spip_mailshots_destinataires",'id_mailshot='.intval($id_mailshot));
60        sql_updateq("spip_mailshots",array('statut'=>'archive'),'id_mailshot='.intval($id_mailshot));
61        spip_log("Archiver mailshot $id_mailshot","mailshot");
62}
63
64/**
65 * Definir la combinaison (periode,nb envois) pour respecter la cadence maxi configuree
66 *
67 * @return array
68 *   (periode du cron, nb envois a chaque appel)
69 */
70function mailshot_cadence(){
71        include_spip('inc/config');
72        if (lire_config("mailshot/boost_send")=='oui')
73                return array(30,100); // autant que possible, toutes les 30s
74
75        // cadence maxi
76        $cadence = array(60,10);
77        $max_rate = lire_config("mailshot/rate_limit");
78        if ($max_rate = intval($max_rate)){
79                $rate_one_per_one = 24*60*60/$cadence[0];
80                if ($max_rate<$rate_one_per_one){
81                        // 1 mail toutes les N secondes pour ne pas en envoyer plus que le rate demande
82                        $cadence = array(intval(ceil($rate_one_per_one/$max_rate*$cadence[0])),1);
83                }
84                else if($max_rate>$rate_one_per_one*$cadence[1]){
85                        // rien on garde la cadence maxi
86                }
87                else {
88                        // envoyer N mails toutes les M secondes pour respecter la cadence max
89                        $nb = $max_rate/$rate_one_per_one;
90                        // on se cale sur le N superieur et on ajuste le delai entre chaque envoi
91                        $nb = intval(ceil($nb));
92                        $cadence = array(intval(ceil($nb*$rate_one_per_one/$max_rate*$cadence[0])),$nb);
93                }
94        }
95
96        spip_log("cadence:".implode(",",$cadence),"mailshot");
97        return $cadence;
98}
99
100/**
101 * Mettre a jour la meta qui indique qu'au moins un envoi est en cours
102 * evite un acces sql a chaque hit du cron
103 *
104 * @param bool $force
105 * @return bool
106 */
107function mailshot_update_meta_processing($force = false){
108        $current = ((isset($GLOBALS['meta']['mailshot_processing']) AND $GLOBALS['meta']['mailshot_processing'])?$GLOBALS['meta']['mailshot_processing']:false);
109
110        $new = false;
111        if ($force OR sql_countsel("spip_mailshots","statut=".sql_quote('processing')))
112                $new = 'oui';
113        if ($new===false and $next = sql_getfetsel('date_start','spip_mailshots',"statut=".sql_quote('init'),'','date_start','0,1')){
114                $new = strtotime($next);
115                if ($new>$_SERVER['REQUEST_TIME']){
116                        $new = 'oui';
117                }
118        }
119
120        if ($new OR $new!==$current){
121                if ($new) {
122                        ecrire_meta("mailshot_processing",$new);
123                        // reprogrammer le cron
124                        include_spip('inc/genie');
125            genie_queue_watch_dist();
126                }
127                else
128                        effacer_meta('mailshot_processing');
129        }
130
131        return $new;
132}
133
134/**
135 * Envoyer une serie de mails
136 * @param int $nb_max
137 * @param int $offset
138 *   pour ne pas commencer au debut de la liste (utile pour les processus paralleles)
139 * @return int
140 *   nombre de mails envoyes
141 */
142function mailshot_envoyer_lot($nb_max=5,$offset=0){
143        $nb_restant = $nb_max;
144        $now = $_SERVER['REQUEST_TIME'];
145        if (!$now) $now=time();
146        define('_MAILSHOT_MAX_TIME',$now+25); // 25s maxi
147        define('_MAILSHOT_MAX_TRY',5); // 5 essais maxis par destinataires
148
149        $offset = intval($offset);
150        // on traite au maximum 2 serie d'envois dans un appel
151        $shot = sql_allfetsel("*","spip_mailshots","statut=".sql_quote('processing'),'','id_mailshot','0,2');
152        foreach($shot as $shoot){
153                spip_log("mailshot_envoyer_lot #".$shoot['id_mailshot']." ".$shoot['current']."/".$shoot['total']." (max $nb_max)","mailshot");
154
155                // verifier que la liste des destinataires est OK
156                mailshot_initialiser_destinataires($shoot);
157                if (time()>_MAILSHOT_MAX_TIME) return $nb_restant;
158                $listes = explode(',',$shoot['listes']);
159
160                // chercher les N prochains destinataires
161                $dests = sql_allfetsel("*","spip_mailshots_destinataires","id_mailshot=".intval($shoot['id_mailshot'])." AND statut=".sql_quote('todo'),'','try',"$offset,$nb_max");
162                if (count($dests)){
163                        $options = array('tracking_id'=>"mailshot".intval($shoot['id_mailshot'])."-".date('Ym',strtotime($shoot['date_start'])));
164                        $subscriber = charger_fonction("subscriber","newsletter");
165                        $send = charger_fonction("send","newsletter");
166                        $corps = array(
167                                'sujet' => &$shoot['sujet'],
168                                'html' => &$shoot['html'],
169                                'texte' => &$shoot['texte'],
170                                'from' => $shoot['from_email'],
171                                'nom_envoyeur' => $shoot['from_name'],
172                        );
173                        foreach($dests as $d){
174                                if (time()>_MAILSHOT_MAX_TIME) return $nb_restant;
175                                $s = $subscriber($d['email'],array('listes'=>$listes));
176                                spip_log("mailshot_envoyer_lot #".$shoot['id_mailshot']."/".$d['email']." send","mailshot");
177                                $erreur = $send($s, $corps, $options);
178                                $try = $d['try']+1;
179                                if ($erreur){
180                                        if ($try>=_MAILSHOT_MAX_TRY
181                                                OR preg_match(",@example\.org$,i",$s['email'])
182                                          OR defined('_TEST_EMAIL_DEST')){
183                                                sql_updateq("spip_mailshots_destinataires",array('statut'=>'fail','try'=>$try,'date'=>date('Y-m-d H:i:s')),"id_mailshot=".intval($shoot['id_mailshot'])." AND email=".sql_quote($d['email']));
184                                                sql_update("spip_mailshots",array("current"=>"current+1","failed"=>"failed+1"),"id_mailshot=".intval($shoot['id_mailshot']));
185                                                spip_log("mailshot_envoyer_lot #".$shoot['id_mailshot']."/".$d['email']." : Erreur [$erreur] / failed apres $try essais","mailshot"._LOG_ERREUR);
186                                                // si c'est un fail max_try verifier et desinscrire eventuellement
187                                                if ($try>1){
188                                                        mailshot_verifier_email_fail($d['email']);
189                                                }
190                                        }
191                                        else {
192                                                sql_updateq("spip_mailshots_destinataires",array('try'=>$try,'date'=>date('Y-m-d H:i:s')),"id_mailshot=".intval($shoot['id_mailshot'])." AND email=".sql_quote($d['email']));
193                                                spip_log("mailshot_envoyer_lot #".$shoot['id_mailshot']."/".$d['email']." : Probleme [$erreur] (essai $try)","mailshot"._LOG_INFO_IMPORTANTE);
194                                        }
195                                }
196                                else {
197                                        $nb_restant--;
198                                        sql_updateq("spip_mailshots_destinataires",array('statut'=>'sent','try'=>$try,'date'=>date('Y-m-d H:i:s')),"id_mailshot=".intval($shoot['id_mailshot'])." AND email=".sql_quote($d['email']));
199                                        sql_update("spip_mailshots",array("current"=>"current+1"),"id_mailshot=".intval($shoot['id_mailshot']));
200                                        spip_log("mailshot_envoyer_lot #".$shoot['id_mailshot']."/".$d['email']." OK","mailshot");
201                                }
202                                $nb_max--;
203                        }
204                        // si $nb_max non nul verifier qu'il n'y a plus de dests sur cette envoi pour maj le statut juste en dessous
205                        if ($nb_max)
206                                $dests = sql_allfetsel("*","spip_mailshots_destinataires","id_mailshot=".intval($shoot['id_mailshot'])." AND statut=".sql_quote('todo'),'','try',"$offset,$nb_max");
207                }
208
209                if ($nb_max AND !count($dests) AND $offset==0){
210                        // plus de destinataires ? on a fini, on met a jour compteur et statut
211                        $set = array(
212                                'statut' => 'end',
213                                'date' => date('Y-m-d H:i:s'),
214                        );
215                        sql_updateq("spip_mailshots",$set,"id_mailshot=".intval($shoot['id_mailshot']));
216                        mailshot_compter_envois($shoot['id_mailshot']);
217                        mailshot_update_meta_processing();
218                }
219                if (!$nb_max OR time()>_MAILSHOT_MAX_TIME) return $nb_restant;
220        }
221
222        return 0; // plus rien a envoyer sur ce lot
223}
224
225
226/**
227 * Verifier un email en fail et si plus de N fails consecutifs le desabonner (email foireux)
228 * @param $email
229 */
230function mailshot_verifier_email_fail($email) {
231        if (_MAILSHOT_DESABONNER_FAILED != false) {
232                if (!defined('_MAILSHOT_MAX_FAIL')) {
233                        define('_MAILSHOT_MAX_FAIL', 3);
234                }
235
236                $historique = sql_allfetsel(
237                        'date, statut, try',
238                        'spip_mailshots_destinataires',
239                        'statut!=' . sql_quote('todo') . ' AND email=' . sql_quote($email),
240                        '',
241                        'date DESC',
242                        "0," . _MAILSHOT_MAX_FAIL
243                );
244
245                $nb_failed = 0;
246                foreach ($historique as $h) {
247                        if ($h['statut'] == 'fail' AND $h['try'] > 1) {
248                                $nb_failed++;
249                        }
250                }
251                if ($nb_failed >= _MAILSHOT_MAX_FAIL) {
252                        $unsubscribe = charger_fonction("unsubscribe", "newsletter");
253                        $unsubscribe($email, array('notify' => false));
254                }
255        }
256}
257
258/**
259 * Initialiser les destinataires d'un envoi
260 * = noter tous les emails a qui envoyer, au debut
261 * (fige la liste en debut d'envoi, evite les risques de sauter un destinataire si on se base seulement sur un compteur
262 * et sur les abonnes en cours car certains peuvent se desabonner pendant le processus d'envoi qui dure dans le temps)
263 *
264 * @param array $shoot
265 */
266function mailshot_initialiser_destinataires($shoot){
267
268        // verifier qu'on a bien initialise tous les destinataires
269        $nbd = sql_countsel("spip_mailshots_destinataires","id_mailshot=".intval($shoot['id_mailshot']));
270        if ($nbd<$shoot['total']){
271                spip_log("mailshot_initialiser_destinataires #".$shoot['id_mailshot']." : $nbd/".$shoot['total'],"mailshot");
272
273                // sinon reprendre l'insertion
274                $nb_lot = 2500;
275                $current = $nbd;
276                $listes = explode(',',$shoot['listes']);
277                $now = date('Y-m-d H:i:s');
278                $subscribers = charger_fonction("subscribers","newsletter");
279                do {
280                        if (time()>_MAILSHOT_MAX_TIME) return;
281                        $limit = "$current,$nb_lot";
282                        $dests = $subscribers($listes,array('limit'=>$limit));
283
284                        if (count($dests)){
285                                // preparer les insertions
286                                $ins = array();
287                                foreach ($dests as $d){
288                                        $ins[] = array(
289                                                'id_mailshot' => $shoot['id_mailshot'],
290                                                'email' => $d['email'],
291                                                'date' => $now,
292                                                'statut' => 'todo',
293                                        );
294                                }
295
296                                if (!sql_insertq_multi('spip_mailshots_destinataires',$ins)){
297                                        foreach ($ins as $i){
298                                                sql_insertq('spip_mailshots_destinataires',$i);
299                                                if (time()>_MAILSHOT_MAX_TIME) return;
300                                        }
301                                }
302                        }
303                        $current = $current + count($dests);
304                }
305                while (count($dests));
306
307                // ici on a fini toutes les init des destinataires
308                // on remet a jour le compteur de total au cas ou
309                $nbd = sql_countsel("spip_mailshots_destinataires","id_mailshot=".intval($shoot['id_mailshot']));
310                if ($nbd<$shoot['total'])
311                        sql_updateq("spip_mailshots",array('total'=>$nbd),"id_mailshot=".intval($shoot['id_mailshot']));
312                spip_log("mailshot_initialiser_destinataires #".$shoot['id_mailshot']." OK ($nbd)","mailshot");
313        }
314
315}
Note: See TracBrowser for help on using the repository browser.