Code source de mail

#!/usr/bin/python
# -*- coding: utf-8 -*-

# Codé par 20-100

"""
Pour que la Note Kfet 2015 puisse envoyer des mails,
sans pour autant de faire cracher à la gueule par le smtp du Cr@ns
qui n'autorise que 10 mails/minute.

Si ce script est appelé avec l'option --flush, il envoie les 10 mails suivants
qui sont dans la queue. (C'est ce que fait cron toutes les minutes)
On utilise pour cela la base pgsql nkmails

Ce module permet également de générer automatiquement des mails du genre
inscription/négatif
"""

import os
import sys
import re
import datetime
import psycopg2
import psycopg2.extras

# Avant toute chose, on n'a pas envie d'être au mauvais endroit
# On est obligé de le faire tout de suite, avant d'importer ../config
import os
thisfile = os.path.realpath(__file__)
thisdirectory = thisfile.rsplit("/", 1)[0]
os.chdir(thisdirectory)

# Fichier de conf
sys.path.append("../config/")
import config
import smtplib
from email.mime.text import MIMEText

DEBUG = False

from email import Charset
Charset.add_charset('utf-8', Charset.QP, Charset.QP, 'utf-8')

precedence_bulk_regex = re.compile(config.precedence_bulk_regex)

[docs]def send_mail(emetteur, destinataires, objet, message, cc=[], replyto=[]): """Envoie un mail en utilisant la commande sendmail""" if config.debug: destinataires = config.debug_sendmail_to cc = [] mail = MIMEText(message, "plain", "utf-8") mail["From"] = emetteur mail["To"] = u", ".join(destinataires) mail["Subject"] = objet mail["X-Mailer"] = u"NK2015" if cc != []: mail["Cc"] = u", ".join(cc) # Les cc sont non seulement dans les headers mais reçoivent aussi le mail destinataires.extend(cc) if replyto != []: mail["Reply-To"] = u", ".join(replyto) if not set(destinataires).isdisjoint(config.to_noack): # Au moins un des destinataires est connu pour envoyer des mails d'auto-réponse (certaines ML) # On ajoute donc un header demandant de ne pas en envoyer. mail["X-Ack"] = u"No" if any([precedence_bulk_regex.match(d) for d in destinataires]): # Un des destinataires (en général il n'y en aura qu'un) est considéré comme # envoyant des notifications de modération. # On ajoute donc un header demandant de ne pas en envoyer. mail["Precedence"] = u"bulk" if DEBUG: print "Sending mail :" print mail s = smtplib.SMTP('localhost') try: s.sendmail(emetteur, destinataires, mail.as_string()) except: raise finally: s.quit()
[docs]def getcursor(db=config.database_mails): """Renvoie une connexion à la base mails (ou une autre) et un curseur""" con = psycopg2.connect(database=db, user=config.pgsql_user) con.set_client_encoding("utf-8") cur = con.cursor(cursor_factory=psycopg2.extras.DictCursor) return con, cur
[docs]def queue_mail(emetteur, destinataires, objet, message, cc=[], replyto=[]): """Enregistre un mail dans la table queue de la base mails""" # On rajoute un timestamp pour que le destinataire sache quand le mail a été écrit message += u"\n\nMail généré par la Note Kfet %s" % ( datetime.datetime.now().strftime("le %d/%m/%Y à %T").decode("utf-8")) con, cur = getcursor() cur.execute("""INSERT INTO queue (emetteur, destinataires, subject, body, cc, replyto) VALUES (%s, %s, %s, %s, %s, %s);""", (emetteur, ", ".join(destinataires), objet, message, ", ".join(cc), ", ".join(replyto))) cur.execute("COMMIT;")
[docs]def postmail(): """Envoie le premier mail dans la queue""" con, cur = getcursor() cur.execute("""SELECT * FROM queue ORDER BY queue_date LIMIT 1;""") message = cur.fetchone() if message != None: # On l'envoie try: send_mail(message["emetteur"].decode("utf-8"), message["destinataires"].decode("utf-8").split(", "), message["subject"].decode("utf-8"), message["body"].decode("utf-8"), message["cc"].decode("utf-8").split(", "), message["replyto"].decode("utf-8").split(", ")) except: print "Erreur SMTP avec le mail à destination de ", message["destinataires"].decode("utf-8"), "(", sys.exc_info()[0], ")" # On le stocke dans les messages envoyés cur.execute("""INSERT INTO sent (queue_date, emetteur, destinataires, subject, body, cc, replyto) VALUES (%(queue_date)s, %(emetteur)s, %(destinataires)s, %(subject)s, %(body)s, %(cc)s, %(replyto)s);""", message) # On l'enlève de la queue cur.execute("""DELETE FROM queue WHERE id = %s;""", (message["id"],)) cur.execute("COMMIT;")
[docs]def postqueue(n=10): """Envoie les n premiers mails qui sont dans la queue""" # On regarde d'abord si il y en a, parce qu'on n'aime pas bosser pour rien con, cur = getcursor() cur.execute("SELECT count(*) FROM queue;") queuen = cur.fetchone() n = min([n, queuen]) for i in range(n): postmail()
[docs]def mail_inscription(compte, real_password): """Place dans la queue un mail envoyé à l'incription.""" emetteur = config.mails_from destinataires = [compte["mail"]] objet = u"[Note Kfet] Inscription sur la Note Kfet 2015" body = u"""Bonjour %s %s, Ce mail t'a été envoyé parce que t'es inscrit sur la Note Kfet du BDE de l'ENS Cachan. Ton numéro de compte est %s. Ton pseudo est par défaut %s mais tu peux le modifier. Ton mot de passe est %s. (Ne le communique pas, et sache que l'équipe des développeurs n'en a pas besoin même en cas de problème avec ton compte) Ton inscription vient d'être finalisée. Tu peux maintenant te connecter sur %s. Pour te loguer, utilise ton pseudo (ou #<ton numéro de compte>) et le mot de passe ci-dessus. Tu peux accéder à ton compte pour consulter ton solde, ton historique de consommations, proposer des activités… N'oublie pas de recharger ton compte pour pouvoir consommer à la Kfet ! -- Le BDE""" % (compte["prenom"], compte["nom"], compte["idbde"], compte["pseudo"], real_password, config.url_note) queue_mail(emetteur, destinataires, objet, body)
[docs]def mail_negatif(emetteur, compte, custom_message=u"", custom_subject=None): """Place dans la queue un mail pour prévenir un compte qu'il est en négatif.""" con, cur = getcursor(config.database) cur.execute("SELECT solde_tres_negatif FROM configurations WHERE used;") solde_tres_negatif = cur.fetchone()["solde_tres_negatif"] / 100 destinataires = [compte["mail"]] replyto = [config.ml_tresoriers] if custom_subject: objet = custom_subject else: objet = u"""[Note Kfet] Négatif sur la Note Kfet (compte n° %s)""" % (compte["idbde"]) args = (compte["prenom"], compte["nom"], custom_message, compte["pseudo"], str(int(compte["solde"]) / 100.), solde_tres_negatif, config.url_note, compte["idbde"], config.ml_tresoriers) body = u"""Bonjour %s %s, %s Ce mail t'a été envoyé parce que le solde de ta Note Kfet %s est négatif ! Ton solde actuel est de %s €. Le BDE rappelle qu'il ne sert plus les adhérents en dessous de %s €. Par ailleurs, le BDE ne sert pas d'alcool aux adhérents dont le solde est inférieur à 0€ depuis plus de 24h. Si tu ne comprends pas ton solde, tu peux consulter ton historique sur %scomptes/%s/historique/1/ Tu peux venir recharger ta note rapidement à la Kfet, ou envoyer un mail à la trésorerie du BdE (%s) pour payer par virement bancaire. -- Le BDE""" % args queue_mail(emetteur, destinataires, objet, body, replyto=replyto)
[docs]def mail_generate_password_duplicate(prenom, nom, mail, comptes): """Prévient les respo info qu'un compte existe en double.""" emetteur = config.mails_from destinataires = config.mails_generate_password_duplicate objet = u"[Note Kfet] Compte dupliqué ?" body = u"""Tentative de régénération du mot de passe de (prenom, nom, mail) = (%s, %s, %s) Mais %s comptes correspondants trouvés ! -- La Note Kfet 2015""" % (prenom, nom, mail, len(comptes)) queue_mail(emetteur, destinataires, objet, body)
[docs]def mail_regenerate_password(prenom, nom, mail, token, timestamp, token_delay): """Envoie le mail au compte pour qu'il puisse réinitialiser son mot de passe.""" emetteur = config.mails_from destinataires = [mail] objet = u"[Note Kfet] Réinitialisation de ton mot de passe" body = u"""Bonjour %s %s, Pour rentrer un nouveau mot de passe pour ta Note Kfet, suis ce lien : %s%s Attention : ce lien n'est valable que pour %s heures (il a été généré %s). Si tu n'es pas à l'origine de cette demande, contacte %s au plus vite. -- La Note Kfet""" % (prenom, nom, config.url_regen_password, token, token_delay/3600, timestamp.strftime("le %d/%m/%Y à %T").decode("utf-8"), config.ml_respo_info) queue_mail(emetteur, destinataires, objet, body)
[docs]def mail_passage_negatif(compte, seuil_mail, solde_apres): """Envoie un mail au compte parce qu'il vient de passer en négatif.""" emetteur = config.mails_from destinataires = [compte["mail"]] replyto = [config.ml_tresoriers] objet = u"""[Note Kfet] Passage en négatif (compte n°%s)""" % (compte["idbde"]) body = u"""Bonjour %s %s, Ce mail t'a été envoyé par la Note Kfet pour t'avertir que ton compte (%s) vient de passer en dessous de %.2f €, ton solde étant maintenant de %.2f €. Par ailleurs, le BDE ne sert plus d'alcool aux adhérents dont le solde est inférieur à 0€ depuis plus de 24h. Il faudrait que tu passes à la Kfet pour remettre ton compte en positif. Si tu ne comprends pas ton solde, tu peux consulter ton historique sur %scomptes/%s/historique/1/ Si les consommations ne sont pas de toi, contacte au plus vite la trésorerie du BDE : %s. -- Le BDE""" % (compte["prenom"], compte["nom"], compte["pseudo"], seuil_mail/100., solde_apres/100., config.url_note, compte["idbde"], config.ml_tresoriers) queue_mail(emetteur, destinataires, objet, body, replyto=replyto)
[docs]def mail_new_activity(new_activity, non_valid_activities): """Envoie un mail à la mailing-list des respos-pots pour les prévenir d'une nouvelle demande d'activité.""" emetteur = config.mails_from destinataires = [config.ml_respo_pot] objet = u"""[Note Kfet] Nouvelle demande d'activité""" body = u"""Bonjour, Vous avez une nouvelle demande d'activité. Résumé de l'activité : %s %s -- La Note Kfet""" % (new_activity, non_valid_activities,) queue_mail(emetteur, destinataires, objet, body)
[docs]def mail_confirm_email(prenom, nom, adresse, idbde, mail_token): """Envoie un mail au compte pour vérifier son adresse email.""" emetteur = config.mails_from destinataires = [adresse] objet = u"""[Note Kfet] Confirmation d'adresse mail""" body = u"""Bonjour %s %s, Ton compte note kfet a été créé ou son adresse e-mail a été modifiée pour %s. Afin de vérifier que tu es bien propriétaire de cette adresse e-mail, merci de cliquer sur le lien suivant : %s%s/%s -- La Note Kfet""" % ( prenom, nom, adresse, config.url_confirm_email, idbde, mail_token, ) queue_mail(emetteur, destinataires, objet, body)
if __name__ == "__main__": if "-v" in sys.argv or "--debug" in sys.argv or "--verbose" in sys.argv: DEBUG = True if "--flush" in sys.argv: postqueue(n=5)