Blog de développement

Listener: Tri et Distribution des Mails

Dernière Modification le :
2024-02-19

Le Listener a synchronisé les boites mails. Lire →. Il s'agit maintenant de distribuer les mails aux différentes fonctions de l'entreprise. A savoir:

  • logistique: expédition et confirmation de livraison
  • logistique: délai de livraisons
  • bon de commande: module d'enregistrement des commandes
  • bon pour accord (commande): module d'enregistrement des commandes
  • demande de devis: la boutique électronique

Valeurs auditées

Pour chaque mail, on collecte les informations qu'on passe au crible en vue de l'affectation à une fonction. Les infos auditées sont:

  • le mail de l'expéditeur
  • le destinataire
  • le sujet
  • le corps du message, en fait les 8 premieres vraies lignes.
  • la présence ou non de pièces jointes
  • les noms des pièces jointes

S'il est complété du module FiltaQuilla, Thunderbird. En mode Desktop, il peut synchroniser et faire le premier tri, la distribution sommaire, l'extraction (sauvegarde des pièces jointes). Au delà, il faut se mouiller un peu...

A proprement parler, il faudrait aussi prendre en considération le CONTENU des pièces jointes, en général au format PDF (pas le plus facile à lire!). Ceci nécessite de détacher les pièces jointes, de les 'Océriser' (ou extraire/convertir), de les interpréter.

L'audit regarde les critères de différentes façons: contient, ne contient pas, est, n'est pas, commence par, se termine par.

Voici un exemple de mail de commande retord:

Le sujet: Re: Offre XXXX
Le message: Ci joint notre bon de commande...

Il invalide l'audit sur le titre (c'est une réponse à un mail, sans mot magique), Il valide l'audit du message (il y a le mot commande) mais pas la combinaison ET des deux...

L'audit du corps du mail (bodytext) doit se limiter aux premières lignes, sinon, on risque de trouver un mot magique (ex: commande) dans tout ce que la conversation a pu ramasser au cours de la discussion sur une offre, par exemple

Là, Thunderbird est à sa limite et un script est plus flexible. Thunderbird, oui, certes, mais après il faut reprendre la main!

Le destinataire et l'expéditeur

Pour un mail de commande, de logistique, un règle simple est que le destinataire principal, c'est vous. Si vous y êtes en CC ou BCC, ce n'est pas un mail à faire gérer par l'automate.

A contrario, vos interlocuteurs internes ou apparentés (fournisseurs, banque, etc) ne vous envoient pas de commandes: on crée une liste d'exclusion. On autorise tous les expéditeurs, sauf ceux-là.

Audit du titre (sujet du mail)

A quelques rares exceptions près, qu'il convient de traiter, les mails dont le titre commence par : "Lu:", "Re:" , "Read" , "AW:", "Le" (pour Leido en espagnol), "WG", "Fwd", "FW", "TR:"... ne sont pas des mails de commande ou de logistique.

Les délais: Ils sont fournis par les fournisseurs, parfois étrangers, avec une grande diversité de comportements. Par exemple, un de mes correspondant a bien des difficultés à écrire le mot "acknowledgement", donc pour le mot clé, je retiens "ackn"

Les commandes: Des mots clés marquent que le mail n'est pas un mail de commande. Par exemple: bien recu, verification, Livraison , Expedition, paiement, automatique, retard, relance, facture...

La présence des mots clés tels que: "commande", "order", "Cde ", "Cde:", "PO:", "PO ", etc. sont le marqueurs privilégié du mail de commande

ArKeyWordExclusTitreCommande (0) = "bien recu" : ArKeyWordExclusTitreCommande (1) = "verification" : ArKeyWordExclusTitreCommande (2) = "Livraison" : ArKeyWordExclusTitreCommande (3) = "Expedition"
ArKeyWordExclusTitreCommande (4) = "paiement" : ArKeyWordExclusTitreCommande (5) = "automatique" : ArKeyWordExclusTitreCommande (5) = "retard" : ArKeyWordExclusTitreCommande (6) = "relance" : ArKeyWordExclusTitreCommande (7) = "facture"

Audit du Corps du Message

2 transformations sont utiles. CDO.Message de Microsoft a un outil bien utile: la génération automatique d'une version texte du corps de mail: AutoGenerateTextBody. Ensuite on extrait du corps du texte, le début (Body_Short), en se limitant aux X premières lignes significatives. Pour l'instant X est réglé à 8.

myMail.AutoGenerateTextBody = True
sBody = myMail.TextBody
If sBody <> "" then 
	ArBody = Split (sBody, VbCrLf)
	k=0
	K_Max = 8
	For j = 0 to Ubound(ArBody) - 1
		if trim(ArBody(j)) <> "" AND k < K_Max then
			sBody_Short = sBody_Short & VbCrLf & trim(ArBody(j))
			k= k+ 1
		End If
	Next						
