Sommaire
Voila, hier et aujourd'hui, n'ayant rien de particulier a faire, j'ai décidé de mettre un oeuvre une idée qui me trottait par la tête depuis quelque temps; Un envoi de rappel dans le futur automatisé par email
Le problème:
J'ai la politique, dans ma boite mail, de tout archiver, et de ne laisser que les mails auxquels je dois répondre, ou les mails qui contiennent des informations pour effectuer des actions en dehors (par exemple des billets d'avion, un mail qui dit "je dois 150€ a tata danielle", etc…)
Quand ma boite est vide, je n'ai rien a faire de spécial!
Problème! Ma boite n'est jamais vide! En effet, les mails du style "je dois 150€ a tata" sont présent, et imaginons que je sais que je vais manger chez elle le 01 juin, comme je ne relis absolument pas régulièrement mes vieux mails, j'oublie totalement les 150€ euros et la tata pas contente
Ou alors gilbert me demande par mail le prix du kilo de savon a marseille, et je ne saurais ce prix que dans 1 semaine, car dans une semaine je vais a marseille pour en acheter, du coup son email traine dans la boite de reception toute la semaine le temps que j'aille a marseille.
Solution:
Et si j'envoyais ce mail a une adresse specifiée, lui disant de me le reenvoyer le 31 mai, la veille du repas chez la tata ! Comme ca je n'oublierais pas les 150€! Et voila je peux archiver le mail "150€ a tata" ma boite de reception est plus propre que propre!
J'en profite pour envoyer le mail que m'a envoyé gilbert a cette meme adresse, en specifiant un reenvoi dans 7 jours, et le tour est joué!
Donc j'ai cree un nouvelle adresse mail sur internet qui m'offre un acces pop et smtp (j'ai fait ça chez yahoo, mais ca devrait marcher n'importe ou, disons que cette adresse est rappel@rappel.fr ), et j'ai fait un petit script qui lit les mails de cette adresse, determine quand ce message doit etre reexpedié, le stocke, et reexpedie le mail a la date demandée!
Exemple d'utilisation:
J'envoie un mail a rappel@rappel.fr, avec comme sujet "20 mai; balayer le placard" et dans le corps ce que je vais, et le 20 mai, entre 6 et 7 h du mail, je recevrais un mail de rappel@rappel.fr me disant "balayer le placard" et ce qui etait dans le corps egalement
Le detail:
j'ai fait un script rappels.py, grace a une entree dans le crontab, ce script est executé plusieurs fois par jours (2 suffisent je pense).
Dans ce script est specifié les adresse mail autorisées a utiliser le service de rappel
Ce script agit de la maniere suivante:
-Il telecharge via pop les messages recus sur rappel@rappel.fr
-Parmi les emails, si l'email vient d'une personne autorisée, il verifie si dans le sujet il arrive a lire pour quelle date l'utilisateur veut son rappel
-Si il n'y arrive pas, il envoie un mail a la personne pour lui preciser que ce n'est pas possible
-Si il y arrive, il stocke le mail dans un fichier a part, le moment prevu d'envoi dans un autre fichier, et cree une tache avec at (le programme doit etre installé) pour le moment voulu de l'envoi
-Il supprime tous les email sur le serveur POP
-Finalement, il lit la liste des tous les envois a faire, et realise ceux qui sont prevu dans le passé par SMTP
les formats de moment de rappel acceptes
Le moment ou le rappel doit être fait s’écrit dans le sujet du mail, se termine par un ";", et doit être le la forme suivante; Le mail sera envoyé le jour spécifié entre 6 et 7h du matin
demain; -> demain
1j; -> dans 1 jour, meme heure
21j; -> dans 23 jours, meme heure
1m; -> dans 1 mois, meme heure
25 juin; -> le 25 juin prochain
4 septembre 2015; -> le 4 septembre 2013
Le code est hautement crado, les emails renvoyés ne sont pas identique, et parfois difficiles a lire, ayant eu un peu de mal avec le parsing des mail en python, d'ailleurs n'ayant que des debian squeeze sous la main, je n'ai teste le programme qu'avec python2.6, mais j'ai vu que a partir de python3.2 le parsing des email a ete amelioré.
Voici le programme:
#!/usr/bin/python2.6
adresseAutorisees=["monAdressePerso@perso.fr"]
SMTPserver = 'smtp.mail.yahoo.com'
SMTPsender = 'rappel@example.com'
SMTPUSERNAME = "rappel@example.com"
SMTPPASSWORD = "motdepasse"
POPSERVER='pop.mail.yahoo.com'
POPUSER='rappel'
POPPASS='motdepasse'
import os
from subprocess import Popen
def ex(command):
print(" on va executer: "+command)
p = Popen(command, shell=True)
sts = os.waitpid(p.pid, 0)[1]
class FichierDate:
def __init__(self,nomFichier,dateEnvoi,sujet):
self.nomFichier=nomFichier
self.dateEnvoi=dateEnvoi
self.sujet=sujet
def __str__(self):
return self.nomFichier+" SUJET: "+self.sujet
if not os.path.exists(".rappel"):
os.makedirs(".rappel")
import shelve
d=shelve.open(".rappel/liste")
def innit(db,key,valeur):
if key in db:
return db[key]
else:
db[key]=valeur
return valeur
f=innit(d,"fichierDate",[])
def enregistrerMail(mail,dateEnvoi):
nomFichier=str(dateEnvoi)+" pour "+str(mail.adresse)
fi=shelve.open(".rappel/"+nomFichier)
fi["mail"]=mail
fi.close()
fichierDate=d["fichierDate"]
fichierDate.append(FichierDate(nomFichier,dateEnvoi,mail.sujet))
d["fichierDate"]=fichierDate
mois=["janvier","fevrier","mars","avril","mai","juin","juillet","aout","septembre","octobre","novembre","decembre"]
moisVersNum={}
for i in range(len(mois)):
moisVersNum[mois[i]]=i+1
syntax="""
demain; -> demain
1j; -> dans 1 jour, meme heure
21j; -> dans 23 jours, meme heure
1m; -> dans 1 mois, meme heure
25 juin; -> le 25 juin prochain
4 septembre 2015; -> le 4 septembre 2013
"""
def chopeNumeroDevant(st):
i=0
while st[i:i+1].isdigit():
i+=1
if i==0:
return None,st
nb=int(st[0:i])
st=st[i:]
return nb,st
from datetime import timedelta,datetime,date
def parseMoment(chaine):
#print chaine
avant,sep,apres=chaine.partition(";")
aujourdhui=datetime.now()
avant=avant.strip()
if avant.startswith("demain"):
return aujourdhui+timedelta(1)
nbJour,reste=chopeNumeroDevant(avant)
if nbJour==0:
return None
avant=reste.strip()
numMois=None
for m in mois:
if avant.startswith(m):
numMois=moisVersNum[m]
avant=avant[len(m):]
avant=avant.strip()
if numMois:
#print avant
nb,reste=chopeNumeroDevant(avant)
if nb:
# print nb
return datetime(nb,numMois,nbJour,6,aujourdhui.minute,aujourdhui.second)
else:
annee=aujourdhui.year
if aujourdhui.month==numMois:
if aujourdhui.day<nbJour:
annee=aujourdhui.year
elif aujourdhui.day>nbJour:
annee=aujourdhui.year+1
else:
return None
elif aujourdhui.month>numMois:
annee=aujourdhui.year+1
return datetime(annee,numMois,nbJour,6,aujourdhui.minute,aujourdhui.second)
# c'est pas une date, c'est des jours relatifs
if avant.startswith("j"):
return aujourdhui+timedelta(nbJour)
if avant.startswith("m"):
return aujourdhui+timedelta(nbJour*30)
class MEMO:
pass
def Envoyeur(memo):
# typical values for text_subtype are plain, html, xml
text_subtype = 'plain'
content=b"""\
Ceci est un rappel comme demande
"""
subject=memo.sujet
import sys
import os
import re
from smtplib import SMTP_SSL # this invokes the secure SMTP protocol (port 465, uses SSL)
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email.mime.multipart import MIMEMultipart
from email.message import Message
msg=MIMEMultipart()
msg['Subject']= subject
msg['From'] = SMTPsender # some SMTP servers will do this automatically, not all
msg['To']=memo.adresse
msg.preamble=b"hoho"
texte=memo.body
kiki=MIMEText(texte,'plain')
msg.attach(kiki)
# att = MIMEBase('application','octet-stream')
# att.set_payload(memo.message)
# att.add_header('Content-Disposition', 'attachment', filename="mess")
# msg.attach(att)
try:
conn = SMTP_SSL(SMTPserver,465)
conn.set_debuglevel(False)
conn.login(SMTPUSERNAME, SMTPPASSWORD)
# print (sender,destination,msg.as_string())
conn.sendmail(SMTPsender, [memo.adresse], msg.as_string())
except:
return False
finally:
conn.close()
return True
import email
import poplib
def extractEmail(chaine):
deb=chaine.find(b"<")
fin=chaine.find(b">")
return chaine[deb+1:fin]
server= poplib.POP3_SSL(POPSERVER)
server.user(POPUSER)
server.pass_(POPPASS)
nbMess,size=server.stat()
print (nbMess)
memos=[]
for i in range(nbMess):
memo=MEMO()
reponse,lines,octets=server.retr(i+1)
print( reponse,"HIHI",octets)
adOk=False
suOk=False
for l in lines:
if not suOk and l.startswith(b"Subject:"):
memo.sujet=l[9:]
suOk=True
print(l[9:])
if not adOk and l.startswith(b"From:"):
memo.adresse=extractEmail(l[5:])
adOk=True
print(extractEmail(l[5:]))
print( len(lines))
# import email.parser.BytesParser
# msg=BytesParser().parsebytes('\n'.join(lines))
lll=""
for l in lines:
ll=l
lll=lll+'\n'+ll
msg = email.message_from_string(lll) # new statement
#print(msg.get_payload())
memo.body=msg.get_payload()
memo.body=memo.body[memo.body.find("Content-Transfer")+32:]
memo.message=lines
memo.numero=i+1
# memo.body=str(lines[len(lines)-1])
# print (memo.message)
memos.append(memo)
server.dele(i+1)
def estAutorise(adresse):
for a in adresseAutorisees:
if adresse in adresseAutorisees:
return True
return False
for m in memos:
moment=parseMoment(m.sujet)
if estAutorise(m.adresse):
if moment:
avant,sep,apres=m.sujet.partition(";")
m.sujet=apres
enregistrerMail(m,moment)
date=moment.strftime("%H:%M %m%d%y")
ex("at -f /home/auberge/rappel.sh "+date)
else:
m.sujet="Nous pas pu identifier le moment du rappel "+m.sujet
Envoyeur(m)
else:
print "ERREUR DESTINATAIRE NON AUTORISE"
#e=Envoyeur(m)
#print (m.sujet," POUR ",m.adresse)
def voyonsVoirSiOnPeutEnvoyerDesMails():
print "voyons voir"
fichierDate=d["fichierDate"]
fichierDateASupp=[]
for f in fichierDate:
print(f)
if f.dateEnvoi <= datetime.now():
print "on doit envoyer un mail "+str(f)
nomf=".rappel/"+f.nomFichier
print nomf
fmail=shelve.open(nomf)
mail=fmail["mail"]
if(Envoyeur(mail)):
os.remove(nomf)
fichierDateASupp.append(f)
for f in fichierDateASupp:
fichierDate.remove(f)
d["fichierDate"]=fichierDate
voyonsVoirSiOnPeutEnvoyerDesMails()
server.quit()
d.close()
# rappel de calendrier
Posté par polytan . Évalué à 9.
Salut,
En fait, tu as recréé la fonction "Rappel" d'un événement dans un calendrier?
[^] # Re: rappel de calendrier
Posté par unsigned . Évalué à 2.
Exactement, mais sans être dépendant d'un calendrier, d'une application, qui souvent ont d'ailleurs des interfaces qui nécessitent au moins 3 clicks et un temps non négligeable pour spécifier la date et le rappel
# at ? cron ?
Posté par philou . Évalué à -1.
La commande at ne te convient pas ? Et pour un mail cyclique comme un rappel d'anniversaire : il y a cron (ou mieux, la crontab de ton utilisateur pour ne pas polluer /etc/cron.d avec des petites commandes mail perso).
[^] # Re: at ? cron ?
Posté par krion . Évalué à 1.
Salut,
T'as lu le journal ?
Il utilise at ET cron.
[^] # Re: at ? cron ?
Posté par philou . Évalué à -2.
Salut, t'as lu mon message ?
Déjà il ne parle pas de la commande at. Et deuxièmement, il se sert de la crontab pour aller relever une boîte mail.
Là je parle d'utiliser at et/ou cron justement pour envoyer le mail, pas pour aller voir s'il y en a un à relever. Cela permet de se servir de cron (comme dans le journal donc) mais de rien de plus (enfin si, echo et mail…). Tu veux te rappeler de faire un truc demain, tu fais :
# agenda
Posté par Dreammm . Évalué à 9.
En fait, tu viens de découvrir l'utilité d'un agenda. Donc utilises-en un, que ce soit papier ou logiciel.
En logiciel, la plupart proposent également de gérer des listes de tâches.
2 cas :
Bonne chance … pour le futur.
[^] # Re: agenda
Posté par krion . Évalué à 6.
Et ensuite tu utilises ton script pour que ça t'envoi un rappel pour regarder ton agenda !
# Le futur c'est bien
Posté par 2PetitsVerres . Évalué à 9.
Tu comptes faire évoluer ça pour gérer le passé aussi ?
Tous les nombres premiers sont impairs, sauf un. Tous les nombres premiers sont impairs, sauf deux.
# Dans Kmail
Posté par lolop (site web personnel) . Évalué à 5.
[je ne l'utilises pas, mais ça existe déjà]
Bouton droit sur l'email dans la liste, menu "Créer une tache/un rappel".
Votez les 30 juin et 7 juillet, en connaissance de cause. http://www.pointal.net/VotesDeputesRN
[^] # Re: Dans Kmail
Posté par NeoX . Évalué à 4.
pareil avec thunderbird+lightning
1°) clic droit -> convertir en taches/evenements
2°) activer le rappel
3°) sauvegarder
si tes divers appareils sont synchronisés pour les calendriers,
tous les appareils te rappellerons la veille :
1°) que tu dois aller manger chez tata daniel demain
2°) que tu dois lui ramener 150euros
# Tu bosses pas chez ATOS au moins ?
Posté par Enzo Bricolo 🛠⚙🛠 . Évalué à 4.
Ce serait marrant
# dredi
Posté par jjl (site web personnel) . Évalué à 2.
Le prix du kilo à Marseille m’intéresse aussi ;)
# Tu as couplé ça avec iPot en plus ?
Posté par windu.2b . Évalué à 4.
# Utilité
Posté par Faya . Évalué à 1.
Clairement ce n'est pas très utile vu qu'il existe pléthore d'agendas web/pas web/mobiles/synchronisés/cloud/… permettant de faire la même chose mais pour moi qui apprend le python ton code peut être intéressant.
Si ploum< passait par là il te dirait d'utiliser gtg mais que c'est pas complètement au point parce pas de version mobile, sinon owncloud te propose un agenda avec caldav et rappels et en GTD proprio il y a doit.im qui n'est pas mal foutu (je rêve d'ailleurs d'une alternative libre).
# Enlarge your income
Posté par Kerro . Évalué à 6.
Je vais facturer mes clients après leur avoir envoyé un email automatique environ chaque heure de la nuit pour montrer que je dépannais le serveur de sauvegarde.
Mouahahaha, à moi l'argent, les femmes et l'huile solaire.
# Réaction typique ... ça existe déjà
Posté par Bianca (site web personnel) . Évalué à 1.
J'ai envie de dire (ou plutôt d'écrire) : "Gna gna gna"
Comme trop souvent ici, les commentaires sont peu constructif et trop agressif à mon goût.
Oui, bien sur, ça existe déjà. On peut faire cela différemment avec Google Agenda, Owncloud, FaceBook, … (rayer la mention inutile).
Je trouve le concept sympathique et même si j'aurai implémenter cela différemment (je pense en laissant les mails sur le serveur et en les consultant périodiquement afin de pouvoir les transférer à l'adresse de l'expéditeur le jour J) ta façon de faire est intéressante.
Pourquoi réinventer la roue ??
Le hollandais volant a expliqué son point de vue ici et je le partage.
Après, si Torvalds avait pensé "ça existe déjà" au moment ou il a écrit un clone de Minix, nous ne serions pas ici à discuter le bout de gras.
Suivre le flux des commentaires
Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.