End If

La liste des pièces jointes

C'est à ce moment là qu'on va établir et conserver une liste des pièces jointes (attachments). J'utilise volontiers l'outil GetStream() de CDO.Message. Il n'est pas exempt d'erreurs et bizareries et sur un mail contenant beaucoup d'images insérées, il m'a généré une liste farfelue...

Je fais donc une deuxième lecture, plus directe. En UTF-8, j'utilise ADODB.Stream pour conserver l'encodage. Toutefois, il semble exister une limite à la taille de fichier (20MB). Lire → Un jour, une cliente m'a envoyé un mail de 50 Mb! C'est très au delà de la limite usuelle de 10 Mb. Bref, ca plante la lecture et dans un premier temps, j'ai mis un filtre qui élimine tous les mails de plus de 10 Mb.

Décision finale suite à l'audit

On combine (ET) chaque résultat d'audit, pour chaque fonction d'entreprise. Les mails ainsi retenus sont envoyés à leur première destination identifiée.

Chaque point audité est duement enregistré, y compris la décision finale. Assez typiquement il y a un élément d'audit pour chaque élément du mail et chaque fonction déstinataire. Ici, un exemple où un ZIP a été extrait et le contenu ajouté à la liste de pièces jointes :

APAXXX Commande standard XXX- 0.eml

** Audit de la fonction: Delais
Audit de: le mail de l'expediteur	: Faux
Audit de: le destinataire	: Vrai
Audit de: le titre	: Faux
Audit de: la présence de pieces jointes	: Vrai
Audit de: la liste des pieces jointes	: Faux
--> Resultat Audit Global de la fonction: Delais	: Faux

** Audit de la fonction: Bons de Commande
Audit de: le mail de l'expediteur	: Vrai
Audit de: le destinataire	: Vrai
Audit de: le titre	: Vrai
Audit de: le corps du message en mode texte	: Vrai
Audit de: la présence de pieces jointes	: Vrai
--> Resultat Audit Global de la fonction: Bons de Commande	: Vrai
AEF_UO - Commande standard XXX, 0
Pieces Jointes : PO_341_XXX_0_F.pdf , Attachements.zip, offre_APAXXX.pdf

Extraction des pièces jointes

L'aimable préposé(e) à la distribution du courrier va prendre un coupe-papier et sortir le courrier de l'enveloppe, c'est à dire distribuer le courrier, extraire les pièces jointes, les désagraffer si besoin (dézippage) et même les envoyer au Service Informatique pour OCR, en même temps qu'à chacune des fonctions (services) ad'hoc.

L'outil d'extraction de pièces jointes, disponible en ligne de commande et sur toutes les plateformes,le plus cité est munpack (pour mail un-pack, je suppose) Lire le Manuel →. Télécharger →

-f : Force l'écrasement des fichiers existants.
-q = En silence...
-C Change le répertoire courant en répertoire avant de lire les fichiers. Dans munpack, on ne met pas de "" pour encadrer le nom du dossier !

Command = """" & sDossierScripts & "munpack20.exe"" -f -q -C " & sDossierTemp  & " """ & sFilePath & """ "
oShell.Run Command, 0, True 

Au demeurant, j'ai rencontré un problème insoluble à ce jour! Sur la version en ligne de commande, sous Windows, lors de l'extraction d'un mail contenant un é (é accent aigu) : LGPLed libiconv tourne en boucle et bloque le bon déroulement du script.

Il existe une alternative: Uud64win.exe. Ce petit logiciel gratuit et tenu à jour est adressable en ligne de commande. Peu connu, il fonctionne très bien ! Télécharger →

Son mode de fonctionnement est simple, très efficace, mais peu documenté, la syntaxe peu habituelle. Je le mets ici, à toutes fins utiles (voir en P.4). Lire le Manuel →

Les opérations DDE prises en charge sont les suivantes:
Open - [open("filename")] Charge et traite le fichier spécifié.
Extraire - [extract("chemin")] Extrait tous les fichiers complets dans le répertoire spécifié.
Clear - [clear] Supprime tous les fichiers et réinitialise UUDWin.
Purge - [purge] Supprime tous les fichiers de sortie complets et partiellement complets.
Quit - [quit] Met fin à UUDWin. Les commandes DDE doivent être envoyées lorsqu'elles sont liées à la rubrique "System".

Command = """" & sDossierScripts & "Uud64win.exe"" """ & sFilePath & """  /OutDir=""" & sDossierDestination & """ /Extract"

Renommage après extraction

Il faudrait voir si des outils modernes d'extraction de mail sous python sont plus satisfaisants... UUDView fonctionne bien, certes, mais il renomme les fichiers pour les écrire de façon "safe":
- Il remplace les blancs par Underscore ( _ )
- Il substitue des caractères UTF-8 par le Losange noir EF BF BD (�) Lire →
Heureusement, il conserve trace de la deuxième transformation dans un fichier ListDump, qu'on exploite par expression régulière pour remonter à la version avec Underscore. Alternativement on peut exploiter le fichier Report, qu'on obtient tout aussi facilement, en veillant à bien placer l'instruction au sein de la ligne de commande (voir manuel).

Command = """" & sDossierScripts & "Uud64win.exe"" """ & sFilePath & """ /OutDir=""" & sDossierTemp & """ "
Command = Command & "/Listdump=""" & sFilePath_Listdump & """ /LogFile=""" & sFilePath_LogFile & """ /Report=""" & sFilePath_Report &  """ /Extract"
Donc, on lit le fichier avec une expression régulière:
objRegExpr.Pattern = "(DestFile:)(.+)(.pdf|\.doc|\.docx|\.zip)(\s--->\s)(.+)(.pdf|\.doc|\.docx|\.zip)" 

On corrige éventuellement la substitution du losange noir:

Set ColMatches = objRegExpr.Execute(sListdump)
If ColMatches.Count > 0 then 	
	for i_match = 0 to ColMatches.Count -1
		sFilePath_Expected = sDossierTemp & ColMatches.Item(i_match).SubMatches(1) & ColMatches.Item(i_match).SubMatches(2)
		sFilePath_Found = sDossierTemp & ColMatches.Item(i_match).SubMatches(4) & ColMatches.Item(i_match).SubMatches(5)
		If objFSO.FileExists(sFilePath_Found) AND NOT objFSO.FileExists(sFilePath_Expected) then
			objFSO.CopyFile sFilePath_Found,sFilePath_Expected
			objFSO.DeleteFile sFilePath_Found
		End If
	Next
End If

On reprend notre liste de pièces jointes (attachments), séparées par virgule et nous regardons si le fichier renommé avec des underscore existe, dans quel cas on fait le renommage inverse.

ArAttachments = Split (sAttachments & ",",",") ' // sAttachments est notre liste de pièces jointes  séparée par virgule.
For i = 0 to UBound(ArAttachments) -1
	sFileName = ArAttachments (i)	
	sFileName_avec_Underscore = replace (sFileName," ", "_")
	if InStr(1,sListdump, "DestFile:" & sFileName,1) = 0 then 
		if InStr(1,sListdump, "DestFile:" & sFileName_avec_Underscore,1) > 0 then 
			sFilePath_Expected = sDossierTemp & sFileName
			sFilePath_Found = sDossierTemp & sFileName_avec_Underscore
			If objFSO.FileExists(sFilePath_Found) AND NOT objFSO.FileExists(sFilePath_Expected) then
				objFSO.CopyFile sFilePath_Found,sFilePath_Expected
				objFSO.DeleteFile sFilePath_Found
			End If		
		End If
	End If
Next

Dézippage

Comme des pièces jointes peuvent avoir été rassemblées en un ZIP, la personne préposée à la distribution du courrier va aimablement dézipper, c'est à dire retirer l'agraffe.

Les fichiers ainsi dézippés vont être mis avec les autres pièces jointes et ils seront rajoutés à la liste des pièces jointes, à Océriser éventuellement.

J'utilise Seven Zip Lire le Manuel → ; ici une présentation très complète Lire →. Télécharger →

Les commandes utilisées sont: e (Extract) et -aoa écraser tous les fichiers existants sans invite.

Command =  """" & SevenZipPath & """ e  """ & sDossierTemp & ExtractedFile.name &  """ -aoa -o""" & sDossierTemp & """ "
oShell.Run Command, 0, True	

Enregistrements

A toutes fins utiles, en particulier le nécessaire déboguage des filtres, on conserve 2 fichiers spécifiques à chaque mail analysé: les détails de l'audit et la liste des pièces jointes, après avoir décortiqué les ZIP éventuels. Certes, pour une utilisation ultérieure on a toujours accès à la liste de pieces jointes au travers de la collection myMail.Attachments de CDO.Message

Et ensuite ? On avance dans le WorkFlow post-tri

Le WorkFlow est le suivant:
  1. La Synchronisation avec le serveur de Mail Lire →
  2. Tri et distribution du courrier (ce billet ici même)
  3. L'Extraction et transformation des pièces jointes (ce billet ici même)
  4. La lecture OCR Lire →
  5. L'extraction de données
  6. La définition des tâches
  7. La Validation manuelle éventuelle
  8. L'exécution